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

Bugfixes for p5.Amplitude and p5.Soundfile for browsers without AudioWorklet support #380

Merged
merged 4 commits into from
Aug 21, 2019

Conversation

oshoham
Copy link
Contributor

@oshoham oshoham commented Aug 15, 2019

As pointed out by @therewasaguy, there is a bug in the AudioWorklet processor for p5.Amplitude in Firefox and Safari, in which the smoothing AudioParam is being passed to the processor as a Float32 array by the AudioWorklet polyfill, rather than as a single value. This PR fixes that issue by setting the smoothing parameter via the worklet's message port instead of using an AudioParam.

This PR also fixes an issue with pausing p5.SoundFiles, in which the SoundFile AudioWorklet processor was incorrectly setting the SoundFile's playback position to 0 on pause.

Copy link
Member

@therewasaguy therewasaguy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice fixes, @oshoham ! I found one remaining small issue. I added a log of soundFile.currentTime() to examples/pause_soundfile to test it manually. Of course, it would be nice if the test were automated!

process(inputs) {
const input = inputs[0];
const inputChannel = input[0];
const position = inputChannel[inputChannel.length - 1] || 0;

this.port.postMessage({ name: 'position', position: position });
if (!this.paused) {
this.port.postMessage({ name: 'position', position: position });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems we can't rely on this.paused having updated in Safari. Maybe this logic needs to be in the SoundFile?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, it's easy enough to move this logic to the SoundFile class

src/soundfile.js Outdated
@@ -539,6 +539,8 @@ define(function (require) {
var pTime = time + now;

if (this.isPlaying() && this.buffer && this.bufferSourceNode) {
this._workletNode.port.postMessage({ name: 'pause' });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in Safari, this message doesn't always update the worker's paused attribute before it can post a position message of 0. As a result, SoundFile._lastPos gets set to 0 and so SoundFile.currentTime() returns 0 while the soundfile is paused. It's inconsistent, happens maybe half the time, and only in Safari.

It resumes playback from the proper position because pauseTime gets set below. Maybe we also/instead need to set a paused attribute on the SoundFile rather than the Processor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The solution I arrived at was to remove the pause logic from the processor and add a guard statement in the worklet node's port callback that returns early if the SoundFile is paused or the reported playback position is 0, which handles the race condition that you encountered in Safari.

Because the processor always looks for the last position value in the current frame of the counter buffer, the playback position will only ever be 0 if the SoundFile is paused (i.e., on the 1st frame, the playback position will be 128/256/whatever the buffer size of the worklet processor is).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the playback position will only ever be 0 if the SoundFile is paused

I'm not sure if this is the case in Firefox, where if you pause and un-pause rapidly, the soundfile will stutter at the same position (including position 0), rather than advancing one buffer frame at a time. That's a tiny bug but probably not a blocker on this PR.

@@ -539,6 +539,8 @@ define(function (require) {
var pTime = time + now;

if (this.isPlaying() && this.buffer && this.bufferSourceNode) {
this._workletNode.port.postMessage({ name: 'pause' });

this.pauseTime = this.currentTime();
this.bufferSourceNode.stop(pTime);
this._counterNode.stop(pTime);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one issue we should be aware of is that a pause can be scheduled in the future...perhaps a reason to make pause an a-rate param so that we could setValueAtTime?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we move the pause logic out of the worklet processor and into the SoundFile, is that still an issue?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, unfortunately that wouldn’t solve the issue. It was an issue before this pr. Just thinking whether keeping pause logic in the processor could help fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried making pause an a-rate AudioParam that would get converted to a boolean in the worklet processor, but I had trouble getting that approach to work properly, even in Chrome. For some reason, setValueAtTime wasn't changing the value of the AudioParam.

One way to handle this problem with the approach that I settled on (see the comment thread above) could be to modify this guard statement to only check event.position.data === 0 rather than this._paused || event.position.data === 0.

event.data.position should only equal 0 when the counter buffer has actually stopped playing, which would be compatible with scheduling a pause in the future. I'm not 100% sure this would work, but it's my best guess...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice solution! At some point, I'd be curious to experiment more with setValueAtTime on worklet AudioParams, but this works for now.

Either way, it seems like we only need to check event.position.data === 0 in the guard statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so, yes. I changed the guard statement to only check event.position.data === 0 and it seems to work on Chrome, Firefox, and Safari.

createP('Press any key to pause. Resume when the key is released')
}

function touchStarted() {
if (getAudioContext().state !== 'running') {
getAudioContext().resume();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a userStartAudio method that can be useful for this, too https://p5js.org/reference/#/p5.sound/userStartAudio

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the sketch to use userStartAudio instead.

Copy link
Member

@therewasaguy therewasaguy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

…pause position logic out of AudioWorklet processor
…sume() on user interaction before any other audio code
@oshoham oshoham force-pushed the amplitude-getlevel-bugfix branch from 9c5f0ef to b32e460 Compare August 21, 2019 16:51
@oshoham oshoham merged commit 4d3a383 into processing:master Aug 21, 2019
@oshoham oshoham deleted the amplitude-getlevel-bugfix branch August 21, 2019 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants