@gjsify/webaudio 0.32.0 → 0.34.0

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.
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{AudioNode as e}from"./audio-node.js";import{ensureGstInit as t}from"./gst-init.js";import{stopAllPipelines as n}from"./gst-teardown.js";import{GainNode as r}from"./gain-node.js";import{AudioBufferSourceNode as i}from"./audio-buffer-source-node.js";import{AudioBuffer as a}from"./audio-buffer.js";import{AudioDestinationNode as o}from"./audio-destination-node.js";import{decodeAudioDataSync as s}from"./gst-decoder.js";import c from"gi://GLib?version=2.0";var AudioContext=class{state=`suspended`;sampleRate=44100;destination;listener={};_startTime;constructor(){t(),this._startTime=c.get_monotonic_time(),this.destination=new o}get currentTime(){return(c.get_monotonic_time()-this._startTime)/1e6}createGain(){return new r}createBufferSource(){return new i}createBuffer(e,t,n){return new a({numberOfChannels:e,length:t,sampleRate:n})}decodeAudioData(e,t,n){try{let n=s(e);return t?.(n),Promise.resolve(n)}catch(e){let t=e instanceof DOMException?e:new DOMException(`Unable to decode audio data`,`EncodingError`);return n?.(t),Promise.reject(t)}}async resume(){this.state=`running`}async suspend(){this.state=`suspended`}async close(){this.state=`closed`,n()}createAnalyser(){return{connect:()=>{},disconnect:()=>{},fftSize:2048,frequencyBinCount:1024,getByteFrequencyData:()=>{},getFloatFrequencyData:()=>{}}}createDynamicsCompressor(){return new e}createBiquadFilter(){return new e}createConvolver(){return new e}createPanner(){return new e}createStereoPanner(){return new e}addEventListener(e,t){}removeEventListener(e,t){}};export{AudioContext};
1
+ import"./_virtual/_rolldown/runtime.js";import{AudioNode as e}from"./audio-node.js";import{tryEnsureGstInit as t}from"./gst-init.js";import{stopAllPipelines as n}from"./gst-teardown.js";import{GainNode as r}from"./gain-node.js";import{AudioBufferSourceNode as i}from"./audio-buffer-source-node.js";import{AudioBuffer as a}from"./audio-buffer.js";import{AudioDestinationNode as o}from"./audio-destination-node.js";import{decodeAudioDataSync as s}from"./gst-decoder.js";import c from"gi://GLib?version=2.0";var AudioContext=class{state=`suspended`;sampleRate=44100;destination;listener={};_startTime;constructor(){let e=t();e!==null&&console.warn(`[webaudio] AudioContext: GStreamer is unavailable, continuing SILENT — ${e}\n Audio needs GStreamer 1.0 with gst-plugins-base. The @gjsify/gtk-runtime-* bundles do not ship it, so install it system-wide (Fedora: gstreamer1 gstreamer1-plugins-base; Windows: an MSYS2/gvsbuild GStreamer on PATH).`),this._startTime=c.get_monotonic_time(),this.destination=new o}get currentTime(){return(c.get_monotonic_time()-this._startTime)/1e6}createGain(){return new r}createBufferSource(){return new i}createBuffer(e,t,n){return new a({numberOfChannels:e,length:t,sampleRate:n})}decodeAudioData(e,t,n){try{let n=s(e);return t?.(n),Promise.resolve(n)}catch(e){let t=e instanceof DOMException?e:new DOMException(`Unable to decode audio data`,`EncodingError`);return n?.(t),Promise.reject(t)}}async resume(){this.state=`running`}async suspend(){this.state=`suspended`}async close(){this.state=`closed`,n()}createAnalyser(){return{connect:()=>{},disconnect:()=>{},fftSize:2048,frequencyBinCount:1024,getByteFrequencyData:()=>{},getFloatFrequencyData:()=>{}}}createDynamicsCompressor(){return new e}createBiquadFilter(){return new e}createConvolver(){return new e}createPanner(){return new e}createStereoPanner(){return new e}addEventListener(e,t){}removeEventListener(e,t){}};export{AudioContext};
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import e from"gi://Gst?version=1.0";import t from"gi://GstApp?version=1.0";let n=!1;function ensureGstAppLoaded(){if(t.AppSrc===void 0)throw Error(`GstApp typelib is unavailable — install gstreamer1-plugins-base (gst-plugins-base)`)}function ensureGstInit(){n||=(e.init(null),ensureGstAppLoaded(),!0)}export{e as Gst,ensureGstInit};
1
+ import"./_virtual/_rolldown/runtime.js";import e from"gi://Gst?version=1.0";import t from"gi://GstApp?version=1.0";let n=!1;function ensureGstAppLoaded(){if(t.AppSrc===void 0)throw Error(`GstApp typelib is unavailable — install gstreamer1-plugins-base (gst-plugins-base)`)}const defaultInitializer=()=>{e.init(null),ensureGstAppLoaded()};let r=`pending`;function tryEnsureGstInit(e=defaultInitializer){let t=e===defaultInitializer;if(t&&r!==`pending`)return r===`ready`?null:r.failed;try{return e(),t&&(r=`ready`,n=!0),null}catch(e){let n=e instanceof Error?e.message:String(e);return t&&(r={failed:n}),n}}function primeGstOutcomeForTests(e){if(e===void 0){r=`pending`,n=!1;return}r=e===null?`ready`:{failed:e},n=e===null}function ensureGstInit(){if(n)return;let e=tryEnsureGstInit();if(e!==null)throw Error(e)}export{e as Gst,ensureGstInit,primeGstOutcomeForTests,tryEnsureGstInit};
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{Gst as e,ensureGstInit as t}from"./gst-init.js";import{stopPipeline as n,trackPipeline as r}from"./gst-teardown.js";const i=new Set([`audio/mpeg`,`audio/mp3`,`audio/wav`,`audio/x-wav`,`audio/ogg`,`audio/webm`,`audio/flac`,`audio/x-flac`,`audio/aac`,`audio/mp4`]);var HTMLAudioElement=class{src=``;volume=1;loop=!1;paused=!0;currentTime=0;duration=0;readyState=0;_pipeline=null;canPlayType(e){let t=e.split(`;`)[0].trim().toLowerCase();return i.has(t)?`maybe`:``}play(){return!this.src||(t(),this._cleanup(),this._pipeline=e.ElementFactory.make(`playbin`,`player`),!this._pipeline)?Promise.resolve():(r(this._pipeline),this._pipeline.set_property(`uri`,this.src),this._pipeline.set_property(`volume`,this.volume),this._pipeline.set_state(e.State.PLAYING),this.paused=!1,Promise.resolve())}pause(){this._pipeline&&(this._pipeline.set_state(e.State.PAUSED),this.paused=!0)}load(){this._cleanup()}addEventListener(e,t){}removeEventListener(e,t){}_cleanup(){this._pipeline&&=(n(this._pipeline),null)}};export{HTMLAudioElement};
1
+ import"./_virtual/_rolldown/runtime.js";import{Gst as e,tryEnsureGstInit as t}from"./gst-init.js";import{stopPipeline as n,trackPipeline as r}from"./gst-teardown.js";const i=new Set([`audio/mpeg`,`audio/mp3`,`audio/wav`,`audio/x-wav`,`audio/ogg`,`audio/webm`,`audio/flac`,`audio/x-flac`,`audio/aac`,`audio/mp4`]);var HTMLAudioElement=class{src=``;volume=1;loop=!1;paused=!0;currentTime=0;duration=0;readyState=0;_pipeline=null;canPlayType(e){let t=e.split(`;`)[0].trim().toLowerCase();return i.has(t)?`maybe`:``}play(){if(!this.src)return Promise.resolve();let n=t();return n===null?(this._cleanup(),this._pipeline=e.ElementFactory.make(`playbin`,`player`),this._pipeline?(r(this._pipeline),this._pipeline.set_property(`uri`,this.src),this._pipeline.set_property(`volume`,this.volume),this._pipeline.set_state(e.State.PLAYING),this.paused=!1,Promise.resolve()):Promise.resolve()):(console.warn(`[webaudio] Audio.play: GStreamer is unavailable, staying silent — ${n}`),Promise.resolve())}pause(){this._pipeline&&(this._pipeline.set_state(e.State.PAUSED),this.paused=!0)}load(){this._cleanup()}addEventListener(e,t){}removeEventListener(e,t){}_cleanup(){this._pipeline&&=(n(this._pipeline),null)}};export{HTMLAudioElement};
@@ -9,6 +9,27 @@ export declare class AudioContext {
9
9
  readonly destination: AudioDestinationNode;
10
10
  readonly listener: {};
11
11
  private _startTime;
12
+ /**
13
+ * CONSTRUCTING A CONTEXT IS NOT PLAYING AUDIO, so a missing audio backend
14
+ * may not be fatal here. `ensureGstInit()` threw, and it killed whole
15
+ * applications: on the batteries-included GTK runtime bundles (win32-x64,
16
+ * darwin) GStreamer is not shipped, and Excalibur's boot-time audio unlock
17
+ * constructs a context before the first frame — so `gjsify showcase
18
+ * excalibur-jelly-jumper --runtime node` on Windows died with "Failed to
19
+ * require Gst 1.0: Typelib file for namespace 'Gst' not found" and the game
20
+ * never rendered.
21
+ *
22
+ * In a browser `new AudioContext()` always succeeds; a page that cannot play
23
+ * sound still runs. This matches that. The rule was already written down one
24
+ * level below — `AudioBufferSourceNode.start()` degrades to silent rather
25
+ * than throw, "a single failed audio node must NEVER crash the whole app" —
26
+ * it just had not been applied to the constructor.
27
+ *
28
+ * Warned ONCE per context and not per sound: a game loading forty samples
29
+ * would otherwise bury its own output. Everything downstream already copes —
30
+ * `start()` degrades to silent and `decodeAudioData` rejects with an
31
+ * `EncodingError`, which is what the spec says a failed decode does.
32
+ */
12
33
  constructor();
13
34
  /** Monotonically increasing time in seconds since context creation. */
14
35
  get currentTime(): number;
@@ -1,3 +1,42 @@
1
1
  import Gst from 'gi://Gst?version=1.0';
2
+ /** The GStreamer bring-up. A parameter so its FAILURE branch is executable. */
3
+ export type GstInitializer = () => void;
4
+ /**
5
+ * Bring GStreamer up and REPORT the outcome instead of throwing.
6
+ *
7
+ * Returns `null` on success, or a human-readable reason. Every caller that can
8
+ * carry on without audio should use this one — see `AudioContext`.
9
+ *
10
+ * `initializer` is injectable for the same reason `dirLinkTarget` takes its link
11
+ * kind: the interesting branch here is the one where GStreamer is ABSENT, and on
12
+ * a developer's Linux box it never is. A branch nobody can execute is how the
13
+ * throwing version below shipped as the only entry point.
14
+ *
15
+ * The memo applies to the DEFAULT initializer only. A test passing its own is
16
+ * asking to exercise one specific outcome, and must neither be answered from the
17
+ * process-wide memo nor poison it.
18
+ */
19
+ export declare function tryEnsureGstInit(initializer?: GstInitializer): string | null;
20
+ /**
21
+ * TEST SEAM — answer the next bring-up with `reason` (or `null` for success)
22
+ * without touching the real GStreamer, and `undefined` to forget again.
23
+ *
24
+ * The injectable initializer above covers `tryEnsureGstInit` itself, but not its
25
+ * CALLERS: `new AudioContext()` has no initializer to hand it, and "constructing
26
+ * a context on a host with no GStreamer does not throw" is the exact property
27
+ * whose absence killed the game. Priming the memo is what lets that be asserted
28
+ * on a developer machine, where GStreamer is always installed.
29
+ */
30
+ export declare function primeGstOutcomeForTests(reason: string | null | undefined): void;
31
+ /**
32
+ * Bring GStreamer up, or throw.
33
+ *
34
+ * For the callers that genuinely cannot proceed without it — decoding bytes,
35
+ * building a playback pipeline. Constructing an `AudioContext` is NOT one of
36
+ * them, and treating it as one is what killed whole applications: on the
37
+ * batteries-included GTK runtime bundles (win32-x64, darwin) GStreamer is not
38
+ * shipped at all, so `new AudioContext()` threw "Failed to require Gst 1.0"
39
+ * during Excalibur's boot-time audio unlock and the game never rendered a frame.
40
+ */
2
41
  export declare function ensureGstInit(): void;
3
42
  export { Gst };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/webaudio",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "Web Audio API for GJS using GStreamer as audio backend",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -42,7 +42,7 @@
42
42
  "audio-api"
43
43
  ],
44
44
  "dependencies": {
45
- "@gjsify/dom-events": "^0.32.0"
45
+ "@gjsify/dom-events": "^0.34.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@girs/gio-2.0": "^4.1.0",
@@ -52,8 +52,8 @@
52
52
  "@girs/gstapp-1.0": "^4.1.0",
53
53
  "@girs/gstaudio-1.0": "^4.1.0",
54
54
  "@girs/gstbase-1.0": "^4.1.0",
55
- "@gjsify/cli": "^0.32.0",
56
- "@gjsify/unit": "^0.32.0",
55
+ "@gjsify/cli": "^0.34.0",
56
+ "@gjsify/unit": "^0.34.0",
57
57
  "@types/node": "^25.9.2",
58
58
  "typescript": "^6.0.3"
59
59
  },