mirror of
https://github.com/godotengine/godot.git
synced 2024-11-22 04:06:14 +00:00
Merge pull request #93362 from adamscott/fix-web-audio-pause
Fix pausing issues when using Web Audio samples
This commit is contained in:
commit
ee3b31da80
@ -423,68 +423,38 @@ class SampleNode {
|
|||||||
this.streamObjectId = params.streamObjectId;
|
this.streamObjectId = params.streamObjectId;
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
this.offset = options.offset ?? 0;
|
this.offset = options.offset ?? 0;
|
||||||
/** @type {LoopMode} */
|
/** @type {number} */
|
||||||
this.startTime = options.startTime ?? 0;
|
this.startTime = options.startTime ?? 0;
|
||||||
|
/** @type {boolean} */
|
||||||
|
this.isPaused = false;
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
this.pauseTime = 0;
|
this.pauseTime = 0;
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
this._playbackRate = 44100;
|
this._playbackRate = 44100;
|
||||||
/** @type {LoopMode} */
|
/** @type {LoopMode} */
|
||||||
this._loopMode = 'disabled';
|
this.loopMode = 'disabled';
|
||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
this._pitchScale = 1;
|
this._pitchScale = 1;
|
||||||
|
/** @type {number} */
|
||||||
|
this._sourceStartTime = 0;
|
||||||
/** @type {Map<Bus, SampleNodeBus>} */
|
/** @type {Map<Bus, SampleNodeBus>} */
|
||||||
this._sampleNodeBuses = new Map();
|
this._sampleNodeBuses = new Map();
|
||||||
/** @type {AudioBufferSourceNode} */
|
/** @type {AudioBufferSourceNode | null} */
|
||||||
this._source = GodotAudio.ctx.createBufferSource();
|
this._source = GodotAudio.ctx.createBufferSource();
|
||||||
|
/** @type {AudioBufferSourceNode["onended"]} */
|
||||||
|
this._onended = null;
|
||||||
|
|
||||||
this.setPlaybackRate(options.playbackRate ?? 44100);
|
this.setPlaybackRate(options.playbackRate ?? 44100);
|
||||||
this.setLoopMode(options.loopMode ?? this.getSample().loopMode ?? 'disabled');
|
this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled';
|
||||||
this._source.buffer = this.getSample().getAudioBuffer();
|
this._source.buffer = this.getSample().getAudioBuffer();
|
||||||
|
|
||||||
/** @type {SampleNode} */
|
this._addEndedListener();
|
||||||
// eslint-disable-next-line consistent-this
|
|
||||||
const self = this;
|
|
||||||
this._source.addEventListener('ended', (_) => {
|
|
||||||
switch (self.getSample().loopMode) {
|
|
||||||
case 'disabled':
|
|
||||||
GodotAudio.SampleNode.stopSampleNode(self.id);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// do nothing
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const bus = GodotAudio.Bus.getBus(params.busIndex);
|
const bus = GodotAudio.Bus.getBus(params.busIndex);
|
||||||
const sampleNodeBus = this.getSampleNodeBus(bus);
|
const sampleNodeBus = this.getSampleNodeBus(bus);
|
||||||
sampleNodeBus.setVolume(options.volume);
|
sampleNodeBus.setVolume(options.volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the loop mode of the current instance.
|
|
||||||
* @returns {LoopMode}
|
|
||||||
*/
|
|
||||||
getLoopMode() {
|
|
||||||
return this._loopMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the loop mode of the current instance.
|
|
||||||
* @param {LoopMode} val Value to set.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
setLoopMode(val) {
|
|
||||||
this._loopMode = val;
|
|
||||||
switch (val) {
|
|
||||||
case 'forward':
|
|
||||||
case 'backward':
|
|
||||||
this._source.loop = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this._source.loop = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the playback rate.
|
* Gets the playback rate.
|
||||||
* @returns {number}
|
* @returns {number}
|
||||||
@ -542,7 +512,8 @@ class SampleNode {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
start() {
|
start() {
|
||||||
this._source.start(this.offset);
|
this._resetSourceStartTime();
|
||||||
|
this._source.start(this.startTime, this.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -550,10 +521,19 @@ class SampleNode {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
stop() {
|
stop() {
|
||||||
this._source.stop();
|
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the `SampleNode`.
|
||||||
|
*/
|
||||||
|
restart() {
|
||||||
|
this.isPaused = false;
|
||||||
|
this.pauseTime = 0;
|
||||||
|
this._resetSourceStartTime();
|
||||||
|
this._restart();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pauses the `SampleNode`.
|
* Pauses the `SampleNode`.
|
||||||
* @param {boolean} [enable=true] State of the pause.
|
* @param {boolean} [enable=true] State of the pause.
|
||||||
@ -561,21 +541,11 @@ class SampleNode {
|
|||||||
*/
|
*/
|
||||||
pause(enable = true) {
|
pause(enable = true) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
this.pauseTime = (GodotAudio.ctx.currentTime - this.startTime) / this.playbackRate;
|
this._pause();
|
||||||
this._source.stop();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.pauseTime === 0) {
|
this._unpause();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._source.disconnect();
|
|
||||||
this._source = GodotAudio.ctx.createBufferSource();
|
|
||||||
|
|
||||||
this._source.buffer = this.getSample().getAudioBuffer();
|
|
||||||
this._source.connect(this._gain);
|
|
||||||
this._source.start(this.offset + this.pauseTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -623,19 +593,33 @@ class SampleNode {
|
|||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
clear() {
|
clear() {
|
||||||
this._source.stop();
|
this.isPaused = false;
|
||||||
this._source.disconnect();
|
this.pauseTime = 0;
|
||||||
this._source = null;
|
|
||||||
|
if (this._source != null) {
|
||||||
|
this._source.removeEventListener('ended', this._onended);
|
||||||
|
this._onended = null;
|
||||||
|
this._source.stop();
|
||||||
|
this._source.disconnect();
|
||||||
|
this._source = null;
|
||||||
|
}
|
||||||
|
|
||||||
for (const sampleNodeBus of this._sampleNodeBuses.values()) {
|
for (const sampleNodeBus of this._sampleNodeBuses.values()) {
|
||||||
sampleNodeBus.clear();
|
sampleNodeBus.clear();
|
||||||
}
|
}
|
||||||
this._sampleNodeBuses.clear();
|
this._sampleNodeBuses.clear();
|
||||||
this._sampleNodeBuses = null;
|
|
||||||
|
|
||||||
GodotAudio.SampleNode.delete(this.id);
|
GodotAudio.SampleNode.delete(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the source start time
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_resetSourceStartTime() {
|
||||||
|
this._sourceStartTime = GodotAudio.ctx.currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Syncs the `AudioNode` playback rate based on the `SampleNode` playback rate and pitch scale.
|
* Syncs the `AudioNode` playback rate based on the `SampleNode` playback rate and pitch scale.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
@ -643,6 +627,80 @@ class SampleNode {
|
|||||||
_syncPlaybackRate() {
|
_syncPlaybackRate() {
|
||||||
this._source.playbackRate.value = this.getPlaybackRate() * this.getPitchScale();
|
this._source.playbackRate.value = this.getPlaybackRate() * this.getPitchScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the `SampleNode`.
|
||||||
|
* Honors `isPaused` and `pauseTime`.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_restart() {
|
||||||
|
this._source.disconnect();
|
||||||
|
this._source = GodotAudio.ctx.createBufferSource();
|
||||||
|
this._source.buffer = this.getSample().getAudioBuffer();
|
||||||
|
|
||||||
|
// Make sure that we connect the new source to the sample node bus.
|
||||||
|
for (const sampleNodeBus of this._sampleNodeBuses.values()) {
|
||||||
|
this.connect(sampleNodeBus.getInputNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
this._addEndedListener();
|
||||||
|
const pauseTime = this.isPaused
|
||||||
|
? this.pauseTime
|
||||||
|
: 0;
|
||||||
|
this._source.start(this.startTime, this.offset + pauseTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses the `SampleNode`.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_pause() {
|
||||||
|
this.isPaused = true;
|
||||||
|
this.pauseTime = (GodotAudio.ctx.currentTime - this._sourceStartTime) / this.getPlaybackRate();
|
||||||
|
this._source.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unpauses the `SampleNode`.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_unpause() {
|
||||||
|
this._restart();
|
||||||
|
this.isPaused = false;
|
||||||
|
this.pauseTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an "ended" listener to the source node to repeat it if necessary.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
_addEndedListener() {
|
||||||
|
if (this._onended != null) {
|
||||||
|
this._source.removeEventListener('ended', this._onended);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {SampleNode} */
|
||||||
|
// eslint-disable-next-line consistent-this
|
||||||
|
const self = this;
|
||||||
|
this._onended = (_) => {
|
||||||
|
if (self.isPaused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (self.getSample().loopMode) {
|
||||||
|
case 'disabled':
|
||||||
|
self.stop();
|
||||||
|
break;
|
||||||
|
case 'forward':
|
||||||
|
case 'backward':
|
||||||
|
self.restart();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this._source.addEventListener('ended', this._onended);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user