Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

playNote() and playBeat() issues #758

Closed
1 task done
stereokai opened this issue Feb 18, 2022 · 9 comments · Fixed by #787
Closed
1 task done

playNote() and playBeat() issues #758

stereokai opened this issue Feb 18, 2022 · 9 comments · Fixed by #787
Assignees
Labels
area-player Related to the audio playback engine. platform-all Affects all platforms state-accepted This is a valid topic to work on.

Comments

@stereokai
Copy link

stereokai commented Feb 18, 2022

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior


1. Short notes are not being synthesized to their full length using playNote()

playNote on any note on the first beat works fine. However:

When calling playnote() for any any quarter note or shorter, on any beat after the first beat, Alphatab tries to play the note (playback is logged), but either no sound is heard, or the note is truncated. The result depends on the value (duration) of the first beat. When the notes are truncated, this is logged:

AlphaTab][AlphaSynth] Position changed: (time: 5.333333333333333/2000.0000000000002, tick: 11/3840, Active Voices: 1

It's important to note, this doesn't happen at all with playBeat.


2. Calling playNote() or playBeat() before the playback has ended breaks the synthesizer

When calling playNote() or playBeat() again (one time is enough) before the playback has ended, it causes the synthesizer to play some combined note or harmonic indefinitely. Alphatab logs thousands of this kind of message:

[AlphaTab][AlphaSynth] Position changed: (time: 623.9999999999998/2000, tick: 1199/3840, Active Voices: 0

Expected Behavior

  1. Short notes will play using playNote() to their full duration irregardless of their position in the tab.
  2. Alphatab should throttle calls to playNote() or playBeat() based on the respective beat duration.

Link to jsFiddle, CodePen, Project

No response

Found in Version

1.3-alpha

Platform

Web

Environment

- **OS**: OSX Big Sur 11.6.2
- **Browser**: Chrome 98

Anything else?

No response

@Danielku15
Copy link
Member

Can you provide a concrete minimal reproducible example?

@stereokai
Copy link
Author

Yes sir, there you go: https://codepen.io/stereokai/pen/WNXzmMo

I added the line api.playNote(note) to the click event. Both issues are evident. You can see the player goes nuts if you press on any note multiple times quickly, which I didn't witness in my own codebase because I didn't use a player with a long song.

@stereokai
Copy link
Author

stereokai commented Feb 25, 2022

@Danielku15 I have an interesting update.

First of all, concerning problem no. 2, a simple throttle code will solve the problem.

  function throttle(callback, limit) {
    let waiting = false;
    return function () {
      if (!waiting) {
        callback.apply(this, arguments);
        waiting = true;
        setTimeout(function () {
          waiting = false;
        }, limit);
      }
    };
  }

Concerning problem no. 1, I was able to somewhat solve it with the following 2 lines:

function onNoteClick(note) {
   note.beat.playbackStart = -Math.abs(note.beat.playbackStart)
   setTimeout(() => api.playNote(note), 100) 
}
  • The first line which guarantees a negative playbackStart is because of this line which truncates the single note playback too short. However, it causes the playing of the note to be delayed by the duration of all the notes preceding it in the bar that it belongs to.
  • The second line has to do with some sync issues between the player and the synth workers. The peculiar thing, is that this fix works for all notes 16th and longer. Shorter notes than that still don't get played. However, increasing the timeout, for example, setTimeout(() => api.playNote(note), 200) or setTimeout(() => api.playNote(note), 1000) allows exceedingly shorter notes to play.

See the updated fix here: https://codepen.io/stereokai/pen/WNXzmMo

@stereokai
Copy link
Author

@Danielku15 can you please explain the purpose or function of brushOffset when AlphaTab is playing midi? I think the bug is around the value of this parameter when playing a single note/beat. Thanks!

@Danielku15
Copy link
Member

When playing a brush stroke on the guitar, each note played on a beat, is slightly delayed. The brushOffset is the value how much later a single note within a beat is played.

@stereokai
Copy link
Author

I see. Then I'm a bit lost. Have you had a chance to look at the information I investigated?

@Danielku15 Danielku15 added area-player Related to the audio playback engine. platform-all Affects all platforms state-accepted This is a valid topic to work on. labels Mar 2, 2022
@Danielku15
Copy link
Member

Not at this point. I am lately quite busy and don't find much time working very focused and actively on the project. I can confirm that there are some strange behaviors regarding this feature in the example which I wouldn't expect to happen. But it behaves generally a bit strange. I will need to invest some more time to investigate on what's happening there and how a sustainable fix for the weird behavior look like.

The only (rather random) suggestion would be to give it also a try with the 1.2 stable version. There is a small chance that some things got broken during the refactorings for the Android support. It is unlikely but still. 😉

Also the whole cursor behavior is kind-a weird.

My assumption currently is that there is an issue with the fact that you're initiating a playNote/playBeat as part of a user interaction. The click on the elements will by default trigger a seek of the player to the position of the note on the main audio playback. This could interfere somehow with the internal state when initiating at the same time a new play of a single note.

The playNote/playBeat support is currently quite minimalistic. The approach internally works that a separate midi file is generated containing only the individual elements and send it for a special playback. Internally the synth switches quickly modes from the main playback to a "one time playback" to reuse the synth components.

I will have to think on how to stabilize this feature in general to be more flexible in regards of firing off the playback of a separate note/beat. The current solution is a fragile setup with rather a complex state handling.

@stereokai
Copy link
Author

Hi, I'm sorry for not having replied earlier!

Very awesome of you taking the time so quickly to fix it. Thank you very much for it!

Any idea when it'll reach a release? No pressure, just asking :)

@stereokai
Copy link
Author

@Danielku15 hi, and thanks again for fixing this :)
Has this fix been released yet on any version?
Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-player Related to the audio playback engine. platform-all Affects all platforms state-accepted This is a valid topic to work on.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants