summaryrefslogtreecommitdiffstats
path: root/platform/web/js/libs/library_godot_audio.js
diff options
context:
space:
mode:
Diffstat (limited to 'platform/web/js/libs/library_godot_audio.js')
-rw-r--r--platform/web/js/libs/library_godot_audio.js128
1 files changed, 120 insertions, 8 deletions
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 66d0d3d79d..40fb0c356c 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -328,8 +328,10 @@ class SampleNodeBus {
* offset?: number
* playbackRate?: number
* startTime?: number
+ * pitchScale?: number
* loopMode?: LoopMode
* volume?: Float32Array
+ * start?: boolean
* }} SampleNodeOptions
*/
@@ -421,9 +423,15 @@ class SampleNode {
/** @type {number} */
this.offset = options.offset ?? 0;
/** @type {number} */
+ this._playbackPosition = options.offset;
+ /** @type {number} */
this.startTime = options.startTime ?? 0;
/** @type {boolean} */
this.isPaused = false;
+ /** @type {boolean} */
+ this.isStarted = false;
+ /** @type {boolean} */
+ this.isCanceled = false;
/** @type {number} */
this.pauseTime = 0;
/** @type {number} */
@@ -431,7 +439,7 @@ class SampleNode {
/** @type {LoopMode} */
this.loopMode = options.loopMode ?? this.getSample().loopMode ?? 'disabled';
/** @type {number} */
- this._pitchScale = 1;
+ this._pitchScale = options.pitchScale ?? 1;
/** @type {number} */
this._sourceStartTime = 0;
/** @type {Map<Bus, SampleNodeBus>} */
@@ -440,6 +448,8 @@ class SampleNode {
this._source = GodotAudio.ctx.createBufferSource();
this._onended = null;
+ /** @type {AudioWorkletNode | null} */
+ this._positionWorklet = null;
this.setPlaybackRate(options.playbackRate ?? 44100);
this._source.buffer = this.getSample().getAudioBuffer();
@@ -449,6 +459,8 @@ class SampleNode {
const bus = GodotAudio.Bus.getBus(params.busIndex);
const sampleNodeBus = this.getSampleNodeBus(bus);
sampleNodeBus.setVolume(options.volume);
+
+ this.connectPositionWorklet(options.start);
}
/**
@@ -460,6 +472,14 @@ class SampleNode {
}
/**
+ * Gets the playback position.
+ * @returns {number}
+ */
+ getPlaybackPosition() {
+ return this._playbackPosition;
+ }
+
+ /**
* Sets the playback rate.
* @param {number} val Value to set.
* @returns {void}
@@ -508,8 +528,12 @@ class SampleNode {
* @returns {void}
*/
start() {
+ if (this.isStarted) {
+ return;
+ }
this._resetSourceStartTime();
this._source.start(this.startTime, this.offset);
+ this.isStarted = true;
}
/**
@@ -585,17 +609,73 @@ class SampleNode {
}
/**
+ * Sets up and connects the source to the GodotPositionReportingProcessor
+ * If the worklet module is not loaded in, it will be added
+ */
+ connectPositionWorklet(start) {
+ try {
+ this._positionWorklet = this.createPositionWorklet();
+ this._source.connect(this._positionWorklet);
+ if (start) {
+ this.start();
+ }
+ } catch (error) {
+ if (error?.name !== 'InvalidStateError') {
+ throw error;
+ }
+ const path = GodotConfig.locate_file('godot.audio.position.worklet.js');
+ GodotAudio.ctx.audioWorklet
+ .addModule(path)
+ .then(() => {
+ if (!this.isCanceled) {
+ this._positionWorklet = this.createPositionWorklet();
+ this._source.connect(this._positionWorklet);
+ if (start) {
+ this.start();
+ }
+ }
+ }).catch((addModuleError) => {
+ GodotRuntime.error('Failed to create PositionWorklet.', addModuleError);
+ });
+ }
+ }
+
+ /**
+ * Creates the AudioWorkletProcessor used to track playback position.
+ * @returns {AudioWorkletNode}
+ */
+ createPositionWorklet() {
+ const worklet = new AudioWorkletNode(
+ GodotAudio.ctx,
+ 'godot-position-reporting-processor'
+ );
+ worklet.port.onmessage = (event) => {
+ switch (event.data['type']) {
+ case 'position':
+ this._playbackPosition = (parseInt(event.data.data, 10) / this.getSample().sampleRate) + this.offset;
+ break;
+ default:
+ // Do nothing.
+ }
+ };
+ return worklet;
+ }
+
+ /**
* Clears the `SampleNode`.
* @returns {void}
*/
clear() {
+ this.isCanceled = true;
this.isPaused = false;
this.pauseTime = 0;
if (this._source != null) {
this._source.removeEventListener('ended', this._onended);
this._onended = null;
- this._source.stop();
+ if (this.isStarted) {
+ this._source.stop();
+ }
this._source.disconnect();
this._source = null;
}
@@ -605,6 +685,12 @@ class SampleNode {
}
this._sampleNodeBuses.clear();
+ if (this._positionWorklet) {
+ this._positionWorklet.disconnect();
+ this._positionWorklet.port.onmessage = null;
+ this._positionWorklet = null;
+ }
+
GodotAudio.SampleNode.delete(this.id);
}
@@ -630,7 +716,9 @@ class SampleNode {
* @returns {void}
*/
_restart() {
- this._source.disconnect();
+ if (this._source != null) {
+ this._source.disconnect();
+ }
this._source = GodotAudio.ctx.createBufferSource();
this._source.buffer = this.getSample().getAudioBuffer();
@@ -643,7 +731,9 @@ class SampleNode {
const pauseTime = this.isPaused
? this.pauseTime
: 0;
+ this.connectPositionWorklet();
this._source.start(this.startTime, this.offset + pauseTime);
+ this.isStarted = true;
}
/**
@@ -858,7 +948,10 @@ class Bus {
* @returns {void}
*/
setVolumeDb(val) {
- this._gainNode.gain.value = GodotAudio.db_to_linear(val);
+ const linear = GodotAudio.db_to_linear(val);
+ if (isFinite(linear)) {
+ this._gainNode.gain.value = linear;
+ }
}
/**
@@ -1257,7 +1350,7 @@ const _GodotAudio = {
startOptions
) {
GodotAudio.SampleNode.stopSampleNode(playbackObjectId);
- const sampleNode = GodotAudio.SampleNode.create(
+ GodotAudio.SampleNode.create(
{
busIndex,
id: playbackObjectId,
@@ -1265,7 +1358,6 @@ const _GodotAudio = {
},
startOptions
);
- sampleNode.start();
},
/**
@@ -1557,13 +1649,14 @@ const _GodotAudio = {
},
godot_audio_sample_start__proxy: 'sync',
- godot_audio_sample_start__sig: 'viiiii',
+ godot_audio_sample_start__sig: 'viiiifi',
/**
* Starts a sample.
* @param {number} playbackObjectIdStrPtr Playback object id pointer
* @param {number} streamObjectIdStrPtr Stream object id pointer
* @param {number} busIndex Bus index
* @param {number} offset Sample offset
+ * @param {number} pitchScale Pitch scale
* @param {number} volumePtr Volume pointer
* @returns {void}
*/
@@ -1572,6 +1665,7 @@ const _GodotAudio = {
streamObjectIdStrPtr,
busIndex,
offset,
+ pitchScale,
volumePtr
) {
/** @type {string} */
@@ -1580,11 +1674,13 @@ const _GodotAudio = {
const streamObjectId = GodotRuntime.parseString(streamObjectIdStrPtr);
/** @type {Float32Array} */
const volume = GodotRuntime.heapSub(HEAPF32, volumePtr, 8);
- /** @type {SampleNodeConstructorOptions} */
+ /** @type {SampleNodeOptions} */
const startOptions = {
offset,
volume,
playbackRate: 1,
+ pitchScale,
+ start: true,
};
GodotAudio.start_sample(
playbackObjectId,
@@ -1630,6 +1726,22 @@ const _GodotAudio = {
return Number(GodotAudio.sampleNodes.has(playbackObjectId));
},
+ godot_audio_get_sample_playback_position__proxy: 'sync',
+ godot_audio_get_sample_playback_position__sig: 'di',
+ /**
+ * Returns the position of the playback position.
+ * @param {number} playbackObjectIdStrPtr Playback object id pointer
+ * @returns {number}
+ */
+ godot_audio_get_sample_playback_position: function (playbackObjectIdStrPtr) {
+ const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
+ const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId);
+ if (sampleNode == null) {
+ return 0;
+ }
+ return sampleNode.getPlaybackPosition();
+ },
+
godot_audio_sample_update_pitch_scale__proxy: 'sync',
godot_audio_sample_update_pitch_scale__sig: 'vii',
/**