Tonejs / Tone.js

A Web Audio framework for making interactive music in the browser.
https://tonejs.github.io
MIT License
13.52k stars 983 forks source link

Player started with offsets intermittently plays erroneously past desired offset, depending on lookAhead #912

Closed marcelblum closed 2 years ago

marcelblum commented 3 years ago

Been playing with taking a single buffer of a drum break and then using multiple Player objects to play different offset slices of different parts of the break, and noticed this bug during rapid fire drumpad triggering. Here is an example:

https://codepen.io/keymapper/pen/BaRLBMq

This creates 8 Player objects out of a short buffer and each plays a different slice of the drum break using offset parameters fed to Player.start() or Player.restart(). Each Player can be started by hitting a keyboard key Z through comma. The bug can be reproduced by rapidly hitting any one of those keys multiple times in succession. For example try smashing on the C key (snare sound) about a dozen times at a rate of at least 5 presses/second (you'll need at least 2 fingers :grin:) and you should hear the bug (e.g. you'll hear it play past the end of the snare sometimes). Observed in Chrome and Firefox on Windows 10.

Some notes:

marcelblum commented 3 years ago

Addendum: I think this may be related to another issue I see occasionally under similar circumstances, which is much harder to reproduce, where sometimes a Player started with offsets will erroneously stop playing too soon (not play all the way until the desired end offset). I will try to post a separate issue for this if I can find a way to consistently repro, but it's similar in that it happens during rapid fire drum pad type triggering, and the slices being played are very short (like < 0.25secs). Just wanted to mention this in case you can think of a situation where the end offset could possibly be miscalculated internally by Tone, to the upside or downside.

marcelblum commented 3 years ago

Update with an interesting finding, AFAICT this bug does not occur if lookAhead is set to even a fraction below .1. But anything >= .1 brings the bug with it. So lookAhead = .09, lookAhead = .05, no bug. JS timing quirks at play perhaps?

tambien commented 3 years ago

@marcelblum good to know! and thanks for the detailed bug report. i've been so busy i haven't had time to look into this further. I wonder if the bug has something to do with not compensating correctly for the lookAhead which is applied to the audio context time somewhere in the player scheduling code. a red flag would be if currentTime instead of now() is used somewhere.

marcelblum commented 3 years ago

No worries, I know things like this are maddening to debug. I've been trying to figure out the cause but haven't stumbled on anything obvious yet but will try to dig further. One thing that was puzzling me that I wanted to ask while I have your ear is why here https://github.com/Tonejs/Tone.js/blob/7ec588f9370057db0be5edbb805488aaef53e5a4/Tone/source/buffer/ToneBufferSource.ts#L185 there is an explicit stop() being scheduled on the native AudioBufferSourceNode playing the Player's buffer if there is an offset specified. Why not just leave it to the native AudioBufferSourceNode to stop at precision native time on its own at the exact offset time specified in the call to start()? Apologies if I am missing something obvious here or misunderstanding the intention (Some kind of legacy browser quirk workaround? Something to accommodate fading options?).

tambien commented 2 years ago

Fixed by #968