@gjsify/webaudio 0.3.13 → 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,62 +2,64 @@ import { AudioNode } from "./audio-node.js";
2
2
  import { AudioParam } from "./audio-param.js";
3
3
  import { GstPlayer } from "./gst-player.js";
4
4
  import { GainNode } from "./gain-node.js";
5
- class AudioBufferSourceNode extends AudioNode {
6
- buffer = null;
7
- loop = false;
8
- loopStart = 0;
9
- loopEnd = 0;
10
- playbackRate;
11
- onended = null;
12
- _player = null;
13
- _started = false;
14
- constructor() {
15
- super(0, 1);
16
- this.playbackRate = new AudioParam(1, 0.0625, 16);
17
- }
18
- start(when = 0, offset = 0, duration) {
19
- if (this._started) {
20
- throw new DOMException("AudioBufferSourceNode can only be started once", "InvalidStateError");
21
- }
22
- this._started = true;
23
- if (!this.buffer) return;
24
- const gainNode = this._findGainNode();
25
- const volume = gainNode ? gainNode.gain.value : 1;
26
- this._player = new GstPlayer({
27
- audioBuffer: this.buffer,
28
- volume,
29
- loop: this.loop,
30
- offset,
31
- duration,
32
- playbackRate: this.playbackRate.value,
33
- onEnded: () => {
34
- if (gainNode) {
35
- gainNode._activePlayers.delete(this._player);
36
- }
37
- this._player = null;
38
- this.onended?.();
39
- }
40
- });
41
- if (gainNode && this._player) {
42
- gainNode._activePlayers.add(this._player);
43
- }
44
- }
45
- stop(_when = 0) {
46
- if (this._player) {
47
- this._player.stop();
48
- }
49
- }
50
- /** Walk the output chain to find a GainNode */
51
- _findGainNode() {
52
- for (const node of this._outputs) {
53
- if (node instanceof GainNode) return node;
54
- for (const inner of node._outputs) {
55
- if (inner instanceof GainNode) return inner;
56
- }
57
- }
58
- return null;
59
- }
60
- }
61
- export {
62
- AudioBufferSourceNode
5
+
6
+ //#region src/audio-buffer-source-node.ts
7
+ var AudioBufferSourceNode = class extends AudioNode {
8
+ buffer = null;
9
+ loop = false;
10
+ loopStart = 0;
11
+ loopEnd = 0;
12
+ playbackRate;
13
+ onended = null;
14
+ _player = null;
15
+ _started = false;
16
+ constructor() {
17
+ super(0, 1);
18
+ this.playbackRate = new AudioParam(1, .0625, 16);
19
+ }
20
+ start(when = 0, offset = 0, duration) {
21
+ if (this._started) {
22
+ throw new DOMException("AudioBufferSourceNode can only be started once", "InvalidStateError");
23
+ }
24
+ this._started = true;
25
+ if (!this.buffer) return;
26
+ const gainNode = this._findGainNode();
27
+ const volume = gainNode ? gainNode.gain.value : 1;
28
+ this._player = new GstPlayer({
29
+ audioBuffer: this.buffer,
30
+ volume,
31
+ loop: this.loop,
32
+ offset,
33
+ duration,
34
+ playbackRate: this.playbackRate.value,
35
+ onEnded: () => {
36
+ if (gainNode) {
37
+ gainNode._activePlayers.delete(this._player);
38
+ }
39
+ this._player = null;
40
+ this.onended?.();
41
+ }
42
+ });
43
+ if (gainNode && this._player) {
44
+ gainNode._activePlayers.add(this._player);
45
+ }
46
+ }
47
+ stop(_when = 0) {
48
+ if (this._player) {
49
+ this._player.stop();
50
+ }
51
+ }
52
+ /** Walk the output chain to find a GainNode */
53
+ _findGainNode() {
54
+ for (const node of this._outputs) {
55
+ if (node instanceof GainNode) return node;
56
+ for (const inner of node._outputs) {
57
+ if (inner instanceof GainNode) return inner;
58
+ }
59
+ }
60
+ return null;
61
+ }
63
62
  };
63
+
64
+ //#endregion
65
+ export { AudioBufferSourceNode };
@@ -1,37 +1,38 @@
1
- class AudioBuffer {
2
- sampleRate;
3
- length;
4
- duration;
5
- numberOfChannels;
6
- /** @internal */
7
- _channelData;
8
- constructor(options) {
9
- this.sampleRate = options.sampleRate;
10
- this.length = options.length;
11
- this.numberOfChannels = options.numberOfChannels;
12
- this.duration = this.length / this.sampleRate;
13
- this._channelData = [];
14
- for (let i = 0; i < this.numberOfChannels; i++) {
15
- this._channelData.push(new Float32Array(this.length));
16
- }
17
- }
18
- getChannelData(channel) {
19
- if (channel < 0 || channel >= this.numberOfChannels) {
20
- throw new RangeError(`channel index ${channel} out of range [0, ${this.numberOfChannels})`);
21
- }
22
- return this._channelData[channel];
23
- }
24
- copyFromChannel(destination, channelNumber, bufferOffset = 0) {
25
- const src = this.getChannelData(channelNumber);
26
- const len = Math.min(destination.length, src.length - bufferOffset);
27
- destination.set(src.subarray(bufferOffset, bufferOffset + len));
28
- }
29
- copyToChannel(source, channelNumber, bufferOffset = 0) {
30
- const dest = this.getChannelData(channelNumber);
31
- const len = Math.min(source.length, dest.length - bufferOffset);
32
- dest.set(source.subarray(0, len), bufferOffset);
33
- }
34
- }
35
- export {
36
- AudioBuffer
1
+ //#region src/audio-buffer.ts
2
+ var AudioBuffer = class {
3
+ sampleRate;
4
+ length;
5
+ duration;
6
+ numberOfChannels;
7
+ /** @internal */
8
+ _channelData;
9
+ constructor(options) {
10
+ this.sampleRate = options.sampleRate;
11
+ this.length = options.length;
12
+ this.numberOfChannels = options.numberOfChannels;
13
+ this.duration = this.length / this.sampleRate;
14
+ this._channelData = [];
15
+ for (let i = 0; i < this.numberOfChannels; i++) {
16
+ this._channelData.push(new Float32Array(this.length));
17
+ }
18
+ }
19
+ getChannelData(channel) {
20
+ if (channel < 0 || channel >= this.numberOfChannels) {
21
+ throw new RangeError(`channel index ${channel} out of range [0, ${this.numberOfChannels})`);
22
+ }
23
+ return this._channelData[channel];
24
+ }
25
+ copyFromChannel(destination, channelNumber, bufferOffset = 0) {
26
+ const src = this.getChannelData(channelNumber);
27
+ const len = Math.min(destination.length, src.length - bufferOffset);
28
+ destination.set(src.subarray(bufferOffset, bufferOffset + len));
29
+ }
30
+ copyToChannel(source, channelNumber, bufferOffset = 0) {
31
+ const dest = this.getChannelData(channelNumber);
32
+ const len = Math.min(source.length, dest.length - bufferOffset);
33
+ dest.set(source.subarray(0, len), bufferOffset);
34
+ }
37
35
  };
36
+
37
+ //#endregion
38
+ export { AudioBuffer };
@@ -1,94 +1,93 @@
1
- import GLib from "gi://GLib?version=2.0";
1
+ import { AudioNode } from "./audio-node.js";
2
2
  import { ensureGstInit } from "./gst-init.js";
3
+ import { GainNode } from "./gain-node.js";
4
+ import { AudioBufferSourceNode } from "./audio-buffer-source-node.js";
3
5
  import { AudioBuffer } from "./audio-buffer.js";
4
- import { AudioNode } from "./audio-node.js";
5
6
  import { AudioDestinationNode } from "./audio-destination-node.js";
6
- import { AudioBufferSourceNode } from "./audio-buffer-source-node.js";
7
- import { GainNode } from "./gain-node.js";
8
7
  import { decodeAudioDataSync } from "./gst-decoder.js";
9
- class AudioContext {
10
- state = "suspended";
11
- sampleRate = 44100;
12
- destination;
13
- listener = {};
14
- _startTime;
15
- constructor() {
16
- ensureGstInit();
17
- this._startTime = GLib.get_monotonic_time();
18
- this.destination = new AudioDestinationNode();
19
- }
20
- /** Monotonically increasing time in seconds since context creation. */
21
- get currentTime() {
22
- return (GLib.get_monotonic_time() - this._startTime) / 1e6;
23
- }
24
- createGain() {
25
- return new GainNode();
26
- }
27
- createBufferSource() {
28
- return new AudioBufferSourceNode();
29
- }
30
- createBuffer(numberOfChannels, length, sampleRate) {
31
- return new AudioBuffer({ numberOfChannels, length, sampleRate });
32
- }
33
- /**
34
- * Decode encoded audio data (MP3, WAV, OGG, etc.) into an AudioBuffer.
35
- * Uses GStreamer's decodebin for format-agnostic decoding.
36
- */
37
- decodeAudioData(arrayBuffer, successCallback, errorCallback) {
38
- try {
39
- const buffer = decodeAudioDataSync(arrayBuffer);
40
- successCallback?.(buffer);
41
- return Promise.resolve(buffer);
42
- } catch (err) {
43
- const domErr = err instanceof DOMException ? err : new DOMException("Unable to decode audio data", "EncodingError");
44
- errorCallback?.(domErr);
45
- return Promise.reject(domErr);
46
- }
47
- }
48
- async resume() {
49
- this.state = "running";
50
- }
51
- async suspend() {
52
- this.state = "suspended";
53
- }
54
- async close() {
55
- this.state = "closed";
56
- }
57
- // Stub methods for APIs not yet backed by GStreamer (Phase 3)
58
- createAnalyser() {
59
- return {
60
- connect: () => {
61
- },
62
- disconnect: () => {
63
- },
64
- fftSize: 2048,
65
- frequencyBinCount: 1024,
66
- getByteFrequencyData: () => {
67
- },
68
- getFloatFrequencyData: () => {
69
- }
70
- };
71
- }
72
- createDynamicsCompressor() {
73
- return new AudioNode();
74
- }
75
- createBiquadFilter() {
76
- return new AudioNode();
77
- }
78
- createConvolver() {
79
- return new AudioNode();
80
- }
81
- createPanner() {
82
- return new AudioNode();
83
- }
84
- createStereoPanner() {
85
- return new AudioNode();
86
- }
87
- addEventListener(_type, _listener) {
88
- }
89
- removeEventListener(_type, _listener) {
90
- }
91
- }
92
- export {
93
- AudioContext
8
+ import GLib from "gi://GLib?version=2.0";
9
+
10
+ //#region src/audio-context.ts
11
+ var AudioContext = class {
12
+ state = "suspended";
13
+ sampleRate = 44100;
14
+ destination;
15
+ listener = {};
16
+ _startTime;
17
+ constructor() {
18
+ ensureGstInit();
19
+ this._startTime = GLib.get_monotonic_time();
20
+ this.destination = new AudioDestinationNode();
21
+ }
22
+ /** Monotonically increasing time in seconds since context creation. */
23
+ get currentTime() {
24
+ return (GLib.get_monotonic_time() - this._startTime) / 1e6;
25
+ }
26
+ createGain() {
27
+ return new GainNode();
28
+ }
29
+ createBufferSource() {
30
+ return new AudioBufferSourceNode();
31
+ }
32
+ createBuffer(numberOfChannels, length, sampleRate) {
33
+ return new AudioBuffer({
34
+ numberOfChannels,
35
+ length,
36
+ sampleRate
37
+ });
38
+ }
39
+ /**
40
+ * Decode encoded audio data (MP3, WAV, OGG, etc.) into an AudioBuffer.
41
+ * Uses GStreamer's decodebin for format-agnostic decoding.
42
+ */
43
+ decodeAudioData(arrayBuffer, successCallback, errorCallback) {
44
+ try {
45
+ const buffer = decodeAudioDataSync(arrayBuffer);
46
+ successCallback?.(buffer);
47
+ return Promise.resolve(buffer);
48
+ } catch (err) {
49
+ const domErr = err instanceof DOMException ? err : new DOMException("Unable to decode audio data", "EncodingError");
50
+ errorCallback?.(domErr);
51
+ return Promise.reject(domErr);
52
+ }
53
+ }
54
+ async resume() {
55
+ this.state = "running";
56
+ }
57
+ async suspend() {
58
+ this.state = "suspended";
59
+ }
60
+ async close() {
61
+ this.state = "closed";
62
+ }
63
+ createAnalyser() {
64
+ return {
65
+ connect: () => {},
66
+ disconnect: () => {},
67
+ fftSize: 2048,
68
+ frequencyBinCount: 1024,
69
+ getByteFrequencyData: () => {},
70
+ getFloatFrequencyData: () => {}
71
+ };
72
+ }
73
+ createDynamicsCompressor() {
74
+ return new AudioNode();
75
+ }
76
+ createBiquadFilter() {
77
+ return new AudioNode();
78
+ }
79
+ createConvolver() {
80
+ return new AudioNode();
81
+ }
82
+ createPanner() {
83
+ return new AudioNode();
84
+ }
85
+ createStereoPanner() {
86
+ return new AudioNode();
87
+ }
88
+ addEventListener(_type, _listener) {}
89
+ removeEventListener(_type, _listener) {}
94
90
  };
91
+
92
+ //#endregion
93
+ export { AudioContext };
@@ -1,10 +1,12 @@
1
1
  import { AudioNode } from "./audio-node.js";
2
- class AudioDestinationNode extends AudioNode {
3
- maxChannelCount = 2;
4
- constructor() {
5
- super(1, 0);
6
- }
7
- }
8
- export {
9
- AudioDestinationNode
2
+
3
+ //#region src/audio-destination-node.ts
4
+ var AudioDestinationNode = class extends AudioNode {
5
+ maxChannelCount = 2;
6
+ constructor() {
7
+ super(1, 0);
8
+ }
10
9
  };
10
+
11
+ //#endregion
12
+ export { AudioDestinationNode };
@@ -1,33 +1,34 @@
1
- class AudioNode {
2
- /** @internal downstream connections */
3
- _outputs = /* @__PURE__ */ new Set();
4
- /** @internal upstream connections */
5
- _inputs = /* @__PURE__ */ new Set();
6
- numberOfInputs;
7
- numberOfOutputs;
8
- channelCount;
9
- constructor(numberOfInputs = 1, numberOfOutputs = 1) {
10
- this.numberOfInputs = numberOfInputs;
11
- this.numberOfOutputs = numberOfOutputs;
12
- this.channelCount = 2;
13
- }
14
- connect(destination) {
15
- this._outputs.add(destination);
16
- destination._inputs.add(this);
17
- return destination;
18
- }
19
- disconnect(destination) {
20
- if (destination) {
21
- this._outputs.delete(destination);
22
- destination._inputs.delete(this);
23
- } else {
24
- for (const node of this._outputs) {
25
- node._inputs.delete(this);
26
- }
27
- this._outputs.clear();
28
- }
29
- }
30
- }
31
- export {
32
- AudioNode
1
+ //#region src/audio-node.ts
2
+ var AudioNode = class {
3
+ /** @internal downstream connections */
4
+ _outputs = new Set();
5
+ /** @internal upstream connections */
6
+ _inputs = new Set();
7
+ numberOfInputs;
8
+ numberOfOutputs;
9
+ channelCount;
10
+ constructor(numberOfInputs = 1, numberOfOutputs = 1) {
11
+ this.numberOfInputs = numberOfInputs;
12
+ this.numberOfOutputs = numberOfOutputs;
13
+ this.channelCount = 2;
14
+ }
15
+ connect(destination) {
16
+ this._outputs.add(destination);
17
+ destination._inputs.add(this);
18
+ return destination;
19
+ }
20
+ disconnect(destination) {
21
+ if (destination) {
22
+ this._outputs.delete(destination);
23
+ destination._inputs.delete(this);
24
+ } else {
25
+ for (const node of this._outputs) {
26
+ node._inputs.delete(this);
27
+ }
28
+ this._outputs.clear();
29
+ }
30
+ }
33
31
  };
32
+
33
+ //#endregion
34
+ export { AudioNode };
@@ -1,78 +1,80 @@
1
1
  import GLib from "gi://GLib?version=2.0";
2
- class AudioParam {
3
- defaultValue;
4
- minValue;
5
- maxValue;
6
- /** @internal callback invoked when value changes */
7
- _onChange = null;
8
- _value;
9
- _rampTimerId = null;
10
- constructor(defaultValue = 0, minValue = -34028235e31, maxValue = 34028235e31) {
11
- this.defaultValue = defaultValue;
12
- this.minValue = minValue;
13
- this.maxValue = maxValue;
14
- this._value = defaultValue;
15
- }
16
- get value() {
17
- return this._value;
18
- }
19
- set value(v) {
20
- this._cancelRamp();
21
- this._value = Math.max(this.minValue, Math.min(this.maxValue, v));
22
- this._onChange?.(this._value);
23
- }
24
- setValueAtTime(value, _startTime) {
25
- this.value = value;
26
- return this;
27
- }
28
- linearRampToValueAtTime(value, _endTime) {
29
- this.value = value;
30
- return this;
31
- }
32
- exponentialRampToValueAtTime(value, _endTime) {
33
- this.value = value;
34
- return this;
35
- }
36
- setTargetAtTime(target, _startTime, timeConstant) {
37
- this._cancelRamp();
38
- if (timeConstant <= 0) {
39
- this.value = target;
40
- return this;
41
- }
42
- const stepMs = Math.max(10, Math.round(timeConstant * 100));
43
- this._rampTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, stepMs, () => {
44
- const diff = target - this._value;
45
- if (Math.abs(diff) < 1e-3) {
46
- this._value = target;
47
- this._onChange?.(this._value);
48
- this._rampTimerId = null;
49
- return GLib.SOURCE_REMOVE;
50
- }
51
- const factor = 1 - Math.exp(-stepMs / (timeConstant * 1e3));
52
- this._value += diff * factor;
53
- this._onChange?.(this._value);
54
- return GLib.SOURCE_CONTINUE;
55
- });
56
- return this;
57
- }
58
- setValueCurveAtTime(_values, _startTime, _duration) {
59
- return this;
60
- }
61
- cancelScheduledValues(_startTime) {
62
- this._cancelRamp();
63
- return this;
64
- }
65
- cancelAndHoldAtTime(_cancelTime) {
66
- this._cancelRamp();
67
- return this;
68
- }
69
- _cancelRamp() {
70
- if (this._rampTimerId !== null) {
71
- GLib.source_remove(this._rampTimerId);
72
- this._rampTimerId = null;
73
- }
74
- }
75
- }
76
- export {
77
- AudioParam
2
+
3
+ //#region src/audio-param.ts
4
+ var AudioParam = class {
5
+ defaultValue;
6
+ minValue;
7
+ maxValue;
8
+ /** @internal callback invoked when value changes */
9
+ _onChange = null;
10
+ _value;
11
+ _rampTimerId = null;
12
+ constructor(defaultValue = 0, minValue = -34028235e31, maxValue = 34028235e31) {
13
+ this.defaultValue = defaultValue;
14
+ this.minValue = minValue;
15
+ this.maxValue = maxValue;
16
+ this._value = defaultValue;
17
+ }
18
+ get value() {
19
+ return this._value;
20
+ }
21
+ set value(v) {
22
+ this._cancelRamp();
23
+ this._value = Math.max(this.minValue, Math.min(this.maxValue, v));
24
+ this._onChange?.(this._value);
25
+ }
26
+ setValueAtTime(value, _startTime) {
27
+ this.value = value;
28
+ return this;
29
+ }
30
+ linearRampToValueAtTime(value, _endTime) {
31
+ this.value = value;
32
+ return this;
33
+ }
34
+ exponentialRampToValueAtTime(value, _endTime) {
35
+ this.value = value;
36
+ return this;
37
+ }
38
+ setTargetAtTime(target, _startTime, timeConstant) {
39
+ this._cancelRamp();
40
+ if (timeConstant <= 0) {
41
+ this.value = target;
42
+ return this;
43
+ }
44
+ const stepMs = Math.max(10, Math.round(timeConstant * 100));
45
+ this._rampTimerId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, stepMs, () => {
46
+ const diff = target - this._value;
47
+ if (Math.abs(diff) < .001) {
48
+ this._value = target;
49
+ this._onChange?.(this._value);
50
+ this._rampTimerId = null;
51
+ return GLib.SOURCE_REMOVE;
52
+ }
53
+ const factor = 1 - Math.exp(-stepMs / (timeConstant * 1e3));
54
+ this._value += diff * factor;
55
+ this._onChange?.(this._value);
56
+ return GLib.SOURCE_CONTINUE;
57
+ });
58
+ return this;
59
+ }
60
+ setValueCurveAtTime(_values, _startTime, _duration) {
61
+ return this;
62
+ }
63
+ cancelScheduledValues(_startTime) {
64
+ this._cancelRamp();
65
+ return this;
66
+ }
67
+ cancelAndHoldAtTime(_cancelTime) {
68
+ this._cancelRamp();
69
+ return this;
70
+ }
71
+ _cancelRamp() {
72
+ if (this._rampTimerId !== null) {
73
+ GLib.source_remove(this._rampTimerId);
74
+ this._rampTimerId = null;
75
+ }
76
+ }
78
77
  };
78
+
79
+ //#endregion
80
+ export { AudioParam };