@lucaismyname/ginger 0.0.37 → 0.0.38
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.
- package/README.md +2 -2
- package/dist/client.cjs +1 -1
- package/dist/client.js +2 -2
- package/dist/components/current/Artwork.d.ts.map +1 -1
- package/dist/components/current/Chapters.d.ts.map +1 -1
- package/dist/components/current/Lyrics.d.ts.map +1 -1
- package/dist/components/current/LyricsSynced.d.ts.map +1 -1
- package/dist/components/current/Playback.d.ts +1 -1
- package/dist/components/current/Playback.d.ts.map +1 -1
- package/dist/components/current/QueueMeta.d.ts +1 -1
- package/dist/components/current/QueueMeta.d.ts.map +1 -1
- package/dist/components/current/Time.d.ts +2 -2
- package/dist/components/current/Time.d.ts.map +1 -1
- package/dist/components/current/Year.d.ts +1 -1
- package/dist/components/current/Year.d.ts.map +1 -1
- package/dist/components/playlist/GingerPlaylist.d.ts.map +1 -1
- package/dist/components/queue/QueueDisplay.d.ts.map +1 -1
- package/dist/context/GingerProvider.d.ts.map +1 -1
- package/dist/equalizer/index.cjs +1 -1
- package/dist/equalizer/index.cjs.map +1 -1
- package/dist/equalizer/index.js +27 -22
- package/dist/equalizer/index.js.map +1 -1
- package/dist/equalizer/useGingerEqualizer.d.ts.map +1 -1
- package/dist/experimental-gapless/index.cjs +1 -1
- package/dist/experimental-gapless/index.cjs.map +1 -1
- package/dist/experimental-gapless/index.d.ts +12 -7
- package/dist/experimental-gapless/index.d.ts.map +1 -1
- package/dist/experimental-gapless/index.js +48 -12
- package/dist/experimental-gapless/index.js.map +1 -1
- package/dist/experimental-gapless/probeGaplessCapability.d.ts +33 -0
- package/dist/experimental-gapless/probeGaplessCapability.d.ts.map +1 -0
- package/dist/experimental-gapless/probeGaplessCapability.test.d.ts +2 -0
- package/dist/experimental-gapless/probeGaplessCapability.test.d.ts.map +1 -0
- package/dist/ginger-Ca8910_n.js +2050 -0
- package/dist/ginger-Ca8910_n.js.map +1 -0
- package/dist/ginger-DrD8F4HX.cjs +2 -0
- package/dist/ginger-DrD8F4HX.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/media/useMediaSession.d.ts +2 -2
- package/dist/media/useMediaSession.d.ts.map +1 -1
- package/dist/remote/useGingerRemote.test.d.ts.map +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/testing/helpers.d.ts +2 -0
- package/dist/testing/helpers.d.ts.map +1 -1
- package/dist/testing/index.cjs +2 -2
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.js +16 -10
- package/dist/testing/index.js.map +1 -1
- package/dist/types.d.ts +20 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/{useGingerChapterProgress-BW6v65wN.js → useGingerChapterProgress-Dbwiwnko.js} +2 -2
- package/dist/useGingerChapterProgress-Dbwiwnko.js.map +1 -0
- package/dist/{useGingerChapterProgress-B2-J3dSP.cjs → useGingerChapterProgress-TeWWJ8Fd.cjs} +2 -2
- package/dist/useGingerChapterProgress-TeWWJ8Fd.cjs.map +1 -0
- package/package.json +2 -1
- package/dist/ginger-DlCkxDXs.js +0 -2223
- package/dist/ginger-DlCkxDXs.js.map +0 -1
- package/dist/ginger-jToP7EpI.cjs +0 -2
- package/dist/ginger-jToP7EpI.cjs.map +0 -1
- package/dist/useGingerChapterProgress-B2-J3dSP.cjs.map +0 -1
- package/dist/useGingerChapterProgress-BW6v65wN.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @lucaismyname/ginger
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React audio primitives for music and podcast UIs on the native **`<audio>`** element: a reducer-backed provider, composable **`Ginger.*`** components, and **`useGinger()`** for full control. Many pieces ship sensible defaults (CSS variables, layout helpers); use **`unstyled`** (and related flags below) when you want **behavior and data only**, with your own styling.
|
|
4
4
|
|
|
5
5
|
**Peer dependencies:** **`react` ≥ 18** (required). **`react-dom` ≥ 18** is listed as a peer for typical DOM apps; it is **optional** in `peerDependenciesMeta`, so setups that do not use `react-dom` can omit it when appropriate.
|
|
6
6
|
|
|
@@ -512,7 +512,7 @@ Props:
|
|
|
512
512
|
| `initialPlaybackRate` | `number` | `1` | Initial playback rate, clamped `0.25..4` |
|
|
513
513
|
| `initialStateKey` | `string \| number` | `undefined` | Re-dispatches `INIT` when this key changes |
|
|
514
514
|
| `locale` | `Partial<GingerLocaleMessages>` | `undefined` | Override built-in strings (controls, chapter list, synced lyrics list, …) |
|
|
515
|
-
| `mediaSession` | `boolean` | `false` |
|
|
515
|
+
| `mediaSession` | `boolean \| GingerMediaSessionOptions` | `false` | `true` enables default Media Session bridge; pass `{ seekForwardSeconds, seekBackwardSeconds, positionState }` for optional OS skip controls and timeline sync |
|
|
516
516
|
| `beforePlay` | `() => boolean \| Promise<boolean>` | `undefined` | Policy hook run before playback starts |
|
|
517
517
|
| `onPlayBlocked` | `() => void` | `undefined` | Called when `beforePlay` returns `false` |
|
|
518
518
|
| `persistence` | `{ get(key): unknown; set(key, value): void }` | `undefined` | Adapter for persisted playback settings and resume state |
|
package/dist/client.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./ginger-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./ginger-DrD8F4HX.cjs"),s=require("./useGinger-BXgia32v.cjs"),r=require("./useGingerChapterProgress-TeWWJ8Fd.cjs"),i=require("./liveAudioGraph-0cpHD_Ic.cjs"),n=require("./selectors-YXnP8Y8g.cjs"),a=require("./GingerSplitContexts-C7puo0M7.cjs");exports.Chapters=e.Chapters;exports.Ginger=e.Ginger;exports.LyricsSynced=e.LyricsSynced;exports.Pause=e.Pause;exports.Play=e.Play;exports.RepeatGlyph=e.RepeatGlyph;exports.ShuffleIcon=e.ShuffleIcon;exports.SkipBack=e.SkipBack;exports.SkipForward=e.SkipForward;exports.Volume2=e.Volume2;exports.VolumeX=e.VolumeX;exports.Wrapper=e.Wrapper;exports.clampPlaybackRate=e.clampPlaybackRate;exports.clampVolume=e.clampVolume;exports.defaultGingerLocale=e.defaultGingerLocale;exports.parseLrc=e.parseLrc;exports.useGingerChapters=e.useGingerChapters;exports.useGingerLocale=e.useGingerLocale;exports.useGingerLyricsSync=e.useGingerLyricsSync;exports.usePlayPauseBinding=e.usePlayPauseBinding;exports.useSeekBarBinding=e.useSeekBarBinding;exports.useVolumeSlider=e.useVolumeSlider;exports.useGinger=s.useGinger;exports.createGingerStore=r.createGingerStore;exports.useGingerChapterProgress=r.useGingerChapterProgress;exports.useGingerDebugLog=r.useGingerDebugLog;exports.useGingerKeyboardShortcuts=r.useGingerKeyboardShortcuts;exports.useGingerLiveAnalyzer=r.useGingerLiveAnalyzer;exports.useGingerPlaybackHistory=r.useGingerPlaybackHistory;exports.useGingerSleepTimer=r.useGingerSleepTimer;exports.useGingerVolumeFade=r.useGingerVolumeFade;exports.useNextTrackPrefetch=r.useNextTrackPrefetch;exports.useSeekDrag=r.useSeekDrag;exports.attachLiveAnalyser=i.attachLiveAnalyser;exports.detachLiveAnalyser=i.detachLiveAnalyser;exports.setProcessingChain=i.setProcessingChain;exports.derivePlaybackUiState=n.derivePlaybackUiState;exports.gingerStateFromContextValues=a.gingerStateFromContextValues;exports.gingerStateFromContexts=a.gingerStateFromContexts;exports.useGingerMedia=a.useGingerMedia;exports.useGingerPlayback=a.useGingerPlayback;exports.useGingerState=a.useGingerState;
|
|
2
2
|
//# sourceMappingURL=client.cjs.map
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as s, G as r, L as i, P as t, a as u, R as n, S as o, b as g, c, V as l, d as p, W as G, e as m, f as y, g as S, p as d, u as f, h, i as P, j as k, k as L, l as b } from "./ginger-
|
|
1
|
+
import { C as s, G as r, L as i, P as t, a as u, R as n, S as o, b as g, c, V as l, d as p, W as G, e as m, f as y, g as S, p as d, u as f, h, i as P, j as k, k as L, l as b } from "./ginger-Ca8910_n.js";
|
|
2
2
|
import { u as C } from "./useGinger-hpp2pAGY.js";
|
|
3
|
-
import { c as v, u as B, a as F, b as A, d as R, e as D, f as T, g as W, h as j, i as w } from "./useGingerChapterProgress-
|
|
3
|
+
import { c as v, u as B, a as F, b as A, d as R, e as D, f as T, g as W, h as j, i as w } from "./useGingerChapterProgress-Dbwiwnko.js";
|
|
4
4
|
import { a as H, d as I, s as K } from "./liveAudioGraph-DvPaxBCP.js";
|
|
5
5
|
import { d as N } from "./selectors-BalBCc7X.js";
|
|
6
6
|
import { g as X, a as q, u as E, b as J, c as O } from "./GingerSplitContexts-BzBExb95.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Artwork.d.ts","sourceRoot":"","sources":["../../../src/components/current/Artwork.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAG9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG,gBAAgB,GACzC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC,GAAG;IACxF,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEJ,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,QAAgB,EAChB,QAAQ,GACT,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"Artwork.d.ts","sourceRoot":"","sources":["../../../src/components/current/Artwork.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,OAAO,CAAC;AAG9D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG,gBAAgB,GACzC,IAAI,CAAC,iBAAiB,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC,GAAG;IACxF,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEJ,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,QAAgB,EAChB,QAAQ,GACT,EAAE,YAAY,kDA6Cd;yBAxDe,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chapters.d.ts","sourceRoot":"","sources":["../../../src/components/current/Chapters.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,
|
|
1
|
+
{"version":3,"file":"Chapters.d.ts","sourceRoot":"","sources":["../../../src/components/current/Chapters.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,EAAE,KAAK,aAAa,EAAqB,MAAM,+BAA+B,CAAC;AAEtF,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IAC/D,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,QAAQ,CAAC,EAAE,CACT,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,WAAW,KACf,SAAS,CAAC;CAChB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAgB,EAChB,WAAwB,EACxB,QAAQ,GACT,EAAE,aAAa,kDAuEf;yBA/Ee,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Lyrics.d.ts","sourceRoot":"","sources":["../../../src/components/current/Lyrics.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG;IAC3C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;IAC5D,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,6HAA6H;IAC7H,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,kBAAyB,EACzB,QAAgB,GACjB,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"Lyrics.d.ts","sourceRoot":"","sources":["../../../src/components/current/Lyrics.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG;IAC3C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;IAC5D,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,6HAA6H;IAC7H,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAgB,MAAM,CAAC,EACrB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,kBAAyB,EACzB,QAAgB,GACjB,EAAE,WAAW,kDAyBb;yBAjCe,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LyricsSynced.d.ts","sourceRoot":"","sources":["../../../src/components/current/LyricsSynced.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAItD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,CACT,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,WAAW,KACf,SAAS,CAAC;CAChB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAgB,EAChB,eAAe,EACf,aAAa,EACb,QAAQ,GACT,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"LyricsSynced.d.ts","sourceRoot":"","sources":["../../../src/components/current/LyricsSynced.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAItD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,UAAU,CAAC,GAAG;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,CACT,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,WAAW,KACf,SAAS,CAAC;CAChB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAgB,EAChB,eAAe,EACf,aAAa,EACb,QAAQ,GACT,EAAE,iBAAiB,kDA0DnB;yBAnEe,YAAY"}
|
|
@@ -3,7 +3,7 @@ import { DisplayBaseProps, GingerState, PlaybackUiState } from '../../types';
|
|
|
3
3
|
export type PlaybackStateProps = DisplayBaseProps & {
|
|
4
4
|
children?: (value: PlaybackUiState, state: GingerState) => ReactNode;
|
|
5
5
|
};
|
|
6
|
-
export declare function PlaybackState({ className, style, fallback, empty, children
|
|
6
|
+
export declare function PlaybackState({ className, style, fallback, empty, children }: PlaybackStateProps): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export declare namespace PlaybackState {
|
|
8
8
|
var displayName: string;
|
|
9
9
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Playback.d.ts","sourceRoot":"","sources":["../../../src/components/current/Playback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"Playback.d.ts","sourceRoot":"","sources":["../../../src/components/current/Playback.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAElF,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,GAAG;IAClD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;CACtE,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,kBAAkB,2CAchG;yBAde,aAAa;;;AAkB7B,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG;IACjD,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,KAAK,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;CAC7D,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,IAAe,EACf,QAAQ,GACT,EAAE,iBAAiB,kDA4BnB;yBAnCe,YAAY"}
|
|
@@ -11,7 +11,7 @@ export declare namespace QueueIndex {
|
|
|
11
11
|
export type QueueLengthProps = DisplayBaseProps & {
|
|
12
12
|
children?: (value: string, state: GingerState) => ReactNode;
|
|
13
13
|
};
|
|
14
|
-
export declare function QueueLength({ className, style, fallback, empty, children
|
|
14
|
+
export declare function QueueLength({ className, style, fallback, empty, children }: QueueLengthProps): import("react/jsx-runtime").JSX.Element | null;
|
|
15
15
|
export declare namespace QueueLength {
|
|
16
16
|
var displayName: string;
|
|
17
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueMeta.d.ts","sourceRoot":"","sources":["../../../src/components/current/QueueMeta.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG;IAC/C,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;CAC7D,CAAC;AAEF,wBAAgB,UAAU,CAAC,EACzB,IAAQ,EACR,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"QueueMeta.d.ts","sourceRoot":"","sources":["../../../src/components/current/QueueMeta.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG;IAC/C,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;CAC7D,CAAC;AAEF,wBAAgB,UAAU,CAAC,EACzB,IAAQ,EACR,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,kDAuBjB;yBA9Be,UAAU;;;AAkC1B,MAAM,MAAM,gBAAgB,GAAG,gBAAgB,GAAG;IAChD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;CAC7D,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,gBAAgB,kDAsB5F;yBAtBe,WAAW;;;AA0B3B,MAAM,MAAM,kBAAkB,GAAG,gBAAgB,GAAG;IAClD,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EACvD,KAAK,EAAE,WAAW,KACf,SAAS,CAAC;CAChB,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAC5B,IAAQ,EACR,SAAiB,EACjB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAQ,GACT,EAAE,kBAAkB,kDAyBpB;yBAjCe,aAAa"}
|
|
@@ -23,7 +23,7 @@ export type ProgressProps = DisplayBaseProps & {
|
|
|
23
23
|
duration: number;
|
|
24
24
|
}, state: GingerState) => ReactNode;
|
|
25
25
|
};
|
|
26
|
-
export declare function Progress({ className, style, fallback, empty, children
|
|
26
|
+
export declare function Progress({ className, style, fallback, empty, children }: ProgressProps): import("react/jsx-runtime").JSX.Element | null;
|
|
27
27
|
export declare namespace Progress {
|
|
28
28
|
var displayName: string;
|
|
29
29
|
}
|
|
@@ -45,7 +45,7 @@ export type BufferRailProps = DisplayBaseProps & {
|
|
|
45
45
|
unstyled?: boolean;
|
|
46
46
|
};
|
|
47
47
|
/** Buffered portion of the timeline (0…`bufferedFraction`); pair with `TimeRail` or use alone. */
|
|
48
|
-
export declare function BufferRail({ className, style, height, unstyled
|
|
48
|
+
export declare function BufferRail({ className, style, height, unstyled }: BufferRailProps): import("react/jsx-runtime").JSX.Element;
|
|
49
49
|
export declare namespace BufferRail {
|
|
50
50
|
var displayName: string;
|
|
51
51
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Time.d.ts","sourceRoot":"","sources":["../../../src/components/current/Time.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Time.d.ts","sourceRoot":"","sources":["../../../src/components/current/Time.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG;IAC7C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACrC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,KAAK,SAAS,CAAC;CAC7D,CAAC;AA8BF,wBAAgB,OAAO,CAAC,KAAK,EAAE,aAAa,iFAG3C;yBAHe,OAAO;;;AAOvB,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,iFAG5C;yBAHe,QAAQ;;;AAOxB,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,iFAG7C;yBAHe,SAAS;;;AAOzB,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAClE,KAAK,EAAE,WAAW,KACf,SAAS,CAAC;CAChB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,aAAa,kDAuBtF;yBAvBe,QAAQ;;;AA2BxB,MAAM,MAAM,aAAa,GAAG,gBAAgB,GAAG;IAC7C,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sEAAsE;IACtE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,KAAK,EACL,MAAU,EACV,YAAoB,EACpB,QAAgB,GACjB,EAAE,aAAa,2CAiDf;yBAvDe,QAAQ;;;AA2DxB,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,kGAAkG;AAClG,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,MAAU,EAAE,QAAgB,EAAE,EAAE,eAAe,2CAiC7F;yBAjCe,UAAU"}
|
|
@@ -2,7 +2,7 @@ import { TextDisplayProps } from './createTextDisplay';
|
|
|
2
2
|
export type YearProps = TextDisplayProps & {
|
|
3
3
|
format?: (year: number) => string;
|
|
4
4
|
};
|
|
5
|
-
export declare function Year({ className, style, fallback, empty, children, format
|
|
5
|
+
export declare function Year({ className, style, fallback, empty, children, format }: YearProps): import("react/jsx-runtime").JSX.Element | null;
|
|
6
6
|
export declare namespace Year {
|
|
7
7
|
var displayName: string;
|
|
8
8
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Year.d.ts","sourceRoot":"","sources":["../../../src/components/current/Year.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG;IACzC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC,CAAC;AAEF,wBAAgB,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"Year.d.ts","sourceRoot":"","sources":["../../../src/components/current/Year.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAE5D,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG;IACzC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC,CAAC;AAEF,wBAAgB,IAAI,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,SAAS,kDAuBtF;yBAvBe,IAAI"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GingerPlaylist.d.ts","sourceRoot":"","sources":["../../../src/components/playlist/GingerPlaylist.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;
|
|
1
|
+
{"version":3,"file":"GingerPlaylist.d.ts","sourceRoot":"","sources":["../../../src/components/playlist/GingerPlaylist.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EAGf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,MAAM,oBAAoB,GAAG;IACjC,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAYF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,GAAG;IACrF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,SAAS,CAAC;IAC5E,iFAAiF;IACjF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAgB,EAChB,QAAQ,EACR,WAAW,EACX,YAAmB,EACnB,KAAK,EACL,GAAG,IAAI,EACR,EAAE,mBAAmB,2CAuErB;yBA/Ee,cAAc;;;AAmF9B,MAAM,MAAM,wBAAwB,GAAG,IAAI,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC,GAAG;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC;CAC3C,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAgB,EAChB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,OAAO,EACP,OAAO,EACP,GAAG,UAAU,EACd,EAAE,wBAAwB,2CAgD1B;yBAzDe,mBAAmB;;;AA6DnC,eAAO,MAAM,sBAAsB;;CAEjC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QueueDisplay.d.ts","sourceRoot":"","sources":["../../../src/components/queue/QueueDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,eAAO,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"QueueDisplay.d.ts","sourceRoot":"","sources":["../../../src/components/queue/QueueDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,eAAO,MAAM,KAAK,yGAAwE,CAAC;AAC3F,eAAO,MAAM,QAAQ,yGAA8E,CAAC;AACpG,eAAO,MAAM,WAAW,yGAGvB,CAAC;AACF,eAAO,MAAM,SAAS,yGAGrB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,GAAG;IACjD,mEAAmE;IACnE,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,KAAK,EACL,QAAQ,EACR,KAAK,EACL,QAAgB,EAChB,QAAQ,GACT,EAAE,iBAAiB,kDAyCnB;yBAhDe,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GingerProvider.d.ts","sourceRoot":"","sources":["../../src/context/GingerProvider.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAEV,mBAAmB,EAKpB,MAAM,UAAU,CAAC;AAuBlB,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,aAAkB,EAClB,YAAgB,EAChB,mBAA0B,EAC1B,cAAsB,EACtB,iBAAyB,EACzB,mBAAgC,EAChC,aAAoB,EACpB,aAAiB,EACjB,YAAoB,EACpB,mBAAuB,EACvB,eAAe,EACf,MAAM,EACN,YAAoB,EACpB,UAAU,EACV,aAAa,EACb,WAAW,EACX,cAAsB,EACtB,mBAA2B,EAC3B,QAAgB,EAChB,OAAe,EACf,SAAS,EACT,KAAK,EACL,GAAG,EAAE,OAAO,EACZ,2BAA+B,EAC/B,aAAa,EACb,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,EACP,cAAc,EACd,oBAAoB,EACpB,MAAM,GACP,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"GingerProvider.d.ts","sourceRoot":"","sources":["../../src/context/GingerProvider.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAEV,mBAAmB,EAKpB,MAAM,UAAU,CAAC;AAuBlB,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,aAAkB,EAClB,YAAgB,EAChB,mBAA0B,EAC1B,cAAsB,EACtB,iBAAyB,EACzB,mBAAgC,EAChC,aAAoB,EACpB,aAAiB,EACjB,YAAoB,EACpB,mBAAuB,EACvB,eAAe,EACf,MAAM,EACN,YAAoB,EACpB,UAAU,EACV,aAAa,EACb,WAAW,EACX,cAAsB,EACtB,mBAA2B,EAC3B,QAAgB,EAChB,OAAe,EACf,SAAS,EACT,KAAK,EACL,GAAG,EAAE,OAAO,EACZ,2BAA+B,EAC/B,aAAa,EACb,MAAM,EACN,OAAO,EACP,UAAU,EACV,OAAO,EACP,cAAc,EACd,oBAAoB,EACpB,MAAM,GACP,EAAE,mBAAmB,2CA2mBrB"}
|
package/dist/equalizer/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react"),a=require("../liveAudioGraph-0cpHD_Ic.cjs"),G=require("../useGinger-BXgia32v.cjs"),R=[{frequency:60},{frequency:250},{frequency:1e3},{frequency:4e3},{frequency:16e3}];function b(p={}){const{enabled:y=!0,bands:h=R}=p,{audioRef:l,state:v}=G.useGinger(),[s,g]=t.useState(h),[S,q]=t.useState(null),c=t.useRef([]),m=t.useRef(s);m.current=s;const B=t.useMemo(()=>s.map(e=>`${e.frequency}:${e.type??"peaking"}:${e.q??1}`).join("|"),[s]);t.useEffect(()=>{const e=l.current;if(!(!e||typeof window>"u")){if(!y){a.setProcessingChain(e,[]),c.current=[];return}try{const n=a.attachLiveAnalyser(e,{fftSize:32,smoothingTimeConstant:0,minDecibels:-100,maxDecibels:0}),{context:r,id:f}=n,i=m.current.map(o=>{const u=r.createBiquadFilter();return u.type=o.type??"peaking",u.frequency.value=o.frequency,u.gain.value=o.gain??0,u.Q.value=o.q??1,u});c.current=i,a.setProcessingChain(e,i),a.detachLiveAnalyser(e,f),q(null)}catch(n){const r=n instanceof Error?n.message:"Failed to create equalizer";q(r),c.current=[]}return()=>{const n=l.current;n&&a.setProcessingChain(n,[]),c.current=[]}}},[y,B,l,v.currentIndex]);const C=t.useCallback((e,n)=>{const r=c.current[e];r&&(r.gain.value=n),g(f=>f.map((d,i)=>i===e?{...d,gain:n}:d))},[]),E=t.useCallback(e=>{g(e)},[]);return{setBandGain:C,setBands:E,bands:s,error:S}}exports.useGingerEqualizer=b;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/equalizer/useGingerEqualizer.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n attachLiveAnalyser,\n detachLiveAnalyser,\n setProcessingChain,\n} from \"../analyzer/liveAudioGraph\";\nimport { useGinger } from \"../hooks/useGinger\";\n\nexport type EqualizerBand = {\n /** Center frequency in Hz (e.g. 60, 250, 1000, 4000, 16000). */\n frequency: number;\n /** Gain in dB. Positive = boost, negative = cut. Typical range: -12 to +12. Default: 0. */\n gain?: number;\n /** Q factor (bandwidth). Higher = narrower. Default: 1. */\n q?: number;\n /** BiquadFilterNode type. Default: \"peaking\". */\n type?: BiquadFilterType;\n};\n\nexport type UseGingerEqualizerOptions = {\n /** When false, EQ nodes are detached and the audio path is bypassed. Default: true. */\n enabled?: boolean;\n /** Band definitions. Each band maps to one BiquadFilterNode. */\n bands?: EqualizerBand[];\n};\n\nexport type UseGingerEqualizerResult = {\n /** Update a single band's gain in dB without rebuilding the filter chain. */\n setBandGain: (bandIndex: number, gainDb: number) => void;\n /** Replace all bands (rebuilds the filter chain). */\n setBands: (bands: EqualizerBand[]) => void;\n /** Current band definitions (reflects the latest `setBands` call). */\n bands: EqualizerBand[];\n /** Error string if Web Audio is unavailable or filter creation failed. */\n error: string | null;\n};\n\nconst DEFAULT_BANDS: EqualizerBand[] = [\n { frequency: 60 },\n { frequency: 250 },\n { frequency: 1000 },\n { frequency: 4000 },\n { frequency: 16000 },\n];\n\n/**\n * Inserts a parametric EQ into the Web Audio graph for the active Ginger media element.\n *\n * Each band is a `BiquadFilterNode` connected in series between the audio source and the\n * speakers. If `useGingerLiveAnalyzer` is also active, the EQ is inserted before the analyser\n * — both share the same `AudioContext` (the browser allows only one `MediaElementAudioSourceNode`\n * per element).\n *\n * Available as a subpath export:\n * ```ts\n * import { useGingerEqualizer } from \"@lucaismyname/ginger/equalizer\";\n * ```\n */\nexport function useGingerEqualizer(\n options: UseGingerEqualizerOptions = {},\n): UseGingerEqualizerResult {\n const { enabled = true, bands: initialBands = DEFAULT_BANDS } = options;\n const { audioRef, state } = useGinger();\n\n const [bands, setBandsState] = useState<EqualizerBand[]>(initialBands);\n const [error, setError] = useState<string | null>(null);\n\n const filterNodesRef = useRef<BiquadFilterNode[]>([]);\n\n useEffect(() => {\n const el = audioRef.current;\n if (!el || typeof window === \"undefined\") {\n return;\n }\n\n if (!enabled) {\n setProcessingChain(el, []);\n filterNodesRef.current = [];\n return;\n }\n\n try {\n // Attach a temporary analyser solely to get (or create) the shared AudioContext.\n // The graph is rebuilt by liveAudioGraph after we call setProcessingChain, so this\n // temporary consumer is detached immediately after we have the context reference.\n const attached = attachLiveAnalyser(el, {\n fftSize: 32,\n smoothingTimeConstant: 0,\n minDecibels: -100,\n maxDecibels: 0,\n });\n const { context, id: tempId } = attached;\n\n const filters: BiquadFilterNode[] =
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/equalizer/useGingerEqualizer.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n attachLiveAnalyser,\n detachLiveAnalyser,\n setProcessingChain,\n} from \"../analyzer/liveAudioGraph\";\nimport { useGinger } from \"../hooks/useGinger\";\n\nexport type EqualizerBand = {\n /** Center frequency in Hz (e.g. 60, 250, 1000, 4000, 16000). */\n frequency: number;\n /** Gain in dB. Positive = boost, negative = cut. Typical range: -12 to +12. Default: 0. */\n gain?: number;\n /** Q factor (bandwidth). Higher = narrower. Default: 1. */\n q?: number;\n /** BiquadFilterNode type. Default: \"peaking\". */\n type?: BiquadFilterType;\n};\n\nexport type UseGingerEqualizerOptions = {\n /** When false, EQ nodes are detached and the audio path is bypassed. Default: true. */\n enabled?: boolean;\n /** Band definitions. Each band maps to one BiquadFilterNode. */\n bands?: EqualizerBand[];\n};\n\nexport type UseGingerEqualizerResult = {\n /** Update a single band's gain in dB without rebuilding the filter chain. */\n setBandGain: (bandIndex: number, gainDb: number) => void;\n /** Replace all bands (rebuilds the filter chain). */\n setBands: (bands: EqualizerBand[]) => void;\n /** Current band definitions (reflects the latest `setBands` call). */\n bands: EqualizerBand[];\n /** Error string if Web Audio is unavailable or filter creation failed. */\n error: string | null;\n};\n\nconst DEFAULT_BANDS: EqualizerBand[] = [\n { frequency: 60 },\n { frequency: 250 },\n { frequency: 1000 },\n { frequency: 4000 },\n { frequency: 16000 },\n];\n\n/**\n * Inserts a parametric EQ into the Web Audio graph for the active Ginger media element.\n *\n * Each band is a `BiquadFilterNode` connected in series between the audio source and the\n * speakers. If `useGingerLiveAnalyzer` is also active, the EQ is inserted before the analyser\n * — both share the same `AudioContext` (the browser allows only one `MediaElementAudioSourceNode`\n * per element).\n *\n * Available as a subpath export:\n * ```ts\n * import { useGingerEqualizer } from \"@lucaismyname/ginger/equalizer\";\n * ```\n */\nexport function useGingerEqualizer(\n options: UseGingerEqualizerOptions = {},\n): UseGingerEqualizerResult {\n const { enabled = true, bands: initialBands = DEFAULT_BANDS } = options;\n const { audioRef, state } = useGinger();\n\n const [bands, setBandsState] = useState<EqualizerBand[]>(initialBands);\n const [error, setError] = useState<string | null>(null);\n\n const filterNodesRef = useRef<BiquadFilterNode[]>([]);\n const bandsRef = useRef(bands);\n bandsRef.current = bands;\n\n /** Rebuild the chain when band frequencies/types change — not on every gain tweak (see setBandGain). */\n const bandStructureKey = useMemo(\n () => bands.map((b) => `${b.frequency}:${b.type ?? \"peaking\"}:${b.q ?? 1}`).join(\"|\"),\n [bands],\n );\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: bandStructureKey and currentIndex must re-run graph setup; bands read via bandsRef\n useEffect(() => {\n const el = audioRef.current;\n if (!el || typeof window === \"undefined\") {\n return;\n }\n\n if (!enabled) {\n setProcessingChain(el, []);\n filterNodesRef.current = [];\n return;\n }\n\n try {\n // Attach a temporary analyser solely to get (or create) the shared AudioContext.\n // The graph is rebuilt by liveAudioGraph after we call setProcessingChain, so this\n // temporary consumer is detached immediately after we have the context reference.\n const attached = attachLiveAnalyser(el, {\n fftSize: 32,\n smoothingTimeConstant: 0,\n minDecibels: -100,\n maxDecibels: 0,\n });\n const { context, id: tempId } = attached;\n\n const bandsNow = bandsRef.current;\n const filters: BiquadFilterNode[] = bandsNow.map((band) => {\n const node = context.createBiquadFilter();\n node.type = band.type ?? \"peaking\";\n node.frequency.value = band.frequency;\n node.gain.value = band.gain ?? 0;\n node.Q.value = band.q ?? 1;\n return node;\n });\n\n filterNodesRef.current = filters;\n\n // Install processing chain BEFORE removing the temp analyser so the graph stays valid\n setProcessingChain(el, filters);\n detachLiveAnalyser(el, tempId);\n\n setError(null);\n } catch (e) {\n const msg = e instanceof Error ? e.message : \"Failed to create equalizer\";\n setError(msg);\n filterNodesRef.current = [];\n }\n\n return () => {\n const element = audioRef.current;\n if (element) {\n setProcessingChain(element, []);\n }\n filterNodesRef.current = [];\n };\n }, [enabled, bandStructureKey, audioRef, state.currentIndex]);\n\n const setBandGain = useCallback((bandIndex: number, gainDb: number) => {\n const node = filterNodesRef.current[bandIndex];\n if (node) {\n node.gain.value = gainDb;\n }\n setBandsState((prev) => prev.map((b, i) => (i === bandIndex ? { ...b, gain: gainDb } : b)));\n }, []);\n\n const setBands = useCallback((nextBands: EqualizerBand[]) => {\n setBandsState(nextBands);\n }, []);\n\n return { setBandGain, setBands, bands, error };\n}\n"],"names":["DEFAULT_BANDS","useGingerEqualizer","options","enabled","initialBands","audioRef","state","useGinger","bands","setBandsState","useState","error","setError","filterNodesRef","useRef","bandsRef","bandStructureKey","useMemo","b","useEffect","el","setProcessingChain","attached","attachLiveAnalyser","context","tempId","filters","band","node","detachLiveAnalyser","e","msg","element","setBandGain","useCallback","bandIndex","gainDb","prev","setBands","nextBands"],"mappings":"4LAqCMA,EAAiC,CACrC,CAAE,UAAW,EAAA,EACb,CAAE,UAAW,GAAA,EACb,CAAE,UAAW,GAAA,EACb,CAAE,UAAW,GAAA,EACb,CAAE,UAAW,IAAA,CACf,EAeO,SAASC,EACdC,EAAqC,GACX,CAC1B,KAAM,CAAE,QAAAC,EAAU,GAAM,MAAOC,EAAeJ,GAAkBE,EAC1D,CAAE,SAAAG,EAAU,MAAAC,CAAA,EAAUC,YAAA,EAEtB,CAACC,EAAOC,CAAa,EAAIC,EAAAA,SAA0BN,CAAY,EAC/D,CAACO,EAAOC,CAAQ,EAAIF,EAAAA,SAAwB,IAAI,EAEhDG,EAAiBC,EAAAA,OAA2B,EAAE,EAC9CC,EAAWD,EAAAA,OAAON,CAAK,EAC7BO,EAAS,QAAUP,EAGnB,MAAMQ,EAAmBC,EAAAA,QACvB,IAAMT,EAAM,IAAKU,GAAM,GAAGA,EAAE,SAAS,IAAIA,EAAE,MAAQ,SAAS,IAAIA,EAAE,GAAK,CAAC,EAAE,EAAE,KAAK,GAAG,EACpF,CAACV,CAAK,CAAA,EAIRW,EAAAA,UAAU,IAAM,CACd,MAAMC,EAAKf,EAAS,QACpB,GAAI,GAACe,GAAM,OAAO,OAAW,KAI7B,IAAI,CAACjB,EAAS,CACZkB,EAAAA,mBAAmBD,EAAI,EAAE,EACzBP,EAAe,QAAU,CAAA,EACzB,MACF,CAEA,GAAI,CAIF,MAAMS,EAAWC,EAAAA,mBAAmBH,EAAI,CACtC,QAAS,GACT,sBAAuB,EACvB,YAAa,KACb,YAAa,CAAA,CACd,EACK,CAAE,QAAAI,EAAS,GAAIC,CAAA,EAAWH,EAG1BI,EADWX,EAAS,QACmB,IAAKY,GAAS,CACzD,MAAMC,EAAOJ,EAAQ,mBAAA,EACrB,OAAAI,EAAK,KAAOD,EAAK,MAAQ,UACzBC,EAAK,UAAU,MAAQD,EAAK,UAC5BC,EAAK,KAAK,MAAQD,EAAK,MAAQ,EAC/BC,EAAK,EAAE,MAAQD,EAAK,GAAK,EAClBC,CACT,CAAC,EAEDf,EAAe,QAAUa,EAGzBL,EAAAA,mBAAmBD,EAAIM,CAAO,EAC9BG,EAAAA,mBAAmBT,EAAIK,CAAM,EAE7Bb,EAAS,IAAI,CACf,OAASkB,EAAG,CACV,MAAMC,EAAMD,aAAa,MAAQA,EAAE,QAAU,6BAC7ClB,EAASmB,CAAG,EACZlB,EAAe,QAAU,CAAA,CAC3B,CAEA,MAAO,IAAM,CACX,MAAMmB,EAAU3B,EAAS,QACrB2B,GACFX,EAAAA,mBAAmBW,EAAS,EAAE,EAEhCnB,EAAe,QAAU,CAAA,CAC3B,EACF,EAAG,CAACV,EAASa,EAAkBX,EAAUC,EAAM,YAAY,CAAC,EAE5D,MAAM2B,EAAcC,EAAAA,YAAY,CAACC,EAAmBC,IAAmB,CACrE,MAAMR,EAAOf,EAAe,QAAQsB,CAAS,EACzCP,IACFA,EAAK,KAAK,MAAQQ,GAEpB3B,EAAe4B,GAASA,EAAK,IAAI,CAACnB,EAAG,IAAO,IAAMiB,EAAY,CAAE,GAAGjB,EAAG,KAAMkB,CAAA,EAAWlB,CAAE,CAAC,CAC5F,EAAG,CAAA,CAAE,EAECoB,EAAWJ,cAAaK,GAA+B,CAC3D9B,EAAc8B,CAAS,CACzB,EAAG,CAAA,CAAE,EAEL,MAAO,CAAE,YAAAN,EAAa,SAAAK,EAAU,MAAA9B,EAAO,MAAAG,CAAA,CACzC"}
|
package/dist/equalizer/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useState as
|
|
2
|
-
import { s as
|
|
1
|
+
import { useState as q, useRef as g, useMemo as R, useEffect as k, useCallback as h } from "react";
|
|
2
|
+
import { s as l, a as x, d as z } from "../liveAudioGraph-DvPaxBCP.js";
|
|
3
3
|
import { u as C } from "../useGinger-hpp2pAGY.js";
|
|
4
4
|
const D = [
|
|
5
5
|
{ frequency: 60 },
|
|
@@ -8,13 +8,18 @@ const D = [
|
|
|
8
8
|
{ frequency: 4e3 },
|
|
9
9
|
{ frequency: 16e3 }
|
|
10
10
|
];
|
|
11
|
-
function
|
|
12
|
-
const { enabled:
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
function $(v = {}) {
|
|
12
|
+
const { enabled: d = !0, bands: B = D } = v, { audioRef: u, state: E } = C(), [r, m] = q(B), [S, y] = q(null), s = g([]), p = g(r);
|
|
13
|
+
p.current = r;
|
|
14
|
+
const w = R(
|
|
15
|
+
() => r.map((e) => `${e.frequency}:${e.type ?? "peaking"}:${e.q ?? 1}`).join("|"),
|
|
16
|
+
[r]
|
|
17
|
+
);
|
|
18
|
+
k(() => {
|
|
19
|
+
const e = u.current;
|
|
15
20
|
if (!(!e || typeof window > "u")) {
|
|
16
|
-
if (!
|
|
17
|
-
|
|
21
|
+
if (!d) {
|
|
22
|
+
l(e, []), s.current = [];
|
|
18
23
|
return;
|
|
19
24
|
}
|
|
20
25
|
try {
|
|
@@ -23,30 +28,30 @@ function R(q = {}) {
|
|
|
23
28
|
smoothingTimeConstant: 0,
|
|
24
29
|
minDecibels: -100,
|
|
25
30
|
maxDecibels: 0
|
|
26
|
-
}), { context: n, id:
|
|
27
|
-
const
|
|
28
|
-
return
|
|
31
|
+
}), { context: n, id: i } = t, a = p.current.map((o) => {
|
|
32
|
+
const c = n.createBiquadFilter();
|
|
33
|
+
return c.type = o.type ?? "peaking", c.frequency.value = o.frequency, c.gain.value = o.gain ?? 0, c.Q.value = o.q ?? 1, c;
|
|
29
34
|
});
|
|
30
|
-
s.current =
|
|
35
|
+
s.current = a, l(e, a), z(e, i), y(null);
|
|
31
36
|
} catch (t) {
|
|
32
37
|
const n = t instanceof Error ? t.message : "Failed to create equalizer";
|
|
33
|
-
|
|
38
|
+
y(n), s.current = [];
|
|
34
39
|
}
|
|
35
40
|
return () => {
|
|
36
|
-
const t =
|
|
37
|
-
t &&
|
|
41
|
+
const t = u.current;
|
|
42
|
+
t && l(t, []), s.current = [];
|
|
38
43
|
};
|
|
39
44
|
}
|
|
40
|
-
}, [
|
|
41
|
-
const
|
|
45
|
+
}, [d, w, u, E.currentIndex]);
|
|
46
|
+
const A = h((e, t) => {
|
|
42
47
|
const n = s.current[e];
|
|
43
|
-
n && (n.gain.value = t),
|
|
44
|
-
}, []),
|
|
45
|
-
|
|
48
|
+
n && (n.gain.value = t), m((i) => i.map((f, a) => a === e ? { ...f, gain: t } : f));
|
|
49
|
+
}, []), N = h((e) => {
|
|
50
|
+
m(e);
|
|
46
51
|
}, []);
|
|
47
|
-
return { setBandGain:
|
|
52
|
+
return { setBandGain: A, setBands: N, bands: r, error: S };
|
|
48
53
|
}
|
|
49
54
|
export {
|
|
50
|
-
|
|
55
|
+
$ as useGingerEqualizer
|
|
51
56
|
};
|
|
52
57
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/equalizer/useGingerEqualizer.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport {\n attachLiveAnalyser,\n detachLiveAnalyser,\n setProcessingChain,\n} from \"../analyzer/liveAudioGraph\";\nimport { useGinger } from \"../hooks/useGinger\";\n\nexport type EqualizerBand = {\n /** Center frequency in Hz (e.g. 60, 250, 1000, 4000, 16000). */\n frequency: number;\n /** Gain in dB. Positive = boost, negative = cut. Typical range: -12 to +12. Default: 0. */\n gain?: number;\n /** Q factor (bandwidth). Higher = narrower. Default: 1. */\n q?: number;\n /** BiquadFilterNode type. Default: \"peaking\". */\n type?: BiquadFilterType;\n};\n\nexport type UseGingerEqualizerOptions = {\n /** When false, EQ nodes are detached and the audio path is bypassed. Default: true. */\n enabled?: boolean;\n /** Band definitions. Each band maps to one BiquadFilterNode. */\n bands?: EqualizerBand[];\n};\n\nexport type UseGingerEqualizerResult = {\n /** Update a single band's gain in dB without rebuilding the filter chain. */\n setBandGain: (bandIndex: number, gainDb: number) => void;\n /** Replace all bands (rebuilds the filter chain). */\n setBands: (bands: EqualizerBand[]) => void;\n /** Current band definitions (reflects the latest `setBands` call). */\n bands: EqualizerBand[];\n /** Error string if Web Audio is unavailable or filter creation failed. */\n error: string | null;\n};\n\nconst DEFAULT_BANDS: EqualizerBand[] = [\n { frequency: 60 },\n { frequency: 250 },\n { frequency: 1000 },\n { frequency: 4000 },\n { frequency: 16000 },\n];\n\n/**\n * Inserts a parametric EQ into the Web Audio graph for the active Ginger media element.\n *\n * Each band is a `BiquadFilterNode` connected in series between the audio source and the\n * speakers. If `useGingerLiveAnalyzer` is also active, the EQ is inserted before the analyser\n * — both share the same `AudioContext` (the browser allows only one `MediaElementAudioSourceNode`\n * per element).\n *\n * Available as a subpath export:\n * ```ts\n * import { useGingerEqualizer } from \"@lucaismyname/ginger/equalizer\";\n * ```\n */\nexport function useGingerEqualizer(\n options: UseGingerEqualizerOptions = {},\n): UseGingerEqualizerResult {\n const { enabled = true, bands: initialBands = DEFAULT_BANDS } = options;\n const { audioRef, state } = useGinger();\n\n const [bands, setBandsState] = useState<EqualizerBand[]>(initialBands);\n const [error, setError] = useState<string | null>(null);\n\n const filterNodesRef = useRef<BiquadFilterNode[]>([]);\n\n useEffect(() => {\n const el = audioRef.current;\n if (!el || typeof window === \"undefined\") {\n return;\n }\n\n if (!enabled) {\n setProcessingChain(el, []);\n filterNodesRef.current = [];\n return;\n }\n\n try {\n // Attach a temporary analyser solely to get (or create) the shared AudioContext.\n // The graph is rebuilt by liveAudioGraph after we call setProcessingChain, so this\n // temporary consumer is detached immediately after we have the context reference.\n const attached = attachLiveAnalyser(el, {\n fftSize: 32,\n smoothingTimeConstant: 0,\n minDecibels: -100,\n maxDecibels: 0,\n });\n const { context, id: tempId } = attached;\n\n const filters: BiquadFilterNode[] =
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/equalizer/useGingerEqualizer.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport {\n attachLiveAnalyser,\n detachLiveAnalyser,\n setProcessingChain,\n} from \"../analyzer/liveAudioGraph\";\nimport { useGinger } from \"../hooks/useGinger\";\n\nexport type EqualizerBand = {\n /** Center frequency in Hz (e.g. 60, 250, 1000, 4000, 16000). */\n frequency: number;\n /** Gain in dB. Positive = boost, negative = cut. Typical range: -12 to +12. Default: 0. */\n gain?: number;\n /** Q factor (bandwidth). Higher = narrower. Default: 1. */\n q?: number;\n /** BiquadFilterNode type. Default: \"peaking\". */\n type?: BiquadFilterType;\n};\n\nexport type UseGingerEqualizerOptions = {\n /** When false, EQ nodes are detached and the audio path is bypassed. Default: true. */\n enabled?: boolean;\n /** Band definitions. Each band maps to one BiquadFilterNode. */\n bands?: EqualizerBand[];\n};\n\nexport type UseGingerEqualizerResult = {\n /** Update a single band's gain in dB without rebuilding the filter chain. */\n setBandGain: (bandIndex: number, gainDb: number) => void;\n /** Replace all bands (rebuilds the filter chain). */\n setBands: (bands: EqualizerBand[]) => void;\n /** Current band definitions (reflects the latest `setBands` call). */\n bands: EqualizerBand[];\n /** Error string if Web Audio is unavailable or filter creation failed. */\n error: string | null;\n};\n\nconst DEFAULT_BANDS: EqualizerBand[] = [\n { frequency: 60 },\n { frequency: 250 },\n { frequency: 1000 },\n { frequency: 4000 },\n { frequency: 16000 },\n];\n\n/**\n * Inserts a parametric EQ into the Web Audio graph for the active Ginger media element.\n *\n * Each band is a `BiquadFilterNode` connected in series between the audio source and the\n * speakers. If `useGingerLiveAnalyzer` is also active, the EQ is inserted before the analyser\n * — both share the same `AudioContext` (the browser allows only one `MediaElementAudioSourceNode`\n * per element).\n *\n * Available as a subpath export:\n * ```ts\n * import { useGingerEqualizer } from \"@lucaismyname/ginger/equalizer\";\n * ```\n */\nexport function useGingerEqualizer(\n options: UseGingerEqualizerOptions = {},\n): UseGingerEqualizerResult {\n const { enabled = true, bands: initialBands = DEFAULT_BANDS } = options;\n const { audioRef, state } = useGinger();\n\n const [bands, setBandsState] = useState<EqualizerBand[]>(initialBands);\n const [error, setError] = useState<string | null>(null);\n\n const filterNodesRef = useRef<BiquadFilterNode[]>([]);\n const bandsRef = useRef(bands);\n bandsRef.current = bands;\n\n /** Rebuild the chain when band frequencies/types change — not on every gain tweak (see setBandGain). */\n const bandStructureKey = useMemo(\n () => bands.map((b) => `${b.frequency}:${b.type ?? \"peaking\"}:${b.q ?? 1}`).join(\"|\"),\n [bands],\n );\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: bandStructureKey and currentIndex must re-run graph setup; bands read via bandsRef\n useEffect(() => {\n const el = audioRef.current;\n if (!el || typeof window === \"undefined\") {\n return;\n }\n\n if (!enabled) {\n setProcessingChain(el, []);\n filterNodesRef.current = [];\n return;\n }\n\n try {\n // Attach a temporary analyser solely to get (or create) the shared AudioContext.\n // The graph is rebuilt by liveAudioGraph after we call setProcessingChain, so this\n // temporary consumer is detached immediately after we have the context reference.\n const attached = attachLiveAnalyser(el, {\n fftSize: 32,\n smoothingTimeConstant: 0,\n minDecibels: -100,\n maxDecibels: 0,\n });\n const { context, id: tempId } = attached;\n\n const bandsNow = bandsRef.current;\n const filters: BiquadFilterNode[] = bandsNow.map((band) => {\n const node = context.createBiquadFilter();\n node.type = band.type ?? \"peaking\";\n node.frequency.value = band.frequency;\n node.gain.value = band.gain ?? 0;\n node.Q.value = band.q ?? 1;\n return node;\n });\n\n filterNodesRef.current = filters;\n\n // Install processing chain BEFORE removing the temp analyser so the graph stays valid\n setProcessingChain(el, filters);\n detachLiveAnalyser(el, tempId);\n\n setError(null);\n } catch (e) {\n const msg = e instanceof Error ? e.message : \"Failed to create equalizer\";\n setError(msg);\n filterNodesRef.current = [];\n }\n\n return () => {\n const element = audioRef.current;\n if (element) {\n setProcessingChain(element, []);\n }\n filterNodesRef.current = [];\n };\n }, [enabled, bandStructureKey, audioRef, state.currentIndex]);\n\n const setBandGain = useCallback((bandIndex: number, gainDb: number) => {\n const node = filterNodesRef.current[bandIndex];\n if (node) {\n node.gain.value = gainDb;\n }\n setBandsState((prev) => prev.map((b, i) => (i === bandIndex ? { ...b, gain: gainDb } : b)));\n }, []);\n\n const setBands = useCallback((nextBands: EqualizerBand[]) => {\n setBandsState(nextBands);\n }, []);\n\n return { setBandGain, setBands, bands, error };\n}\n"],"names":["DEFAULT_BANDS","useGingerEqualizer","options","enabled","initialBands","audioRef","state","useGinger","bands","setBandsState","useState","error","setError","filterNodesRef","useRef","bandsRef","bandStructureKey","useMemo","b","useEffect","el","setProcessingChain","attached","attachLiveAnalyser","context","tempId","filters","band","node","detachLiveAnalyser","e","msg","element","setBandGain","useCallback","bandIndex","gainDb","prev","i","setBands","nextBands"],"mappings":";;;AAqCA,MAAMA,IAAiC;AAAA,EACrC,EAAE,WAAW,GAAA;AAAA,EACb,EAAE,WAAW,IAAA;AAAA,EACb,EAAE,WAAW,IAAA;AAAA,EACb,EAAE,WAAW,IAAA;AAAA,EACb,EAAE,WAAW,KAAA;AACf;AAeO,SAASC,EACdC,IAAqC,IACX;AAC1B,QAAM,EAAE,SAAAC,IAAU,IAAM,OAAOC,IAAeJ,MAAkBE,GAC1D,EAAE,UAAAG,GAAU,OAAAC,EAAA,IAAUC,EAAA,GAEtB,CAACC,GAAOC,CAAa,IAAIC,EAA0BN,CAAY,GAC/D,CAACO,GAAOC,CAAQ,IAAIF,EAAwB,IAAI,GAEhDG,IAAiBC,EAA2B,EAAE,GAC9CC,IAAWD,EAAON,CAAK;AAC7B,EAAAO,EAAS,UAAUP;AAGnB,QAAMQ,IAAmBC;AAAA,IACvB,MAAMT,EAAM,IAAI,CAACU,MAAM,GAAGA,EAAE,SAAS,IAAIA,EAAE,QAAQ,SAAS,IAAIA,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,GAAG;AAAA,IACpF,CAACV,CAAK;AAAA,EAAA;AAIR,EAAAW,EAAU,MAAM;AACd,UAAMC,IAAKf,EAAS;AACpB,QAAI,GAACe,KAAM,OAAO,SAAW,MAI7B;AAAA,UAAI,CAACjB,GAAS;AACZ,QAAAkB,EAAmBD,GAAI,EAAE,GACzBP,EAAe,UAAU,CAAA;AACzB;AAAA,MACF;AAEA,UAAI;AAIF,cAAMS,IAAWC,EAAmBH,GAAI;AAAA,UACtC,SAAS;AAAA,UACT,uBAAuB;AAAA,UACvB,aAAa;AAAA,UACb,aAAa;AAAA,QAAA,CACd,GACK,EAAE,SAAAI,GAAS,IAAIC,EAAA,IAAWH,GAG1BI,IADWX,EAAS,QACmB,IAAI,CAACY,MAAS;AACzD,gBAAMC,IAAOJ,EAAQ,mBAAA;AACrB,iBAAAI,EAAK,OAAOD,EAAK,QAAQ,WACzBC,EAAK,UAAU,QAAQD,EAAK,WAC5BC,EAAK,KAAK,QAAQD,EAAK,QAAQ,GAC/BC,EAAK,EAAE,QAAQD,EAAK,KAAK,GAClBC;AAAA,QACT,CAAC;AAED,QAAAf,EAAe,UAAUa,GAGzBL,EAAmBD,GAAIM,CAAO,GAC9BG,EAAmBT,GAAIK,CAAM,GAE7Bb,EAAS,IAAI;AAAA,MACf,SAASkB,GAAG;AACV,cAAMC,IAAMD,aAAa,QAAQA,EAAE,UAAU;AAC7C,QAAAlB,EAASmB,CAAG,GACZlB,EAAe,UAAU,CAAA;AAAA,MAC3B;AAEA,aAAO,MAAM;AACX,cAAMmB,IAAU3B,EAAS;AACzB,QAAI2B,KACFX,EAAmBW,GAAS,EAAE,GAEhCnB,EAAe,UAAU,CAAA;AAAA,MAC3B;AAAA;AAAA,EACF,GAAG,CAACV,GAASa,GAAkBX,GAAUC,EAAM,YAAY,CAAC;AAE5D,QAAM2B,IAAcC,EAAY,CAACC,GAAmBC,MAAmB;AACrE,UAAMR,IAAOf,EAAe,QAAQsB,CAAS;AAC7C,IAAIP,MACFA,EAAK,KAAK,QAAQQ,IAEpB3B,EAAc,CAAC4B,MAASA,EAAK,IAAI,CAACnB,GAAGoB,MAAOA,MAAMH,IAAY,EAAE,GAAGjB,GAAG,MAAMkB,EAAA,IAAWlB,CAAE,CAAC;AAAA,EAC5F,GAAG,CAAA,CAAE,GAECqB,IAAWL,EAAY,CAACM,MAA+B;AAC3D,IAAA/B,EAAc+B,CAAS;AAAA,EACzB,GAAG,CAAA,CAAE;AAEL,SAAO,EAAE,aAAAP,GAAa,UAAAM,GAAU,OAAA/B,GAAO,OAAAG,EAAA;AACzC;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGingerEqualizer.d.ts","sourceRoot":"","sources":["../../src/equalizer/useGingerEqualizer.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,aAAa,GAAG;IAC1B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,6EAA6E;IAC7E,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,qDAAqD;IACrD,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAC3C,sEAAsE;IACtE,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAUF;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,
|
|
1
|
+
{"version":3,"file":"useGingerEqualizer.d.ts","sourceRoot":"","sources":["../../src/equalizer/useGingerEqualizer.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,aAAa,GAAG;IAC1B,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gEAAgE;IAChE,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,6EAA6E;IAC7E,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,qDAAqD;IACrD,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IAC3C,sEAAsE;IACtE,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAUF;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CAuF1B"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("react"),r=require("../GingerSplitContexts-C7puo0M7.cjs"),s={crossOrigin:"anonymous",format:"Full-file fetch + decode is the portable path; Media Source Extensions help only when you control containerized segments. Prefer HTTP(S) or blob: URLs; cross-origin media needs CORS and matching crossOrigin on <audio>. Codec support (MP3, AAC/MP4, Opus, …) is browser-specific."};function a(e){return!!(e&&typeof e.prototype.decodeAudioData=="function")}function n(){if(typeof globalThis>"u")return{supported:!1,reason:"No global object (probe unsupported in this runtime).",hints:s};const e=globalThis;if(typeof e.window>"u")return{supported:!1,reason:"No window (SSR or non-browser). Run the probe in the browser if you need live results.",hints:s};if(!(typeof e.isSecureContext=="boolean"?e.isSecureContext:e.window.isSecureContext!==!1))return{supported:!1,reason:"A secure context (HTTPS or localhost) is required for Web Audio in most browsers. Gapless decode path would not be available here.",hints:s};const o=e.AudioContext??e.webkitAudioContext;return!o||!a(o)?{supported:!1,reason:"Web Audio API is unavailable or decodeAudioData is missing. A gapless decode path cannot run in this environment.",hints:s}:{supported:!0,reason:`Browser exposes Web Audio decode primitives (${typeof e.MediaSource<"u"?"MediaSource is available for segment-based strategies; Ginger still uses a single <audio> for playback today.":"MediaSource is not exposed; only full-buffer decode (or progressive chunking in user code) applies."}). Ginger does not yet schedule gapless transitions — keep using Ginger.Player and existing queue actions.`,hints:s}}function u(){const{tracks:e}=r.useGingerPlayback();return i.useMemo(()=>({...n(),gingerGaplessPlayback:!1,preloadedTrackIds:e.map(o=>o.id??o.fileUrl)}),[e])}exports.probeGaplessCapability=n;exports.useExperimentalGapless=u;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/experimental-gapless/index.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type ExperimentalGaplessState = {\n
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/experimental-gapless/probeGaplessCapability.ts","../../src/experimental-gapless/index.ts"],"sourcesContent":["/**\n * Milestone 1 gapless probe: detects whether the **browser** exposes the minimum APIs\n * for a future Web Audio decode + schedule path. Ginger does not implement gapless\n * playback yet; this never touches the live `HTMLAudioElement` or playback.\n */\n\nexport type GaplessHints = {\n /**\n * Use on `HTMLMediaElement` (and matching `fetch`) when audio is cross-origin so\n * decoded samples can be routed without tainting (required for Web Audio).\n */\n crossOrigin: \"anonymous\" | \"use-credentials\" | null;\n /**\n * Short guidance on containers/codecs and fetch constraints for a decode path.\n */\n format: string;\n};\n\nexport type GaplessCapabilityResult = {\n /**\n * True when `AudioContext` (or legacy equivalent) and `decodeAudioData` appear\n * available in a secure context — i.e. the environment could support a decode-based\n * gapless implementation. Does **not** mean Ginger ships gapless playback.\n */\n supported: boolean;\n /** Human-readable explanation, including caveats and fallback behavior. */\n reason: string;\n hints: GaplessHints;\n};\n\nconst DEFAULT_HINTS: GaplessHints = {\n crossOrigin: \"anonymous\",\n format:\n \"Full-file fetch + decode is the portable path; Media Source Extensions help only when you control containerized segments. Prefer HTTP(S) or blob: URLs; cross-origin media needs CORS and matching crossOrigin on <audio>. Codec support (MP3, AAC/MP4, Opus, …) is browser-specific.\",\n};\n\nfunction hasDecodeAudioDataOnPrototype(ctor: typeof AudioContext | undefined): boolean {\n return Boolean(ctor && typeof ctor.prototype.decodeAudioData === \"function\");\n}\n\n/**\n * Inspect globals only (no `AudioContext` construction) so it is safe to call during SSR\n * or in tests without user gesture.\n */\nexport function probeGaplessCapability(): GaplessCapabilityResult {\n if (typeof globalThis === \"undefined\") {\n return {\n supported: false,\n reason: \"No global object (probe unsupported in this runtime).\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const g = globalThis as unknown as {\n window?: Window;\n isSecureContext?: boolean;\n AudioContext?: typeof AudioContext;\n webkitAudioContext?: typeof AudioContext;\n MediaSource?: typeof MediaSource;\n };\n\n if (typeof g.window === \"undefined\") {\n return {\n supported: false,\n reason:\n \"No window (SSR or non-browser). Run the probe in the browser if you need live results.\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const secure =\n typeof g.isSecureContext === \"boolean\" ? g.isSecureContext : g.window.isSecureContext !== false;\n if (!secure) {\n return {\n supported: false,\n reason:\n \"A secure context (HTTPS or localhost) is required for Web Audio in most browsers. Gapless decode path would not be available here.\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const Ctor = g.AudioContext ?? g.webkitAudioContext;\n if (!Ctor || !hasDecodeAudioDataOnPrototype(Ctor)) {\n return {\n supported: false,\n reason:\n \"Web Audio API is unavailable or decodeAudioData is missing. A gapless decode path cannot run in this environment.\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const mse = typeof g.MediaSource !== \"undefined\";\n const mseNote = mse\n ? \"MediaSource is available for segment-based strategies; Ginger still uses a single <audio> for playback today.\"\n : \"MediaSource is not exposed; only full-buffer decode (or progressive chunking in user code) applies.\";\n\n return {\n supported: true,\n reason: `Browser exposes Web Audio decode primitives (${mseNote}). Ginger does not yet schedule gapless transitions — keep using Ginger.Player and existing queue actions.`,\n hints: DEFAULT_HINTS,\n };\n}\n","import { useMemo } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { type GaplessCapabilityResult, probeGaplessCapability } from \"./probeGaplessCapability\";\n\nexport type { GaplessCapabilityResult, GaplessHints } from \"./probeGaplessCapability\";\nexport { probeGaplessCapability } from \"./probeGaplessCapability\";\n\nexport type ExperimentalGaplessState = GaplessCapabilityResult & {\n /**\n * Ginger does not yet implement gapless scheduling; playback always uses\n * the usual `Ginger.Player` + `<audio>` pipeline.\n */\n gingerGaplessPlayback: false;\n /** Track identifiers for prefetch / experimentation (id, else file URL). */\n preloadedTrackIds: string[];\n};\n\n/**\n * Reports **environment** capability for a future gapless decode path plus queue ids.\n * Does not change Ginger playback behavior.\n */\nexport function useExperimentalGapless(): ExperimentalGaplessState {\n const { tracks } = useGingerPlayback();\n return useMemo(() => {\n const probe = probeGaplessCapability();\n return {\n ...probe,\n gingerGaplessPlayback: false as const,\n preloadedTrackIds: tracks.map((track) => track.id ?? track.fileUrl),\n };\n }, [tracks]);\n}\n"],"names":["DEFAULT_HINTS","hasDecodeAudioDataOnPrototype","ctor","probeGaplessCapability","g","Ctor","useExperimentalGapless","tracks","useGingerPlayback","useMemo","track"],"mappings":"0JA8BMA,EAA8B,CAClC,YAAa,YACb,OACE,uRACJ,EAEA,SAASC,EAA8BC,EAAgD,CACrF,MAAO,GAAQA,GAAQ,OAAOA,EAAK,UAAU,iBAAoB,WACnE,CAMO,SAASC,GAAkD,CAChE,GAAI,OAAO,WAAe,IACxB,MAAO,CACL,UAAW,GACX,OAAQ,wDACR,MAAOH,CAAA,EAIX,MAAMI,EAAI,WAQV,GAAI,OAAOA,EAAE,OAAW,IACtB,MAAO,CACL,UAAW,GACX,OACE,yFACF,MAAOJ,CAAA,EAMX,GAAI,EADF,OAAOI,EAAE,iBAAoB,UAAYA,EAAE,gBAAkBA,EAAE,OAAO,kBAAoB,IAE1F,MAAO,CACL,UAAW,GACX,OACE,qIACF,MAAOJ,CAAA,EAIX,MAAMK,EAAOD,EAAE,cAAgBA,EAAE,mBACjC,MAAI,CAACC,GAAQ,CAACJ,EAA8BI,CAAI,EACvC,CACL,UAAW,GACX,OACE,oHACF,MAAOL,CAAA,EASJ,CACL,UAAW,GACX,OAAQ,gDAPE,OAAOI,EAAE,YAAgB,IAEjC,gHACA,qGAI6D,6GAC/D,MAAOJ,CAAA,CAEX,CChFO,SAASM,GAAmD,CACjE,KAAM,CAAE,OAAAC,CAAA,EAAWC,oBAAA,EACnB,OAAOC,EAAAA,QAAQ,KAEN,CACL,GAFYN,EAAA,EAGZ,sBAAuB,GACvB,kBAAmBI,EAAO,IAAKG,GAAUA,EAAM,IAAMA,EAAM,OAAO,CAAA,GAEnE,CAACH,CAAM,CAAC,CACb"}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { GaplessCapabilityResult } from './probeGaplessCapability';
|
|
2
|
+
export type { GaplessCapabilityResult, GaplessHints } from './probeGaplessCapability';
|
|
3
|
+
export { probeGaplessCapability } from './probeGaplessCapability';
|
|
4
|
+
export type ExperimentalGaplessState = GaplessCapabilityResult & {
|
|
5
|
+
/**
|
|
6
|
+
* Ginger does not yet implement gapless scheduling; playback always uses
|
|
7
|
+
* the usual `Ginger.Player` + `<audio>` pipeline.
|
|
8
|
+
*/
|
|
9
|
+
gingerGaplessPlayback: false;
|
|
10
|
+
/** Track identifiers for prefetch / experimentation (id, else file URL). */
|
|
4
11
|
preloadedTrackIds: string[];
|
|
5
12
|
};
|
|
6
13
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @deprecated This export is intentionally non-production and currently reports capabilities only.
|
|
10
|
-
* It does not alter Ginger playback behavior.
|
|
14
|
+
* Reports **environment** capability for a future gapless decode path plus queue ids.
|
|
15
|
+
* Does not change Ginger playback behavior.
|
|
11
16
|
*/
|
|
12
17
|
export declare function useExperimentalGapless(): ExperimentalGaplessState;
|
|
13
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/experimental-gapless/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/experimental-gapless/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,uBAAuB,EAA0B,MAAM,0BAA0B,CAAC;AAEhG,YAAY,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACtF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,MAAM,MAAM,wBAAwB,GAAG,uBAAuB,GAAG;IAC/D;;;OAGG;IACH,qBAAqB,EAAE,KAAK,CAAC;IAC7B,4EAA4E;IAC5E,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,wBAAwB,CAUjE"}
|
|
@@ -1,17 +1,53 @@
|
|
|
1
|
-
import { useMemo as
|
|
2
|
-
import { b as
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { useMemo as r } from "react";
|
|
2
|
+
import { b as i } from "../GingerSplitContexts-BzBExb95.js";
|
|
3
|
+
const s = {
|
|
4
|
+
crossOrigin: "anonymous",
|
|
5
|
+
format: "Full-file fetch + decode is the portable path; Media Source Extensions help only when you control containerized segments. Prefer HTTP(S) or blob: URLs; cross-origin media needs CORS and matching crossOrigin on <audio>. Codec support (MP3, AAC/MP4, Opus, …) is browser-specific."
|
|
6
|
+
};
|
|
7
|
+
function t(e) {
|
|
8
|
+
return !!(e && typeof e.prototype.decodeAudioData == "function");
|
|
9
|
+
}
|
|
10
|
+
function a() {
|
|
11
|
+
if (typeof globalThis > "u")
|
|
12
|
+
return {
|
|
13
|
+
supported: !1,
|
|
14
|
+
reason: "No global object (probe unsupported in this runtime).",
|
|
15
|
+
hints: s
|
|
16
|
+
};
|
|
17
|
+
const e = globalThis;
|
|
18
|
+
if (typeof e.window > "u")
|
|
19
|
+
return {
|
|
7
20
|
supported: !1,
|
|
8
|
-
reason: "
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
21
|
+
reason: "No window (SSR or non-browser). Run the probe in the browser if you need live results.",
|
|
22
|
+
hints: s
|
|
23
|
+
};
|
|
24
|
+
if (!(typeof e.isSecureContext == "boolean" ? e.isSecureContext : e.window.isSecureContext !== !1))
|
|
25
|
+
return {
|
|
26
|
+
supported: !1,
|
|
27
|
+
reason: "A secure context (HTTPS or localhost) is required for Web Audio in most browsers. Gapless decode path would not be available here.",
|
|
28
|
+
hints: s
|
|
29
|
+
};
|
|
30
|
+
const o = e.AudioContext ?? e.webkitAudioContext;
|
|
31
|
+
return !o || !t(o) ? {
|
|
32
|
+
supported: !1,
|
|
33
|
+
reason: "Web Audio API is unavailable or decodeAudioData is missing. A gapless decode path cannot run in this environment.",
|
|
34
|
+
hints: s
|
|
35
|
+
} : {
|
|
36
|
+
supported: !0,
|
|
37
|
+
reason: `Browser exposes Web Audio decode primitives (${typeof e.MediaSource < "u" ? "MediaSource is available for segment-based strategies; Ginger still uses a single <audio> for playback today." : "MediaSource is not exposed; only full-buffer decode (or progressive chunking in user code) applies."}). Ginger does not yet schedule gapless transitions — keep using Ginger.Player and existing queue actions.`,
|
|
38
|
+
hints: s
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function c() {
|
|
42
|
+
const { tracks: e } = i();
|
|
43
|
+
return r(() => ({
|
|
44
|
+
...a(),
|
|
45
|
+
gingerGaplessPlayback: !1,
|
|
46
|
+
preloadedTrackIds: e.map((o) => o.id ?? o.fileUrl)
|
|
47
|
+
}), [e]);
|
|
13
48
|
}
|
|
14
49
|
export {
|
|
15
|
-
|
|
50
|
+
a as probeGaplessCapability,
|
|
51
|
+
c as useExperimentalGapless
|
|
16
52
|
};
|
|
17
53
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/experimental-gapless/index.ts"],"sourcesContent":["import { useMemo } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\n\nexport type ExperimentalGaplessState = {\n
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/experimental-gapless/probeGaplessCapability.ts","../../src/experimental-gapless/index.ts"],"sourcesContent":["/**\n * Milestone 1 gapless probe: detects whether the **browser** exposes the minimum APIs\n * for a future Web Audio decode + schedule path. Ginger does not implement gapless\n * playback yet; this never touches the live `HTMLAudioElement` or playback.\n */\n\nexport type GaplessHints = {\n /**\n * Use on `HTMLMediaElement` (and matching `fetch`) when audio is cross-origin so\n * decoded samples can be routed without tainting (required for Web Audio).\n */\n crossOrigin: \"anonymous\" | \"use-credentials\" | null;\n /**\n * Short guidance on containers/codecs and fetch constraints for a decode path.\n */\n format: string;\n};\n\nexport type GaplessCapabilityResult = {\n /**\n * True when `AudioContext` (or legacy equivalent) and `decodeAudioData` appear\n * available in a secure context — i.e. the environment could support a decode-based\n * gapless implementation. Does **not** mean Ginger ships gapless playback.\n */\n supported: boolean;\n /** Human-readable explanation, including caveats and fallback behavior. */\n reason: string;\n hints: GaplessHints;\n};\n\nconst DEFAULT_HINTS: GaplessHints = {\n crossOrigin: \"anonymous\",\n format:\n \"Full-file fetch + decode is the portable path; Media Source Extensions help only when you control containerized segments. Prefer HTTP(S) or blob: URLs; cross-origin media needs CORS and matching crossOrigin on <audio>. Codec support (MP3, AAC/MP4, Opus, …) is browser-specific.\",\n};\n\nfunction hasDecodeAudioDataOnPrototype(ctor: typeof AudioContext | undefined): boolean {\n return Boolean(ctor && typeof ctor.prototype.decodeAudioData === \"function\");\n}\n\n/**\n * Inspect globals only (no `AudioContext` construction) so it is safe to call during SSR\n * or in tests without user gesture.\n */\nexport function probeGaplessCapability(): GaplessCapabilityResult {\n if (typeof globalThis === \"undefined\") {\n return {\n supported: false,\n reason: \"No global object (probe unsupported in this runtime).\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const g = globalThis as unknown as {\n window?: Window;\n isSecureContext?: boolean;\n AudioContext?: typeof AudioContext;\n webkitAudioContext?: typeof AudioContext;\n MediaSource?: typeof MediaSource;\n };\n\n if (typeof g.window === \"undefined\") {\n return {\n supported: false,\n reason:\n \"No window (SSR or non-browser). Run the probe in the browser if you need live results.\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const secure =\n typeof g.isSecureContext === \"boolean\" ? g.isSecureContext : g.window.isSecureContext !== false;\n if (!secure) {\n return {\n supported: false,\n reason:\n \"A secure context (HTTPS or localhost) is required for Web Audio in most browsers. Gapless decode path would not be available here.\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const Ctor = g.AudioContext ?? g.webkitAudioContext;\n if (!Ctor || !hasDecodeAudioDataOnPrototype(Ctor)) {\n return {\n supported: false,\n reason:\n \"Web Audio API is unavailable or decodeAudioData is missing. A gapless decode path cannot run in this environment.\",\n hints: DEFAULT_HINTS,\n };\n }\n\n const mse = typeof g.MediaSource !== \"undefined\";\n const mseNote = mse\n ? \"MediaSource is available for segment-based strategies; Ginger still uses a single <audio> for playback today.\"\n : \"MediaSource is not exposed; only full-buffer decode (or progressive chunking in user code) applies.\";\n\n return {\n supported: true,\n reason: `Browser exposes Web Audio decode primitives (${mseNote}). Ginger does not yet schedule gapless transitions — keep using Ginger.Player and existing queue actions.`,\n hints: DEFAULT_HINTS,\n };\n}\n","import { useMemo } from \"react\";\nimport { useGingerPlayback } from \"../context/GingerSplitContexts\";\nimport { type GaplessCapabilityResult, probeGaplessCapability } from \"./probeGaplessCapability\";\n\nexport type { GaplessCapabilityResult, GaplessHints } from \"./probeGaplessCapability\";\nexport { probeGaplessCapability } from \"./probeGaplessCapability\";\n\nexport type ExperimentalGaplessState = GaplessCapabilityResult & {\n /**\n * Ginger does not yet implement gapless scheduling; playback always uses\n * the usual `Ginger.Player` + `<audio>` pipeline.\n */\n gingerGaplessPlayback: false;\n /** Track identifiers for prefetch / experimentation (id, else file URL). */\n preloadedTrackIds: string[];\n};\n\n/**\n * Reports **environment** capability for a future gapless decode path plus queue ids.\n * Does not change Ginger playback behavior.\n */\nexport function useExperimentalGapless(): ExperimentalGaplessState {\n const { tracks } = useGingerPlayback();\n return useMemo(() => {\n const probe = probeGaplessCapability();\n return {\n ...probe,\n gingerGaplessPlayback: false as const,\n preloadedTrackIds: tracks.map((track) => track.id ?? track.fileUrl),\n };\n }, [tracks]);\n}\n"],"names":["DEFAULT_HINTS","hasDecodeAudioDataOnPrototype","ctor","probeGaplessCapability","g","Ctor","useExperimentalGapless","tracks","useGingerPlayback","useMemo","track"],"mappings":";;AA8BA,MAAMA,IAA8B;AAAA,EAClC,aAAa;AAAA,EACb,QACE;AACJ;AAEA,SAASC,EAA8BC,GAAgD;AACrF,SAAO,GAAQA,KAAQ,OAAOA,EAAK,UAAU,mBAAoB;AACnE;AAMO,SAASC,IAAkD;AAChE,MAAI,OAAO,aAAe;AACxB,WAAO;AAAA,MACL,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,OAAOH;AAAA,IAAA;AAIX,QAAMI,IAAI;AAQV,MAAI,OAAOA,EAAE,SAAW;AACtB,WAAO;AAAA,MACL,WAAW;AAAA,MACX,QACE;AAAA,MACF,OAAOJ;AAAA,IAAA;AAMX,MAAI,EADF,OAAOI,EAAE,mBAAoB,YAAYA,EAAE,kBAAkBA,EAAE,OAAO,oBAAoB;AAE1F,WAAO;AAAA,MACL,WAAW;AAAA,MACX,QACE;AAAA,MACF,OAAOJ;AAAA,IAAA;AAIX,QAAMK,IAAOD,EAAE,gBAAgBA,EAAE;AACjC,SAAI,CAACC,KAAQ,CAACJ,EAA8BI,CAAI,IACvC;AAAA,IACL,WAAW;AAAA,IACX,QACE;AAAA,IACF,OAAOL;AAAA,EAAA,IASJ;AAAA,IACL,WAAW;AAAA,IACX,QAAQ,gDAPE,OAAOI,EAAE,cAAgB,MAEjC,kHACA,qGAI6D;AAAA,IAC/D,OAAOJ;AAAA,EAAA;AAEX;AChFO,SAASM,IAAmD;AACjE,QAAM,EAAE,QAAAC,EAAA,IAAWC,EAAA;AACnB,SAAOC,EAAQ,OAEN;AAAA,IACL,GAFYN,EAAA;AAAA,IAGZ,uBAAuB;AAAA,IACvB,mBAAmBI,EAAO,IAAI,CAACG,MAAUA,EAAM,MAAMA,EAAM,OAAO;AAAA,EAAA,IAEnE,CAACH,CAAM,CAAC;AACb;"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Milestone 1 gapless probe: detects whether the **browser** exposes the minimum APIs
|
|
3
|
+
* for a future Web Audio decode + schedule path. Ginger does not implement gapless
|
|
4
|
+
* playback yet; this never touches the live `HTMLAudioElement` or playback.
|
|
5
|
+
*/
|
|
6
|
+
export type GaplessHints = {
|
|
7
|
+
/**
|
|
8
|
+
* Use on `HTMLMediaElement` (and matching `fetch`) when audio is cross-origin so
|
|
9
|
+
* decoded samples can be routed without tainting (required for Web Audio).
|
|
10
|
+
*/
|
|
11
|
+
crossOrigin: "anonymous" | "use-credentials" | null;
|
|
12
|
+
/**
|
|
13
|
+
* Short guidance on containers/codecs and fetch constraints for a decode path.
|
|
14
|
+
*/
|
|
15
|
+
format: string;
|
|
16
|
+
};
|
|
17
|
+
export type GaplessCapabilityResult = {
|
|
18
|
+
/**
|
|
19
|
+
* True when `AudioContext` (or legacy equivalent) and `decodeAudioData` appear
|
|
20
|
+
* available in a secure context — i.e. the environment could support a decode-based
|
|
21
|
+
* gapless implementation. Does **not** mean Ginger ships gapless playback.
|
|
22
|
+
*/
|
|
23
|
+
supported: boolean;
|
|
24
|
+
/** Human-readable explanation, including caveats and fallback behavior. */
|
|
25
|
+
reason: string;
|
|
26
|
+
hints: GaplessHints;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Inspect globals only (no `AudioContext` construction) so it is safe to call during SSR
|
|
30
|
+
* or in tests without user gesture.
|
|
31
|
+
*/
|
|
32
|
+
export declare function probeGaplessCapability(): GaplessCapabilityResult;
|
|
33
|
+
//# sourceMappingURL=probeGaplessCapability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probeGaplessCapability.d.ts","sourceRoot":"","sources":["../../src/experimental-gapless/probeGaplessCapability.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,WAAW,EAAE,WAAW,GAAG,iBAAiB,GAAG,IAAI,CAAC;IACpD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;OAIG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAYF;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,uBAAuB,CAyDhE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probeGaplessCapability.test.d.ts","sourceRoot":"","sources":["../../src/experimental-gapless/probeGaplessCapability.test.ts"],"names":[],"mappings":""}
|