mirror of
https://github.com/scratchfoundation/scratch-audio.git
synced 2024-12-22 14:02:29 -05:00
schedule stop DECAY in the future for firefox
Firefox at this time cannot smoothly schedule audio parameter changes to happen immediately. Immediately scheduled changes clip when firefox tries to catch up. Smoothly fading out a sound immediately instead of clipping the end of the sound, in firefox at this time clips the sound between where the fade out starts and where firefox catches up and finishes the scheduled fade.
This commit is contained in:
parent
a79684a720
commit
fb355abb7d
4 changed files with 25 additions and 12 deletions
|
@ -113,6 +113,17 @@ class AudioEngine {
|
||||||
return 0.025;
|
return 0.025;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some environments cannot smoothly change parameters immediately, provide
|
||||||
|
* a small delay before decaying.
|
||||||
|
*
|
||||||
|
* @see {@link https://bugzilla.mozilla.org/show_bug.cgi?id=1228207}
|
||||||
|
* @const {number}
|
||||||
|
*/
|
||||||
|
get DECAY_SOON () {
|
||||||
|
return 0.05;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the input node.
|
* Get the input node.
|
||||||
* @return {AudioNode} - audio node that is the input for this effect
|
* @return {AudioNode} - audio node that is the input for this effect
|
||||||
|
|
|
@ -245,7 +245,8 @@ class SoundPlayer extends EventEmitter {
|
||||||
taken.finished().then(() => taken.dispose());
|
taken.finished().then(() => taken.dispose());
|
||||||
|
|
||||||
taken.volumeEffect.set(0);
|
taken.volumeEffect.set(0);
|
||||||
taken.outputNode.stop(this.audioEngine.audioContext.currentTime + this.audioEngine.DECAY_TIME);
|
const {audioContext, DECAY_TIME, DECAY_SOON} = this.audioEngine;
|
||||||
|
taken.outputNode.stop(audioContext.currentTime + DECAY_SOON + DECAY_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -38,9 +38,9 @@ class VolumeEffect extends Effect {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
|
||||||
const {gain} = this.outputNode;
|
const {gain} = this.outputNode;
|
||||||
const {audioContext: {currentTime}, DECAY_TIME} = this.audioEngine;
|
const {audioContext: {currentTime}, DECAY_TIME, DECAY_SOON} = this.audioEngine;
|
||||||
gain.setValueAtTime(gain.value, currentTime);
|
gain.setValueAtTime(gain.value, currentTime + DECAY_SOON);
|
||||||
gain.linearRampToValueAtTime(value / 100, currentTime + DECAY_TIME);
|
gain.linearRampToValueAtTime(value / 100, currentTime + DECAY_SOON + DECAY_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,10 +20,10 @@ tap.test('SoundPlayer', suite => {
|
||||||
suite.beforeEach(() => {
|
suite.beforeEach(() => {
|
||||||
audioContext = new AudioContext();
|
audioContext = new AudioContext();
|
||||||
audioEngine = new AudioEngine(audioContext);
|
audioEngine = new AudioEngine(audioContext);
|
||||||
// sound will be 0.1 seconds long
|
// sound will be 0.2 seconds long
|
||||||
audioContext.DECODE_AUDIO_DATA_RESULT = audioContext.createBuffer(2, 4410, 44100);
|
audioContext.DECODE_AUDIO_DATA_RESULT = audioContext.createBuffer(2, 8820, 44100);
|
||||||
audioContext.DECODE_AUDIO_DATA_FAILED = false;
|
audioContext.DECODE_AUDIO_DATA_FAILED = false;
|
||||||
const data = new Uint8Array(44100);
|
const data = new Uint8Array(0);
|
||||||
return audioEngine.decodeSoundPlayer({data}).then(result => {
|
return audioEngine.decodeSoundPlayer({data}).then(result => {
|
||||||
soundPlayer = result;
|
soundPlayer = result;
|
||||||
});
|
});
|
||||||
|
@ -88,13 +88,13 @@ tap.test('SoundPlayer', suite => {
|
||||||
inputs: [outputNode.toJSON()]
|
inputs: [outputNode.toJSON()]
|
||||||
}], 'output node connects to gain node to input node');
|
}], 'output node connects to gain node to input node');
|
||||||
|
|
||||||
audioContext.$processTo(audioEngine.DECAY_TIME / 2);
|
audioContext.$processTo(audioEngine.DECAY_SOON + audioEngine.DECAY_TIME / 2);
|
||||||
const engineInputs = help.engineInputs;
|
const engineInputs = help.engineInputs;
|
||||||
t.notEqual(engineInputs[0].gain.value, 1, 'gain value should not be 1');
|
t.notEqual(engineInputs[0].gain.value, 1, 'gain value should not be 1');
|
||||||
t.notEqual(engineInputs[0].gain.value, 0, 'gain value should not be 0');
|
t.notEqual(engineInputs[0].gain.value, 0, 'gain value should not be 0');
|
||||||
t.equal(outputNode.$state, 'PLAYING');
|
t.equal(outputNode.$state, 'PLAYING');
|
||||||
|
|
||||||
audioContext.$processTo(audioEngine.DECAY_TIME);
|
audioContext.$processTo(audioEngine.DECAY_SOON + audioEngine.DECAY_TIME + 0.001);
|
||||||
t.deepEqual(help.engineInputs, [{
|
t.deepEqual(help.engineInputs, [{
|
||||||
name: 'GainNode',
|
name: 'GainNode',
|
||||||
gain: {
|
gain: {
|
||||||
|
@ -180,18 +180,19 @@ tap.test('SoundPlayer', suite => {
|
||||||
t.equal(soundPlayer.outputNode.$state, 'PLAYING');
|
t.equal(soundPlayer.outputNode.$state, 'PLAYING');
|
||||||
t.equal(help.engineInputs[0].gain.value, 1, 'old sound connectect to gain node with volume 1');
|
t.equal(help.engineInputs[0].gain.value, 1, 'old sound connectect to gain node with volume 1');
|
||||||
|
|
||||||
audioContext.$processTo(audioContext.currentTime + 0.001);
|
const {currentTime} = audioContext;
|
||||||
|
audioContext.$processTo(currentTime + audioEngine.DECAY_SOON + 0.001);
|
||||||
t.notEqual(help.engineInputs[0].gain.value, 1,
|
t.notEqual(help.engineInputs[0].gain.value, 1,
|
||||||
'old sound connected to gain node which will fade');
|
'old sound connected to gain node which will fade');
|
||||||
|
|
||||||
audioContext.$processTo(audioContext.currentTime + audioEngine.DECAY_TIME + 0.001);
|
audioContext.$processTo(currentTime + audioEngine.DECAY_SOON + audioEngine.DECAY_TIME + 0.001);
|
||||||
t.equal(soundPlayer.outputNode.$state, 'PLAYING');
|
t.equal(soundPlayer.outputNode.$state, 'PLAYING');
|
||||||
t.equal(firstPlayNode.$state, 'FINISHED');
|
t.equal(firstPlayNode.$state, 'FINISHED');
|
||||||
|
|
||||||
t.equal(help.engineInputs[0].gain.value, 0, 'faded old sound to 0');
|
t.equal(help.engineInputs[0].gain.value, 0, 'faded old sound to 0');
|
||||||
|
|
||||||
t.equal(log.length, 1);
|
t.equal(log.length, 1);
|
||||||
audioContext.$processTo(audioContext.currentTime + 0.2);
|
audioContext.$processTo(currentTime + audioEngine.DECAY_SOON + audioEngine.DECAY_TIME + 0.3);
|
||||||
|
|
||||||
// wait for a micro-task loop to fire our previous events
|
// wait for a micro-task loop to fire our previous events
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
|
|
Loading…
Reference in a new issue