@juandinella/audio-bands 0.4.7 → 0.6.2

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.
@@ -144,6 +144,7 @@ var AudioBands = class {
144
144
  micActive: false,
145
145
  hasTrack: false,
146
146
  loadError: null,
147
+ playbackError: null,
147
148
  micError: null
148
149
  };
149
150
  this.ctx = null;
@@ -157,6 +158,10 @@ var AudioBands = class {
157
158
  this.musicSource = null;
158
159
  this.micSource = null;
159
160
  this.micStream = null;
161
+ this.musicEventCleanup = null;
162
+ this.pendingLoadCleanup = null;
163
+ this.pendingLoadReject = null;
164
+ this.trackLoop = false;
160
165
  this.destroyed = false;
161
166
  this.options = options;
162
167
  this.musicConfig = normalizeAnalyserConfig(options.music, DEFAULT_MUSIC_ANALYSER);
@@ -177,41 +182,96 @@ var AudioBands = class {
177
182
  try {
178
183
  ctx = this.ensureCtx();
179
184
  } catch (error) {
180
- throw this.handleError("load", error);
185
+ throw this.handleError("load", error, "load_error");
181
186
  }
182
187
  this.teardownMusic();
183
188
  const audio = new Audio();
184
189
  audio.crossOrigin = "anonymous";
190
+ audio.preload = "auto";
185
191
  audio.src = url;
186
- audio.loop = true;
192
+ audio.loop = this.trackLoop;
193
+ this.bindMusicElementEvents(audio);
187
194
  this.audioEl = audio;
188
- this.setState({ hasTrack: true, loadError: null });
195
+ this.setState({ hasTrack: false, loadError: null, playbackError: null });
189
196
  const source = ctx.createMediaElementSource(audio);
190
197
  source.connect(this.musicAnalyser);
191
198
  this.musicSource = source;
199
+ await new Promise((resolve, reject) => {
200
+ const finalize = () => {
201
+ cleanup();
202
+ this.pendingLoadCleanup = null;
203
+ this.pendingLoadReject = null;
204
+ };
205
+ const handleReady = () => {
206
+ finalize();
207
+ this.setState({ hasTrack: true, loadError: null });
208
+ resolve();
209
+ };
210
+ const handleFailure = (event) => {
211
+ finalize();
212
+ const mediaError = event?.target?.error ?? audio.error ?? event;
213
+ reject(this.handleLoadFailure(mediaError));
214
+ };
215
+ const cleanup = () => {
216
+ audio.removeEventListener("loadedmetadata", handleReady);
217
+ audio.removeEventListener("canplay", handleReady);
218
+ audio.removeEventListener("error", handleFailure);
219
+ };
220
+ this.pendingLoadCleanup = cleanup;
221
+ this.pendingLoadReject = reject;
222
+ audio.addEventListener("loadedmetadata", handleReady, { once: true });
223
+ audio.addEventListener("canplay", handleReady, { once: true });
224
+ audio.addEventListener("error", handleFailure, { once: true });
225
+ audio.load();
226
+ });
227
+ }
228
+ async play() {
229
+ const audio = this.audioEl;
230
+ if (!audio) return;
192
231
  try {
193
232
  await audio.play();
194
- this.setState({ isPlaying: true, loadError: null });
195
- this.options.onPlay?.();
233
+ this.setState({ playbackError: null });
196
234
  } catch (error) {
197
- throw this.handleError("load", error, "load_error");
235
+ throw this.handleError("playback", error, "playback_error");
198
236
  }
199
237
  }
200
- togglePlayPause() {
238
+ pause() {
239
+ const audio = this.audioEl;
240
+ if (!audio || audio.paused) return;
241
+ audio.pause();
242
+ }
243
+ async togglePlayPause() {
201
244
  const audio = this.audioEl;
202
245
  if (!audio) return;
203
246
  if (audio.paused) {
204
- void audio.play().then(() => {
205
- this.setState({ isPlaying: true, loadError: null });
206
- this.options.onPlay?.();
207
- }).catch((error) => {
208
- this.handleError("load", error, "playback_error");
209
- });
247
+ await this.play();
210
248
  return;
211
249
  }
212
- audio.pause();
213
- this.setState({ isPlaying: false });
214
- this.options.onPause?.();
250
+ this.pause();
251
+ }
252
+ setLoop(loop) {
253
+ this.trackLoop = loop;
254
+ if (this.audioEl) this.audioEl.loop = loop;
255
+ }
256
+ seek(seconds) {
257
+ if (!Number.isFinite(seconds) || seconds < 0) {
258
+ throw new AudioBandsError(
259
+ "config",
260
+ "invalid_config",
261
+ "seek time must be a finite number greater than or equal to 0"
262
+ );
263
+ }
264
+ if (!this.audioEl) return;
265
+ const duration = Number.isFinite(this.audioEl.duration) ? this.audioEl.duration : null;
266
+ this.audioEl.currentTime = duration === null ? seconds : Math.min(seconds, duration);
267
+ }
268
+ getDuration() {
269
+ if (!this.audioEl) return null;
270
+ return Number.isFinite(this.audioEl.duration) ? this.audioEl.duration : null;
271
+ }
272
+ getCurrentTime() {
273
+ if (!this.audioEl) return null;
274
+ return Number.isFinite(this.audioEl.currentTime) ? this.audioEl.currentTime : null;
215
275
  }
216
276
  async enableMic() {
217
277
  let ctx;
@@ -270,6 +330,16 @@ var AudioBands = class {
270
330
  getWaveform(source = "music") {
271
331
  return this.readWaveformData(source);
272
332
  }
333
+ snapshot(source = "music") {
334
+ const fft = this.readFrequencyData(source);
335
+ const waveform = this.readWaveformData(source);
336
+ return {
337
+ bands: fft ? computeBands(fft, this.classicRanges) : { ...ZERO },
338
+ customBands: fft ? computeCustomBands(fft, this.customBandRanges) : computeCustomBands(new Uint8Array(1), this.customBandRanges),
339
+ fft,
340
+ waveform
341
+ };
342
+ }
273
343
  destroy() {
274
344
  if (this.destroyed) return;
275
345
  this.teardownMusic();
@@ -337,16 +407,19 @@ var AudioBands = class {
337
407
  analyser.smoothingTimeConstant = config.smoothingTimeConstant;
338
408
  return analyser;
339
409
  }
340
- handleError(kind, error, fallbackCode = kind === "mic" ? "mic_error" : "load_error") {
410
+ handleError(kind, error, fallbackCode = kind === "mic" ? "mic_error" : kind === "playback" ? "playback_error" : "load_error") {
341
411
  const wrapped = error instanceof AudioBandsError ? error : new AudioBandsError(
342
412
  kind,
343
413
  fallbackCode,
344
- kind === "mic" ? "Failed to access microphone input" : "Failed to load or play audio track",
414
+ kind === "mic" ? "Failed to access microphone input" : kind === "playback" ? "Failed to play audio track" : "Failed to load audio track",
345
415
  error
346
416
  );
347
417
  if (kind === "load") {
348
418
  this.setState({ isPlaying: false, loadError: wrapped });
349
419
  this.options.onLoadError?.(wrapped);
420
+ } else if (kind === "playback") {
421
+ this.setState({ isPlaying: false, playbackError: wrapped });
422
+ this.options.onPlaybackError?.(wrapped);
350
423
  } else {
351
424
  this.setState({ micActive: false, micError: wrapped });
352
425
  this.options.onMicError?.(wrapped);
@@ -354,6 +427,36 @@ var AudioBands = class {
354
427
  this.options.onError?.(wrapped);
355
428
  return wrapped;
356
429
  }
430
+ bindMusicElementEvents(audio) {
431
+ this.musicEventCleanup?.();
432
+ const handlePlay = () => {
433
+ if (this.audioEl !== audio) return;
434
+ this.setState({ isPlaying: true, playbackError: null });
435
+ this.options.onPlay?.();
436
+ };
437
+ const handlePause = () => {
438
+ if (this.audioEl !== audio) return;
439
+ this.setState({ isPlaying: false });
440
+ this.options.onPause?.();
441
+ };
442
+ const handleEnded = () => {
443
+ if (this.audioEl !== audio) return;
444
+ this.setState({ isPlaying: false });
445
+ };
446
+ audio.addEventListener("play", handlePlay);
447
+ audio.addEventListener("pause", handlePause);
448
+ audio.addEventListener("ended", handleEnded);
449
+ this.musicEventCleanup = () => {
450
+ audio.removeEventListener("play", handlePlay);
451
+ audio.removeEventListener("pause", handlePause);
452
+ audio.removeEventListener("ended", handleEnded);
453
+ if (this.musicEventCleanup) this.musicEventCleanup = null;
454
+ };
455
+ }
456
+ handleLoadFailure(error) {
457
+ this.teardownMusic();
458
+ return this.handleError("load", error, "load_error");
459
+ }
357
460
  setState(patch) {
358
461
  let changed = false;
359
462
  for (const [key, value] of Object.entries(patch)) {
@@ -365,6 +468,23 @@ var AudioBands = class {
365
468
  if (changed) this.options.onStateChange?.(this.getState());
366
469
  }
367
470
  teardownMusic() {
471
+ this.musicEventCleanup?.();
472
+ if (this.pendingLoadReject) {
473
+ const reject = this.pendingLoadReject;
474
+ this.pendingLoadReject = null;
475
+ this.pendingLoadCleanup?.();
476
+ this.pendingLoadCleanup = null;
477
+ reject(
478
+ new AudioBandsError(
479
+ "load",
480
+ "load_error",
481
+ "Audio track loading was interrupted before completion"
482
+ )
483
+ );
484
+ } else if (this.pendingLoadCleanup) {
485
+ this.pendingLoadCleanup();
486
+ this.pendingLoadCleanup = null;
487
+ }
368
488
  this.audioEl?.pause();
369
489
  if (this.audioEl) {
370
490
  this.audioEl.src = "";
@@ -377,7 +497,11 @@ var AudioBands = class {
377
497
  }
378
498
  this.musicSource = null;
379
499
  this.musicWaveformData = this.musicAnalyser ? new Uint8Array(this.musicAnalyser.fftSize) : null;
380
- this.setState({ isPlaying: false, hasTrack: false });
500
+ this.setState({
501
+ isPlaying: false,
502
+ hasTrack: false,
503
+ playbackError: null
504
+ });
381
505
  }
382
506
  };
383
507
 
@@ -387,83 +511,168 @@ var INITIAL_STATE = {
387
511
  micActive: false,
388
512
  hasTrack: false,
389
513
  loadError: null,
514
+ playbackError: null,
390
515
  micError: null
391
516
  };
517
+ function stableStringify(value) {
518
+ if (Array.isArray(value)) {
519
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
520
+ }
521
+ if (value && typeof value === "object") {
522
+ return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, nestedValue]) => `${JSON.stringify(key)}:${stableStringify(nestedValue)}`).join(",")}}`;
523
+ }
524
+ return JSON.stringify(value) ?? "null";
525
+ }
526
+ function getStructuralOptionsKey(options) {
527
+ return stableStringify({
528
+ music: options.music,
529
+ mic: options.mic,
530
+ bandRanges: options.bandRanges,
531
+ customBands: options.customBands
532
+ });
533
+ }
534
+ function createAudioBandsInstance(options, latestOptions, setState, instanceRef) {
535
+ const next = new AudioBands({
536
+ ...options,
537
+ onPlay: () => {
538
+ if (instanceRef.current !== next) return;
539
+ latestOptions.current.onPlay?.();
540
+ },
541
+ onPause: () => {
542
+ if (instanceRef.current !== next) return;
543
+ latestOptions.current.onPause?.();
544
+ },
545
+ onError: (error) => {
546
+ if (instanceRef.current !== next) return;
547
+ latestOptions.current.onError?.(error);
548
+ },
549
+ onLoadError: (error) => {
550
+ if (instanceRef.current !== next) return;
551
+ latestOptions.current.onLoadError?.(error);
552
+ },
553
+ onPlaybackError: (error) => {
554
+ if (instanceRef.current !== next) return;
555
+ latestOptions.current.onPlaybackError?.(error);
556
+ },
557
+ onMicError: (error) => {
558
+ if (instanceRef.current !== next) return;
559
+ latestOptions.current.onMicError?.(error);
560
+ },
561
+ onMicStart: () => {
562
+ if (instanceRef.current !== next) return;
563
+ latestOptions.current.onMicStart?.();
564
+ },
565
+ onMicStop: () => {
566
+ if (instanceRef.current !== next) return;
567
+ latestOptions.current.onMicStop?.();
568
+ },
569
+ onStateChange: (nextState) => {
570
+ if (instanceRef.current !== next) return;
571
+ setState(nextState);
572
+ latestOptions.current.onStateChange?.(nextState);
573
+ }
574
+ });
575
+ return next;
576
+ }
392
577
  function useAudioBands(options = {}) {
393
578
  const [state, setState] = (0, import_react.useState)(INITIAL_STATE);
394
579
  const latestOptions = (0, import_react.useRef)(options);
395
580
  const instance = (0, import_react.useRef)(null);
581
+ const structuralOptionsKey = getStructuralOptionsKey(options);
582
+ const structuralOptionsKeyRef = (0, import_react.useRef)(structuralOptionsKey);
396
583
  latestOptions.current = options;
397
- if (!instance.current) {
398
- instance.current = new AudioBands({
399
- ...options,
400
- onPlay: () => {
401
- latestOptions.current.onPlay?.();
402
- },
403
- onPause: () => {
404
- latestOptions.current.onPause?.();
405
- },
406
- onError: (error) => {
407
- latestOptions.current.onError?.(error);
408
- },
409
- onLoadError: (error) => {
410
- latestOptions.current.onLoadError?.(error);
411
- },
412
- onMicError: (error) => {
413
- latestOptions.current.onMicError?.(error);
414
- },
415
- onMicStart: () => {
416
- latestOptions.current.onMicStart?.();
417
- },
418
- onMicStop: () => {
419
- latestOptions.current.onMicStop?.();
420
- },
421
- onStateChange: (nextState) => {
422
- setState(nextState);
423
- latestOptions.current.onStateChange?.(nextState);
424
- }
425
- });
426
- }
584
+ const getOrCreateInstance = () => {
585
+ if (instance.current) return instance.current;
586
+ const next = createAudioBandsInstance(options, latestOptions, setState, instance);
587
+ instance.current = next;
588
+ structuralOptionsKeyRef.current = structuralOptionsKey;
589
+ return next;
590
+ };
591
+ getOrCreateInstance();
427
592
  (0, import_react.useEffect)(() => {
428
- setState(instance.current.getState());
429
- return () => instance.current?.destroy();
593
+ const current = getOrCreateInstance();
594
+ setState(current.getState());
595
+ return () => {
596
+ if (instance.current !== current) return;
597
+ current.destroy();
598
+ instance.current = null;
599
+ };
430
600
  }, []);
601
+ (0, import_react.useEffect)(() => {
602
+ if (structuralOptionsKeyRef.current === structuralOptionsKey) return;
603
+ const previous = instance.current;
604
+ const next = createAudioBandsInstance(options, latestOptions, setState, instance);
605
+ instance.current = next;
606
+ structuralOptionsKeyRef.current = structuralOptionsKey;
607
+ setState(next.getState());
608
+ previous?.destroy();
609
+ }, [options, structuralOptionsKey]);
431
610
  const loadTrack = (0, import_react.useCallback)(async (url) => {
432
- await instance.current.load(url);
611
+ await getOrCreateInstance().load(url);
612
+ }, []);
613
+ const play = (0, import_react.useCallback)(async () => {
614
+ await getOrCreateInstance().play();
433
615
  }, []);
434
- const togglePlayPause = (0, import_react.useCallback)(() => {
435
- instance.current.togglePlayPause();
616
+ const pause = (0, import_react.useCallback)(() => {
617
+ getOrCreateInstance().pause();
618
+ }, []);
619
+ const setLoop = (0, import_react.useCallback)((loop) => {
620
+ getOrCreateInstance().setLoop(loop);
621
+ }, []);
622
+ const seek = (0, import_react.useCallback)((seconds) => {
623
+ getOrCreateInstance().seek(seconds);
624
+ }, []);
625
+ const getDuration = (0, import_react.useCallback)(() => {
626
+ return getOrCreateInstance().getDuration();
627
+ }, []);
628
+ const getCurrentTime = (0, import_react.useCallback)(() => {
629
+ return getOrCreateInstance().getCurrentTime();
630
+ }, []);
631
+ const togglePlayPause = (0, import_react.useCallback)(async () => {
632
+ await getOrCreateInstance().togglePlayPause();
436
633
  }, []);
437
634
  const toggleMic = (0, import_react.useCallback)(async () => {
438
- if (instance.current.getState().micActive) {
439
- instance.current.disableMic();
635
+ const current = getOrCreateInstance();
636
+ if (current.getState().micActive) {
637
+ current.disableMic();
440
638
  } else {
441
- await instance.current.enableMic();
639
+ await current.enableMic();
442
640
  }
443
641
  }, []);
642
+ const snapshot = (0, import_react.useCallback)((source) => {
643
+ return getOrCreateInstance().snapshot(source);
644
+ }, []);
444
645
  const getBands = (0, import_react.useCallback)((source) => {
445
- return instance.current.getBands(source);
646
+ return getOrCreateInstance().getBands(source);
446
647
  }, []);
447
648
  const getCustomBands = (0, import_react.useCallback)((source) => {
448
- return instance.current.getCustomBands(source);
649
+ return getOrCreateInstance().getCustomBands(source);
449
650
  }, []);
450
651
  const getFftData = (0, import_react.useCallback)((source) => {
451
- return instance.current.getFftData(source);
652
+ return getOrCreateInstance().getFftData(source);
452
653
  }, []);
453
654
  const getWaveform = (0, import_react.useCallback)((source) => {
454
- return instance.current.getWaveform(source);
655
+ return getOrCreateInstance().getWaveform(source);
455
656
  }, []);
456
657
  return {
457
658
  isPlaying: state.isPlaying,
458
659
  micActive: state.micActive,
459
660
  hasTrack: state.hasTrack,
460
- audioError: Boolean(state.loadError || state.micError),
661
+ audioError: Boolean(state.loadError || state.playbackError || state.micError),
461
662
  loadError: state.loadError,
663
+ playbackError: state.playbackError,
462
664
  micError: state.micError,
463
665
  state,
464
666
  loadTrack,
667
+ play,
668
+ pause,
669
+ setLoop,
670
+ seek,
671
+ getDuration,
672
+ getCurrentTime,
465
673
  togglePlayPause,
466
674
  toggleMic,
675
+ snapshot,
467
676
  getBands,
468
677
  getCustomBands,
469
678
  getFftData,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/react-entry.ts","../src/react.ts","../src/errors.ts","../src/core.ts"],"sourcesContent":["export { useAudioBands } from './react';\nexport { AudioBandsError } from './errors';\nexport type { UseAudioBandsReturn } from './react';\nexport type {\n AudioAnalyserConfig,\n AudioBandsCallbacks,\n AudioBandsErrorCode,\n AudioBandsErrorKind,\n AudioBandsOptions,\n AudioBandsState,\n AudioSource,\n BandRange,\n Bands,\n ClassicBandRanges,\n CustomBandRanges,\n} from './types';\n","'use client';\n\nimport { useRef, useState, useCallback, useEffect } from 'react';\nimport { AudioBands } from './core';\nimport type { AudioBandsOptions, AudioBandsState, AudioSource, Bands } from './types';\nimport type { AudioBandsError } from './errors';\n\nexport type UseAudioBandsReturn = {\n isPlaying: boolean;\n micActive: boolean;\n hasTrack: boolean;\n audioError: boolean;\n loadError: AudioBandsError | null;\n micError: AudioBandsError | null;\n state: AudioBandsState;\n loadTrack: (url: string) => Promise<void>;\n togglePlayPause: () => void;\n toggleMic: () => Promise<void>;\n getBands: (source?: AudioSource) => Bands;\n getCustomBands: (source?: AudioSource) => Record<string, number>;\n getFftData: (source?: AudioSource) => Uint8Array<ArrayBuffer> | null;\n getWaveform: (source?: AudioSource) => Uint8Array<ArrayBuffer> | null;\n};\n\nconst INITIAL_STATE: AudioBandsState = {\n isPlaying: false,\n micActive: false,\n hasTrack: false,\n loadError: null,\n micError: null,\n};\n\n/**\n * React hook — thin wrapper over AudioBands.\n * Handles lifecycle (destroy on unmount) and exposes state for re-renders.\n */\nexport function useAudioBands(options: AudioBandsOptions = {}): UseAudioBandsReturn {\n const [state, setState] = useState<AudioBandsState>(INITIAL_STATE);\n const latestOptions = useRef(options);\n const instance = useRef<AudioBands | null>(null);\n\n latestOptions.current = options;\n\n if (!instance.current) {\n instance.current = new AudioBands({\n ...options,\n onPlay: () => {\n latestOptions.current.onPlay?.();\n },\n onPause: () => {\n latestOptions.current.onPause?.();\n },\n onError: (error) => {\n latestOptions.current.onError?.(error);\n },\n onLoadError: (error) => {\n latestOptions.current.onLoadError?.(error);\n },\n onMicError: (error) => {\n latestOptions.current.onMicError?.(error);\n },\n onMicStart: () => {\n latestOptions.current.onMicStart?.();\n },\n onMicStop: () => {\n latestOptions.current.onMicStop?.();\n },\n onStateChange: (nextState) => {\n setState(nextState);\n latestOptions.current.onStateChange?.(nextState);\n },\n });\n }\n\n useEffect(() => {\n setState(instance.current!.getState());\n return () => instance.current?.destroy();\n }, []);\n\n const loadTrack = useCallback(async (url: string) => {\n await instance.current!.load(url);\n }, []);\n\n const togglePlayPause = useCallback(() => {\n instance.current!.togglePlayPause();\n }, []);\n\n const toggleMic = useCallback(async () => {\n if (instance.current!.getState().micActive) {\n instance.current!.disableMic();\n } else {\n await instance.current!.enableMic();\n }\n }, []);\n\n const getBands = useCallback((source?: AudioSource) => {\n return instance.current!.getBands(source);\n }, []);\n\n const getCustomBands = useCallback((source?: AudioSource) => {\n return instance.current!.getCustomBands(source);\n }, []);\n\n const getFftData = useCallback((source?: AudioSource) => {\n return instance.current!.getFftData(source);\n }, []);\n\n const getWaveform = useCallback((source?: AudioSource) => {\n return instance.current!.getWaveform(source);\n }, []);\n\n return {\n isPlaying: state.isPlaying,\n micActive: state.micActive,\n hasTrack: state.hasTrack,\n audioError: Boolean(state.loadError || state.micError),\n loadError: state.loadError,\n micError: state.micError,\n state,\n loadTrack,\n togglePlayPause,\n toggleMic,\n getBands,\n getCustomBands,\n getFftData,\n getWaveform,\n };\n}\n","import type { AudioBandsErrorCode, AudioBandsErrorKind } from './types';\n\nexport class AudioBandsError extends Error {\n readonly kind: AudioBandsErrorKind;\n readonly code: AudioBandsErrorCode;\n readonly cause?: unknown;\n\n constructor(\n kind: AudioBandsErrorKind,\n code: AudioBandsErrorCode,\n message: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = 'AudioBandsError';\n this.kind = kind;\n this.code = code;\n this.cause = cause;\n }\n}\n","import { AudioBandsError } from './errors';\nimport type {\n AudioAnalyserConfig,\n AudioBandsOptions,\n AudioBandsState,\n AudioSource,\n BandRange,\n Bands,\n ClassicBandRanges,\n CustomBandRanges,\n} from './types';\n\nconst DEFAULT_MUSIC_ANALYSER: Required<AudioAnalyserConfig> = {\n fftSize: 256,\n smoothingTimeConstant: 0.85,\n};\n\nconst DEFAULT_MIC_ANALYSER: Required<AudioAnalyserConfig> = {\n fftSize: 256,\n smoothingTimeConstant: 0.8,\n};\n\nconst DEFAULT_CLASSIC_RANGES: Record<keyof Omit<Bands, 'overall'>, BandRange> = {\n bass: { from: 0, to: 0.08 },\n mid: { from: 0.08, to: 0.4 },\n high: { from: 0.4, to: 1 },\n};\n\nconst ZERO: Bands = { bass: 0, mid: 0, high: 0, overall: 0 };\n\nfunction avg(arr: Uint8Array<ArrayBuffer>, from: number, to: number): number {\n let sum = 0;\n for (let i = from; i < to; i++) sum += arr[i];\n return sum / (to - from);\n}\n\nfunction isPowerOfTwo(value: number): boolean {\n return (value & (value - 1)) === 0;\n}\n\nfunction normalizeAnalyserConfig(\n config: AudioAnalyserConfig | undefined,\n fallback: Required<AudioAnalyserConfig>,\n): Required<AudioAnalyserConfig> {\n const fftSize = config?.fftSize ?? fallback.fftSize;\n const smoothingTimeConstant =\n config?.smoothingTimeConstant ?? fallback.smoothingTimeConstant;\n\n if (\n !Number.isInteger(fftSize) ||\n fftSize < 32 ||\n fftSize > 32768 ||\n !isPowerOfTwo(fftSize)\n ) {\n throw new AudioBandsError(\n 'config',\n 'invalid_config',\n 'fftSize must be a power of two between 32 and 32768',\n );\n }\n\n if (\n typeof smoothingTimeConstant !== 'number' ||\n smoothingTimeConstant < 0 ||\n smoothingTimeConstant > 1\n ) {\n throw new AudioBandsError(\n 'config',\n 'invalid_config',\n 'smoothingTimeConstant must be between 0 and 1',\n );\n }\n\n return { fftSize, smoothingTimeConstant };\n}\n\nfunction normalizeRange(name: string, range: BandRange | undefined): BandRange {\n const normalized = range ?? DEFAULT_CLASSIC_RANGES[name as keyof typeof DEFAULT_CLASSIC_RANGES];\n\n if (\n typeof normalized?.from !== 'number' ||\n typeof normalized?.to !== 'number' ||\n normalized.from < 0 ||\n normalized.to > 1 ||\n normalized.from >= normalized.to\n ) {\n throw new AudioBandsError(\n 'config',\n 'invalid_config',\n `Band range \"${name}\" must satisfy 0 <= from < to <= 1`,\n );\n }\n\n return normalized;\n}\n\nfunction normalizeClassicRanges(\n ranges: ClassicBandRanges | undefined,\n): Record<keyof Omit<Bands, 'overall'>, BandRange> {\n return {\n bass: normalizeRange('bass', ranges?.bass),\n mid: normalizeRange('mid', ranges?.mid),\n high: normalizeRange('high', ranges?.high),\n };\n}\n\nfunction normalizeCustomBands(customBands: CustomBandRanges | undefined): CustomBandRanges {\n if (!customBands) return {};\n\n return Object.fromEntries(\n Object.entries(customBands).map(([name, range]) => [name, normalizeRange(name, range)]),\n );\n}\n\nfunction getIndexes(len: number, range: BandRange): [number, number] {\n const from = Math.max(0, Math.min(len - 1, Math.floor(len * range.from)));\n const to = Math.max(from + 1, Math.min(len, Math.floor(len * range.to)));\n return [from, to];\n}\n\nfunction getRangeValue(data: Uint8Array<ArrayBuffer>, range: BandRange): number {\n const [from, to] = getIndexes(data.length, range);\n return avg(data, from, to) / 255;\n}\n\nfunction fillFrequencyData(\n analyser: AnalyserNode,\n data: Uint8Array<ArrayBuffer>,\n): Uint8Array<ArrayBuffer> {\n analyser.getByteFrequencyData(data);\n return data;\n}\n\nfunction computeBands(\n data: Uint8Array<ArrayBuffer>,\n ranges: Record<keyof Omit<Bands, 'overall'>, BandRange>,\n): Bands {\n const bass = getRangeValue(data, ranges.bass);\n const mid = getRangeValue(data, ranges.mid);\n const high = getRangeValue(data, ranges.high);\n\n return {\n bass,\n mid,\n high,\n overall: bass * 0.5 + mid * 0.3 + high * 0.2,\n };\n}\n\nfunction computeCustomBands(\n data: Uint8Array<ArrayBuffer>,\n ranges: CustomBandRanges,\n): Record<string, number> {\n return Object.fromEntries(\n Object.entries(ranges).map(([name, range]) => [name, getRangeValue(data, range)]),\n );\n}\n\nfunction cloneState(state: AudioBandsState): AudioBandsState {\n return { ...state };\n}\n\n/**\n * Vanilla JS class — no framework dependency.\n * Works in React, Vue, Svelte, or plain HTML.\n */\nexport class AudioBands {\n private options: AudioBandsOptions;\n private readonly musicConfig: Required<AudioAnalyserConfig>;\n private readonly micConfig: Required<AudioAnalyserConfig>;\n private readonly classicRanges: Record<keyof Omit<Bands, 'overall'>, BandRange>;\n private readonly customBandRanges: CustomBandRanges;\n\n private readonly state: AudioBandsState = {\n isPlaying: false,\n micActive: false,\n hasTrack: false,\n loadError: null,\n micError: null,\n };\n\n private ctx: AudioContext | null = null;\n private musicAnalyser: AnalyserNode | null = null;\n private musicData: Uint8Array<ArrayBuffer> | null = null;\n private musicWaveformData: Uint8Array<ArrayBuffer> | null = null;\n private micAnalyser: AnalyserNode | null = null;\n private micData: Uint8Array<ArrayBuffer> | null = null;\n private micWaveformData: Uint8Array<ArrayBuffer> | null = null;\n private audioEl: HTMLAudioElement | null = null;\n private musicSource: MediaElementAudioSourceNode | null = null;\n private micSource: MediaStreamAudioSourceNode | null = null;\n private micStream: MediaStream | null = null;\n private destroyed = false;\n\n constructor(options: AudioBandsOptions = {}) {\n this.options = options;\n this.musicConfig = normalizeAnalyserConfig(options.music, DEFAULT_MUSIC_ANALYSER);\n this.micConfig = normalizeAnalyserConfig(options.mic, DEFAULT_MIC_ANALYSER);\n this.classicRanges = normalizeClassicRanges(options.bandRanges);\n this.customBandRanges = normalizeCustomBands(options.customBands);\n }\n\n getState(): AudioBandsState {\n return cloneState(this.state);\n }\n\n getCustomBands(source: AudioSource = 'music'): Record<string, number> {\n const data = this.readFrequencyData(source);\n if (!data) return computeCustomBands(new Uint8Array(1) as Uint8Array<ArrayBuffer>, this.customBandRanges);\n return computeCustomBands(data, this.customBandRanges);\n }\n\n async load(url: string): Promise<void> {\n let ctx: AudioContext;\n try {\n ctx = this.ensureCtx();\n } catch (error) {\n throw this.handleError('load', error);\n }\n\n this.teardownMusic();\n\n const audio = new Audio();\n audio.crossOrigin = 'anonymous';\n audio.src = url;\n audio.loop = true;\n this.audioEl = audio;\n this.setState({ hasTrack: true, loadError: null });\n\n const source = ctx.createMediaElementSource(audio);\n source.connect(this.musicAnalyser!);\n this.musicSource = source;\n\n try {\n await audio.play();\n this.setState({ isPlaying: true, loadError: null });\n this.options.onPlay?.();\n } catch (error) {\n throw this.handleError('load', error, 'load_error');\n }\n }\n\n togglePlayPause(): void {\n const audio = this.audioEl;\n if (!audio) return;\n\n if (audio.paused) {\n void audio\n .play()\n .then(() => {\n this.setState({ isPlaying: true, loadError: null });\n this.options.onPlay?.();\n })\n .catch((error) => {\n this.handleError('load', error, 'playback_error');\n });\n return;\n }\n\n audio.pause();\n this.setState({ isPlaying: false });\n this.options.onPause?.();\n }\n\n async enableMic(): Promise<void> {\n let ctx: AudioContext;\n try {\n ctx = this.ensureCtx();\n } catch (error) {\n throw this.handleError('mic', error);\n }\n\n if (this.micStream) return;\n\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false,\n });\n this.micStream = stream;\n\n const analyser = this.createAnalyser(ctx, this.micConfig);\n this.micAnalyser = analyser;\n this.micData = new Uint8Array(\n analyser.frequencyBinCount,\n ) as Uint8Array<ArrayBuffer>;\n this.micWaveformData = new Uint8Array(\n analyser.fftSize,\n ) as Uint8Array<ArrayBuffer>;\n\n const source = ctx.createMediaStreamSource(stream);\n source.connect(analyser);\n this.micSource = source;\n\n this.setState({ micActive: true, micError: null });\n this.options.onMicStart?.();\n } catch (error) {\n throw this.handleError('mic', error, 'mic_error');\n }\n }\n\n disableMic(): void {\n const hadMic = Boolean(this.micStream || this.micSource || this.micAnalyser);\n this.micStream?.getTracks().forEach((track) => track.stop());\n this.micStream = null;\n\n try {\n this.micSource?.disconnect();\n } catch {\n /* already disconnected */\n }\n\n this.micSource = null;\n this.micAnalyser = null;\n this.micData = null;\n this.micWaveformData = null;\n this.setState({ micActive: false });\n\n if (hadMic) this.options.onMicStop?.();\n }\n\n getBands(source: AudioSource = 'music'): Bands {\n const data = this.readFrequencyData(source);\n if (!data) return { ...ZERO };\n return computeBands(data, this.classicRanges);\n }\n\n getFftData(source: AudioSource = 'music'): Uint8Array<ArrayBuffer> | null {\n return this.readFrequencyData(source);\n }\n\n getWaveform(source: AudioSource = 'music'): Uint8Array<ArrayBuffer> | null {\n return this.readWaveformData(source);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n\n this.teardownMusic();\n this.disableMic();\n void this.ctx?.close();\n this.ctx = null;\n this.musicAnalyser = null;\n this.musicData = null;\n this.musicWaveformData = null;\n this.setState({ isPlaying: false, micActive: false, hasTrack: false });\n this.options = {};\n this.destroyed = true;\n }\n\n private readFrequencyData(source: AudioSource): Uint8Array<ArrayBuffer> | null {\n if (source === 'mic') {\n if (!this.micAnalyser || !this.micData) return null;\n return fillFrequencyData(this.micAnalyser, this.micData);\n }\n\n if (!this.musicAnalyser || !this.musicData) return null;\n return fillFrequencyData(this.musicAnalyser, this.musicData);\n }\n\n private readWaveformData(source: AudioSource): Uint8Array<ArrayBuffer> | null {\n if (source === 'mic') {\n if (!this.micAnalyser || !this.micWaveformData) return null;\n this.micAnalyser.getByteTimeDomainData(this.micWaveformData);\n return this.micWaveformData;\n }\n\n if (!this.musicAnalyser || !this.musicWaveformData) return null;\n this.musicAnalyser.getByteTimeDomainData(this.musicWaveformData);\n return this.musicWaveformData;\n }\n\n private ensureCtx(): AudioContext {\n if (this.destroyed) {\n throw new AudioBandsError(\n 'lifecycle',\n 'destroyed',\n 'This AudioBands instance was destroyed',\n );\n }\n\n if (this.ctx) return this.ctx;\n\n const Ctx =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext })\n .webkitAudioContext;\n\n if (!Ctx) {\n throw new AudioBandsError(\n 'lifecycle',\n 'unsupported_audio_context',\n 'AudioContext is not supported in this environment',\n );\n }\n\n const ctx = new Ctx();\n const analyser = this.createAnalyser(ctx, this.musicConfig);\n analyser.connect(ctx.destination);\n\n this.ctx = ctx;\n this.musicAnalyser = analyser;\n this.musicData = new Uint8Array(\n analyser.frequencyBinCount,\n ) as Uint8Array<ArrayBuffer>;\n this.musicWaveformData = new Uint8Array(\n analyser.fftSize,\n ) as Uint8Array<ArrayBuffer>;\n\n return ctx;\n }\n\n private createAnalyser(\n ctx: AudioContext,\n config: Required<AudioAnalyserConfig>,\n ): AnalyserNode {\n const analyser = ctx.createAnalyser();\n analyser.fftSize = config.fftSize;\n analyser.smoothingTimeConstant = config.smoothingTimeConstant;\n return analyser;\n }\n\n private handleError(\n kind: 'load' | 'mic',\n error: unknown,\n fallbackCode: 'load_error' | 'playback_error' | 'mic_error' = kind === 'mic'\n ? 'mic_error'\n : 'load_error',\n ): AudioBandsError {\n const wrapped =\n error instanceof AudioBandsError\n ? error\n : new AudioBandsError(\n kind,\n fallbackCode,\n kind === 'mic'\n ? 'Failed to access microphone input'\n : 'Failed to load or play audio track',\n error,\n );\n\n if (kind === 'load') {\n this.setState({ isPlaying: false, loadError: wrapped });\n this.options.onLoadError?.(wrapped);\n } else {\n this.setState({ micActive: false, micError: wrapped });\n this.options.onMicError?.(wrapped);\n }\n\n this.options.onError?.(wrapped);\n return wrapped;\n }\n\n private setState(patch: Partial<AudioBandsState>): void {\n let changed = false;\n\n for (const [key, value] of Object.entries(patch) as Array<\n [keyof AudioBandsState, AudioBandsState[keyof AudioBandsState]]\n >) {\n if (this.state[key] !== value) {\n this.state[key] = value as never;\n changed = true;\n }\n }\n\n if (changed) this.options.onStateChange?.(this.getState());\n }\n\n private teardownMusic(): void {\n this.audioEl?.pause();\n if (this.audioEl) {\n this.audioEl.src = '';\n this.audioEl.load();\n }\n this.audioEl = null;\n\n try {\n this.musicSource?.disconnect();\n } catch {\n /* already disconnected */\n }\n\n this.musicSource = null;\n this.musicWaveformData = this.musicAnalyser\n ? new Uint8Array(this.musicAnalyser.fftSize) as Uint8Array<ArrayBuffer>\n : null;\n this.setState({ isPlaying: false, hasTrack: false });\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/react-entry.ts","../src/react.ts","../src/errors.ts","../src/core.ts"],"sourcesContent":["export { useAudioBands } from './react';\nexport { AudioBandsError } from './errors';\nexport type { UseAudioBandsReturn } from './react';\nexport type {\n AudioAnalyserConfig,\n AudioBandsCallbacks,\n AudioBandsErrorCode,\n AudioBandsErrorKind,\n AudioBandsOptions,\n AudioBandsSnapshot,\n AudioBandsState,\n AudioSource,\n BandRange,\n Bands,\n ClassicBandRanges,\n CustomBandRanges,\n} from './types';\n","'use client';\n\nimport { useRef, useState, useCallback, useEffect } from 'react';\nimport type { Dispatch, MutableRefObject, SetStateAction } from 'react';\nimport { AudioBands } from './core';\nimport type {\n AudioBandsOptions,\n AudioBandsSnapshot,\n AudioBandsState,\n AudioSource,\n Bands,\n} from './types';\nimport type { AudioBandsError } from './errors';\n\nexport type UseAudioBandsReturn = {\n isPlaying: boolean;\n micActive: boolean;\n hasTrack: boolean;\n audioError: boolean;\n loadError: AudioBandsError | null;\n playbackError: AudioBandsError | null;\n micError: AudioBandsError | null;\n state: AudioBandsState;\n loadTrack: (url: string) => Promise<void>;\n play: () => Promise<void>;\n pause: () => void;\n setLoop: (loop: boolean) => void;\n seek: (seconds: number) => void;\n getDuration: () => number | null;\n getCurrentTime: () => number | null;\n togglePlayPause: () => Promise<void>;\n toggleMic: () => Promise<void>;\n snapshot: (source?: AudioSource) => AudioBandsSnapshot;\n getBands: (source?: AudioSource) => Bands;\n getCustomBands: (source?: AudioSource) => Record<string, number>;\n getFftData: (source?: AudioSource) => Uint8Array<ArrayBuffer> | null;\n getWaveform: (source?: AudioSource) => Uint8Array<ArrayBuffer> | null;\n};\n\nconst INITIAL_STATE: AudioBandsState = {\n isPlaying: false,\n micActive: false,\n hasTrack: false,\n loadError: null,\n playbackError: null,\n micError: null,\n};\n\nfunction stableStringify(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableStringify(item)).join(',')}]`;\n }\n\n if (value && typeof value === 'object') {\n return `{${Object.entries(value as Record<string, unknown>)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, nestedValue]) => `${JSON.stringify(key)}:${stableStringify(nestedValue)}`)\n .join(',')}}`;\n }\n\n return JSON.stringify(value) ?? 'null';\n}\n\nfunction getStructuralOptionsKey(options: AudioBandsOptions): string {\n return stableStringify({\n music: options.music,\n mic: options.mic,\n bandRanges: options.bandRanges,\n customBands: options.customBands,\n });\n}\n\nfunction createAudioBandsInstance(\n options: AudioBandsOptions,\n latestOptions: MutableRefObject<AudioBandsOptions>,\n setState: Dispatch<SetStateAction<AudioBandsState>>,\n instanceRef: MutableRefObject<AudioBands | null>,\n): AudioBands {\n const next = new AudioBands({\n ...options,\n onPlay: () => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onPlay?.();\n },\n onPause: () => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onPause?.();\n },\n onError: (error) => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onError?.(error);\n },\n onLoadError: (error) => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onLoadError?.(error);\n },\n onPlaybackError: (error) => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onPlaybackError?.(error);\n },\n onMicError: (error) => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onMicError?.(error);\n },\n onMicStart: () => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onMicStart?.();\n },\n onMicStop: () => {\n if (instanceRef.current !== next) return;\n latestOptions.current.onMicStop?.();\n },\n onStateChange: (nextState) => {\n if (instanceRef.current !== next) return;\n setState(nextState);\n latestOptions.current.onStateChange?.(nextState);\n },\n });\n\n return next;\n}\n\n/**\n * React hook — thin wrapper over AudioBands.\n * Handles lifecycle (destroy on unmount) and exposes state for re-renders.\n */\nexport function useAudioBands(options: AudioBandsOptions = {}): UseAudioBandsReturn {\n const [state, setState] = useState<AudioBandsState>(INITIAL_STATE);\n const latestOptions = useRef(options);\n const instance = useRef<AudioBands | null>(null);\n const structuralOptionsKey = getStructuralOptionsKey(options);\n const structuralOptionsKeyRef = useRef(structuralOptionsKey);\n\n latestOptions.current = options;\n\n const getOrCreateInstance = (): AudioBands => {\n if (instance.current) return instance.current;\n\n const next = createAudioBandsInstance(options, latestOptions, setState, instance);\n instance.current = next;\n structuralOptionsKeyRef.current = structuralOptionsKey;\n return next;\n };\n\n getOrCreateInstance();\n\n useEffect(() => {\n const current = getOrCreateInstance();\n setState(current.getState());\n\n return () => {\n if (instance.current !== current) return;\n current.destroy();\n instance.current = null;\n };\n }, []);\n\n useEffect(() => {\n if (structuralOptionsKeyRef.current === structuralOptionsKey) return;\n\n const previous = instance.current;\n const next = createAudioBandsInstance(options, latestOptions, setState, instance);\n instance.current = next;\n structuralOptionsKeyRef.current = structuralOptionsKey;\n setState(next.getState());\n previous?.destroy();\n }, [options, structuralOptionsKey]);\n\n const loadTrack = useCallback(async (url: string) => {\n await getOrCreateInstance().load(url);\n }, []);\n\n const play = useCallback(async () => {\n await getOrCreateInstance().play();\n }, []);\n\n const pause = useCallback(() => {\n getOrCreateInstance().pause();\n }, []);\n\n const setLoop = useCallback((loop: boolean) => {\n getOrCreateInstance().setLoop(loop);\n }, []);\n\n const seek = useCallback((seconds: number) => {\n getOrCreateInstance().seek(seconds);\n }, []);\n\n const getDuration = useCallback(() => {\n return getOrCreateInstance().getDuration();\n }, []);\n\n const getCurrentTime = useCallback(() => {\n return getOrCreateInstance().getCurrentTime();\n }, []);\n\n const togglePlayPause = useCallback(async () => {\n await getOrCreateInstance().togglePlayPause();\n }, []);\n\n const toggleMic = useCallback(async () => {\n const current = getOrCreateInstance();\n if (current.getState().micActive) {\n current.disableMic();\n } else {\n await current.enableMic();\n }\n }, []);\n\n const snapshot = useCallback((source?: AudioSource) => {\n return getOrCreateInstance().snapshot(source);\n }, []);\n\n const getBands = useCallback((source?: AudioSource) => {\n return getOrCreateInstance().getBands(source);\n }, []);\n\n const getCustomBands = useCallback((source?: AudioSource) => {\n return getOrCreateInstance().getCustomBands(source);\n }, []);\n\n const getFftData = useCallback((source?: AudioSource) => {\n return getOrCreateInstance().getFftData(source);\n }, []);\n\n const getWaveform = useCallback((source?: AudioSource) => {\n return getOrCreateInstance().getWaveform(source);\n }, []);\n\n return {\n isPlaying: state.isPlaying,\n micActive: state.micActive,\n hasTrack: state.hasTrack,\n audioError: Boolean(state.loadError || state.playbackError || state.micError),\n loadError: state.loadError,\n playbackError: state.playbackError,\n micError: state.micError,\n state,\n loadTrack,\n play,\n pause,\n setLoop,\n seek,\n getDuration,\n getCurrentTime,\n togglePlayPause,\n toggleMic,\n snapshot,\n getBands,\n getCustomBands,\n getFftData,\n getWaveform,\n };\n}\n","import type { AudioBandsErrorCode, AudioBandsErrorKind } from './types';\n\nexport class AudioBandsError extends Error {\n readonly kind: AudioBandsErrorKind;\n readonly code: AudioBandsErrorCode;\n readonly cause?: unknown;\n\n constructor(\n kind: AudioBandsErrorKind,\n code: AudioBandsErrorCode,\n message: string,\n cause?: unknown,\n ) {\n super(message);\n this.name = 'AudioBandsError';\n this.kind = kind;\n this.code = code;\n this.cause = cause;\n }\n}\n","import { AudioBandsError } from './errors';\nimport type {\n AudioAnalyserConfig,\n AudioBandsOptions,\n AudioBandsSnapshot,\n AudioBandsState,\n AudioSource,\n BandRange,\n Bands,\n ClassicBandRanges,\n CustomBandRanges,\n} from './types';\n\nconst DEFAULT_MUSIC_ANALYSER: Required<AudioAnalyserConfig> = {\n fftSize: 256,\n smoothingTimeConstant: 0.85,\n};\n\nconst DEFAULT_MIC_ANALYSER: Required<AudioAnalyserConfig> = {\n fftSize: 256,\n smoothingTimeConstant: 0.8,\n};\n\nconst DEFAULT_CLASSIC_RANGES: Record<keyof Omit<Bands, 'overall'>, BandRange> = {\n bass: { from: 0, to: 0.08 },\n mid: { from: 0.08, to: 0.4 },\n high: { from: 0.4, to: 1 },\n};\n\nconst ZERO: Bands = { bass: 0, mid: 0, high: 0, overall: 0 };\n\nfunction avg(arr: Uint8Array<ArrayBuffer>, from: number, to: number): number {\n let sum = 0;\n for (let i = from; i < to; i++) sum += arr[i];\n return sum / (to - from);\n}\n\nfunction isPowerOfTwo(value: number): boolean {\n return (value & (value - 1)) === 0;\n}\n\nfunction normalizeAnalyserConfig(\n config: AudioAnalyserConfig | undefined,\n fallback: Required<AudioAnalyserConfig>,\n): Required<AudioAnalyserConfig> {\n const fftSize = config?.fftSize ?? fallback.fftSize;\n const smoothingTimeConstant =\n config?.smoothingTimeConstant ?? fallback.smoothingTimeConstant;\n\n if (\n !Number.isInteger(fftSize) ||\n fftSize < 32 ||\n fftSize > 32768 ||\n !isPowerOfTwo(fftSize)\n ) {\n throw new AudioBandsError(\n 'config',\n 'invalid_config',\n 'fftSize must be a power of two between 32 and 32768',\n );\n }\n\n if (\n typeof smoothingTimeConstant !== 'number' ||\n smoothingTimeConstant < 0 ||\n smoothingTimeConstant > 1\n ) {\n throw new AudioBandsError(\n 'config',\n 'invalid_config',\n 'smoothingTimeConstant must be between 0 and 1',\n );\n }\n\n return { fftSize, smoothingTimeConstant };\n}\n\nfunction normalizeRange(name: string, range: BandRange | undefined): BandRange {\n const normalized = range ?? DEFAULT_CLASSIC_RANGES[name as keyof typeof DEFAULT_CLASSIC_RANGES];\n\n if (\n typeof normalized?.from !== 'number' ||\n typeof normalized?.to !== 'number' ||\n normalized.from < 0 ||\n normalized.to > 1 ||\n normalized.from >= normalized.to\n ) {\n throw new AudioBandsError(\n 'config',\n 'invalid_config',\n `Band range \"${name}\" must satisfy 0 <= from < to <= 1`,\n );\n }\n\n return normalized;\n}\n\nfunction normalizeClassicRanges(\n ranges: ClassicBandRanges | undefined,\n): Record<keyof Omit<Bands, 'overall'>, BandRange> {\n return {\n bass: normalizeRange('bass', ranges?.bass),\n mid: normalizeRange('mid', ranges?.mid),\n high: normalizeRange('high', ranges?.high),\n };\n}\n\nfunction normalizeCustomBands(customBands: CustomBandRanges | undefined): CustomBandRanges {\n if (!customBands) return {};\n\n return Object.fromEntries(\n Object.entries(customBands).map(([name, range]) => [name, normalizeRange(name, range)]),\n );\n}\n\nfunction getIndexes(len: number, range: BandRange): [number, number] {\n const from = Math.max(0, Math.min(len - 1, Math.floor(len * range.from)));\n const to = Math.max(from + 1, Math.min(len, Math.floor(len * range.to)));\n return [from, to];\n}\n\nfunction getRangeValue(data: Uint8Array<ArrayBuffer>, range: BandRange): number {\n const [from, to] = getIndexes(data.length, range);\n return avg(data, from, to) / 255;\n}\n\nfunction fillFrequencyData(\n analyser: AnalyserNode,\n data: Uint8Array<ArrayBuffer>,\n): Uint8Array<ArrayBuffer> {\n analyser.getByteFrequencyData(data);\n return data;\n}\n\nfunction computeBands(\n data: Uint8Array<ArrayBuffer>,\n ranges: Record<keyof Omit<Bands, 'overall'>, BandRange>,\n): Bands {\n const bass = getRangeValue(data, ranges.bass);\n const mid = getRangeValue(data, ranges.mid);\n const high = getRangeValue(data, ranges.high);\n\n return {\n bass,\n mid,\n high,\n overall: bass * 0.5 + mid * 0.3 + high * 0.2,\n };\n}\n\nfunction computeCustomBands(\n data: Uint8Array<ArrayBuffer>,\n ranges: CustomBandRanges,\n): Record<string, number> {\n return Object.fromEntries(\n Object.entries(ranges).map(([name, range]) => [name, getRangeValue(data, range)]),\n );\n}\n\nfunction cloneState(state: AudioBandsState): AudioBandsState {\n return { ...state };\n}\n\n/**\n * Vanilla JS class — no framework dependency.\n * Works in React, Vue, Svelte, or plain HTML.\n */\nexport class AudioBands {\n private options: AudioBandsOptions;\n private readonly musicConfig: Required<AudioAnalyserConfig>;\n private readonly micConfig: Required<AudioAnalyserConfig>;\n private readonly classicRanges: Record<keyof Omit<Bands, 'overall'>, BandRange>;\n private readonly customBandRanges: CustomBandRanges;\n\n private readonly state: AudioBandsState = {\n isPlaying: false,\n micActive: false,\n hasTrack: false,\n loadError: null,\n playbackError: null,\n micError: null,\n };\n\n private ctx: AudioContext | null = null;\n private musicAnalyser: AnalyserNode | null = null;\n private musicData: Uint8Array<ArrayBuffer> | null = null;\n private musicWaveformData: Uint8Array<ArrayBuffer> | null = null;\n private micAnalyser: AnalyserNode | null = null;\n private micData: Uint8Array<ArrayBuffer> | null = null;\n private micWaveformData: Uint8Array<ArrayBuffer> | null = null;\n private audioEl: HTMLAudioElement | null = null;\n private musicSource: MediaElementAudioSourceNode | null = null;\n private micSource: MediaStreamAudioSourceNode | null = null;\n private micStream: MediaStream | null = null;\n private musicEventCleanup: (() => void) | null = null;\n private pendingLoadCleanup: (() => void) | null = null;\n private pendingLoadReject: ((error: AudioBandsError) => void) | null = null;\n private trackLoop = false;\n private destroyed = false;\n\n constructor(options: AudioBandsOptions = {}) {\n this.options = options;\n this.musicConfig = normalizeAnalyserConfig(options.music, DEFAULT_MUSIC_ANALYSER);\n this.micConfig = normalizeAnalyserConfig(options.mic, DEFAULT_MIC_ANALYSER);\n this.classicRanges = normalizeClassicRanges(options.bandRanges);\n this.customBandRanges = normalizeCustomBands(options.customBands);\n }\n\n getState(): AudioBandsState {\n return cloneState(this.state);\n }\n\n getCustomBands(source: AudioSource = 'music'): Record<string, number> {\n const data = this.readFrequencyData(source);\n if (!data) return computeCustomBands(new Uint8Array(1) as Uint8Array<ArrayBuffer>, this.customBandRanges);\n return computeCustomBands(data, this.customBandRanges);\n }\n\n async load(url: string): Promise<void> {\n let ctx: AudioContext;\n try {\n ctx = this.ensureCtx();\n } catch (error) {\n throw this.handleError('load', error, 'load_error');\n }\n\n this.teardownMusic();\n\n const audio = new Audio();\n audio.crossOrigin = 'anonymous';\n audio.preload = 'auto';\n audio.src = url;\n audio.loop = this.trackLoop;\n this.bindMusicElementEvents(audio);\n this.audioEl = audio;\n this.setState({ hasTrack: false, loadError: null, playbackError: null });\n\n const source = ctx.createMediaElementSource(audio);\n source.connect(this.musicAnalyser!);\n this.musicSource = source;\n\n await new Promise<void>((resolve, reject) => {\n const finalize = () => {\n cleanup();\n this.pendingLoadCleanup = null;\n this.pendingLoadReject = null;\n };\n\n const handleReady = () => {\n finalize();\n this.setState({ hasTrack: true, loadError: null });\n resolve();\n };\n\n const handleFailure = (event?: Event) => {\n finalize();\n const mediaError =\n (event?.target as HTMLMediaElement | null)?.error ??\n audio.error ??\n event;\n reject(this.handleLoadFailure(mediaError));\n };\n\n const cleanup = () => {\n audio.removeEventListener('loadedmetadata', handleReady);\n audio.removeEventListener('canplay', handleReady);\n audio.removeEventListener('error', handleFailure);\n };\n\n this.pendingLoadCleanup = cleanup;\n this.pendingLoadReject = reject;\n\n audio.addEventListener('loadedmetadata', handleReady, { once: true });\n audio.addEventListener('canplay', handleReady, { once: true });\n audio.addEventListener('error', handleFailure, { once: true });\n audio.load();\n });\n }\n\n async play(): Promise<void> {\n const audio = this.audioEl;\n if (!audio) return;\n\n try {\n await audio.play();\n this.setState({ playbackError: null });\n } catch (error) {\n throw this.handleError('playback', error, 'playback_error');\n }\n }\n\n pause(): void {\n const audio = this.audioEl;\n if (!audio || audio.paused) return;\n\n audio.pause();\n }\n\n async togglePlayPause(): Promise<void> {\n const audio = this.audioEl;\n if (!audio) return;\n\n if (audio.paused) {\n await this.play();\n return;\n }\n\n this.pause();\n }\n\n setLoop(loop: boolean): void {\n this.trackLoop = loop;\n if (this.audioEl) this.audioEl.loop = loop;\n }\n\n seek(seconds: number): void {\n if (!Number.isFinite(seconds) || seconds < 0) {\n throw new AudioBandsError(\n 'config',\n 'invalid_config',\n 'seek time must be a finite number greater than or equal to 0',\n );\n }\n\n if (!this.audioEl) return;\n\n const duration = Number.isFinite(this.audioEl.duration) ? this.audioEl.duration : null;\n this.audioEl.currentTime = duration === null ? seconds : Math.min(seconds, duration);\n }\n\n getDuration(): number | null {\n if (!this.audioEl) return null;\n return Number.isFinite(this.audioEl.duration) ? this.audioEl.duration : null;\n }\n\n getCurrentTime(): number | null {\n if (!this.audioEl) return null;\n return Number.isFinite(this.audioEl.currentTime) ? this.audioEl.currentTime : null;\n }\n\n async enableMic(): Promise<void> {\n let ctx: AudioContext;\n try {\n ctx = this.ensureCtx();\n } catch (error) {\n throw this.handleError('mic', error);\n }\n\n if (this.micStream) return;\n\n try {\n const stream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false,\n });\n this.micStream = stream;\n\n const analyser = this.createAnalyser(ctx, this.micConfig);\n this.micAnalyser = analyser;\n this.micData = new Uint8Array(\n analyser.frequencyBinCount,\n ) as Uint8Array<ArrayBuffer>;\n this.micWaveformData = new Uint8Array(\n analyser.fftSize,\n ) as Uint8Array<ArrayBuffer>;\n\n const source = ctx.createMediaStreamSource(stream);\n source.connect(analyser);\n this.micSource = source;\n\n this.setState({ micActive: true, micError: null });\n this.options.onMicStart?.();\n } catch (error) {\n throw this.handleError('mic', error, 'mic_error');\n }\n }\n\n disableMic(): void {\n const hadMic = Boolean(this.micStream || this.micSource || this.micAnalyser);\n this.micStream?.getTracks().forEach((track) => track.stop());\n this.micStream = null;\n\n try {\n this.micSource?.disconnect();\n } catch {\n /* already disconnected */\n }\n\n this.micSource = null;\n this.micAnalyser = null;\n this.micData = null;\n this.micWaveformData = null;\n this.setState({ micActive: false });\n\n if (hadMic) this.options.onMicStop?.();\n }\n\n getBands(source: AudioSource = 'music'): Bands {\n const data = this.readFrequencyData(source);\n if (!data) return { ...ZERO };\n return computeBands(data, this.classicRanges);\n }\n\n getFftData(source: AudioSource = 'music'): Uint8Array<ArrayBuffer> | null {\n return this.readFrequencyData(source);\n }\n\n getWaveform(source: AudioSource = 'music'): Uint8Array<ArrayBuffer> | null {\n return this.readWaveformData(source);\n }\n\n snapshot(source: AudioSource = 'music'): AudioBandsSnapshot {\n const fft = this.readFrequencyData(source);\n const waveform = this.readWaveformData(source);\n\n return {\n bands: fft ? computeBands(fft, this.classicRanges) : { ...ZERO },\n customBands: fft\n ? computeCustomBands(fft, this.customBandRanges)\n : computeCustomBands(new Uint8Array(1) as Uint8Array<ArrayBuffer>, this.customBandRanges),\n fft,\n waveform,\n };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n\n this.teardownMusic();\n this.disableMic();\n void this.ctx?.close();\n this.ctx = null;\n this.musicAnalyser = null;\n this.musicData = null;\n this.musicWaveformData = null;\n this.setState({ isPlaying: false, micActive: false, hasTrack: false });\n this.options = {};\n this.destroyed = true;\n }\n\n private readFrequencyData(source: AudioSource): Uint8Array<ArrayBuffer> | null {\n if (source === 'mic') {\n if (!this.micAnalyser || !this.micData) return null;\n return fillFrequencyData(this.micAnalyser, this.micData);\n }\n\n if (!this.musicAnalyser || !this.musicData) return null;\n return fillFrequencyData(this.musicAnalyser, this.musicData);\n }\n\n private readWaveformData(source: AudioSource): Uint8Array<ArrayBuffer> | null {\n if (source === 'mic') {\n if (!this.micAnalyser || !this.micWaveformData) return null;\n this.micAnalyser.getByteTimeDomainData(this.micWaveformData);\n return this.micWaveformData;\n }\n\n if (!this.musicAnalyser || !this.musicWaveformData) return null;\n this.musicAnalyser.getByteTimeDomainData(this.musicWaveformData);\n return this.musicWaveformData;\n }\n\n private ensureCtx(): AudioContext {\n if (this.destroyed) {\n throw new AudioBandsError(\n 'lifecycle',\n 'destroyed',\n 'This AudioBands instance was destroyed',\n );\n }\n\n if (this.ctx) return this.ctx;\n\n const Ctx =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext?: typeof AudioContext })\n .webkitAudioContext;\n\n if (!Ctx) {\n throw new AudioBandsError(\n 'lifecycle',\n 'unsupported_audio_context',\n 'AudioContext is not supported in this environment',\n );\n }\n\n const ctx = new Ctx();\n const analyser = this.createAnalyser(ctx, this.musicConfig);\n analyser.connect(ctx.destination);\n\n this.ctx = ctx;\n this.musicAnalyser = analyser;\n this.musicData = new Uint8Array(\n analyser.frequencyBinCount,\n ) as Uint8Array<ArrayBuffer>;\n this.musicWaveformData = new Uint8Array(\n analyser.fftSize,\n ) as Uint8Array<ArrayBuffer>;\n\n return ctx;\n }\n\n private createAnalyser(\n ctx: AudioContext,\n config: Required<AudioAnalyserConfig>,\n ): AnalyserNode {\n const analyser = ctx.createAnalyser();\n analyser.fftSize = config.fftSize;\n analyser.smoothingTimeConstant = config.smoothingTimeConstant;\n return analyser;\n }\n\n private handleError(\n kind: 'load' | 'playback' | 'mic',\n error: unknown,\n fallbackCode: 'load_error' | 'playback_error' | 'mic_error' = kind === 'mic'\n ? 'mic_error'\n : kind === 'playback'\n ? 'playback_error'\n : 'load_error',\n ): AudioBandsError {\n const wrapped =\n error instanceof AudioBandsError\n ? error\n : new AudioBandsError(\n kind,\n fallbackCode,\n kind === 'mic'\n ? 'Failed to access microphone input'\n : kind === 'playback'\n ? 'Failed to play audio track'\n : 'Failed to load audio track',\n error,\n );\n\n if (kind === 'load') {\n this.setState({ isPlaying: false, loadError: wrapped });\n this.options.onLoadError?.(wrapped);\n } else if (kind === 'playback') {\n this.setState({ isPlaying: false, playbackError: wrapped });\n this.options.onPlaybackError?.(wrapped);\n } else {\n this.setState({ micActive: false, micError: wrapped });\n this.options.onMicError?.(wrapped);\n }\n\n this.options.onError?.(wrapped);\n return wrapped;\n }\n\n private bindMusicElementEvents(audio: HTMLAudioElement): void {\n this.musicEventCleanup?.();\n\n const handlePlay = () => {\n if (this.audioEl !== audio) return;\n this.setState({ isPlaying: true, playbackError: null });\n this.options.onPlay?.();\n };\n\n const handlePause = () => {\n if (this.audioEl !== audio) return;\n this.setState({ isPlaying: false });\n this.options.onPause?.();\n };\n\n const handleEnded = () => {\n if (this.audioEl !== audio) return;\n this.setState({ isPlaying: false });\n };\n\n audio.addEventListener('play', handlePlay);\n audio.addEventListener('pause', handlePause);\n audio.addEventListener('ended', handleEnded);\n\n this.musicEventCleanup = () => {\n audio.removeEventListener('play', handlePlay);\n audio.removeEventListener('pause', handlePause);\n audio.removeEventListener('ended', handleEnded);\n if (this.musicEventCleanup) this.musicEventCleanup = null;\n };\n }\n\n private handleLoadFailure(error: unknown): AudioBandsError {\n this.teardownMusic();\n return this.handleError('load', error, 'load_error');\n }\n\n private setState(patch: Partial<AudioBandsState>): void {\n let changed = false;\n\n for (const [key, value] of Object.entries(patch) as Array<\n [keyof AudioBandsState, AudioBandsState[keyof AudioBandsState]]\n >) {\n if (this.state[key] !== value) {\n this.state[key] = value as never;\n changed = true;\n }\n }\n\n if (changed) this.options.onStateChange?.(this.getState());\n }\n\n private teardownMusic(): void {\n this.musicEventCleanup?.();\n if (this.pendingLoadReject) {\n const reject = this.pendingLoadReject;\n this.pendingLoadReject = null;\n this.pendingLoadCleanup?.();\n this.pendingLoadCleanup = null;\n reject(\n new AudioBandsError(\n 'load',\n 'load_error',\n 'Audio track loading was interrupted before completion',\n ),\n );\n } else if (this.pendingLoadCleanup) {\n this.pendingLoadCleanup();\n this.pendingLoadCleanup = null;\n }\n this.audioEl?.pause();\n if (this.audioEl) {\n this.audioEl.src = '';\n this.audioEl.load();\n }\n this.audioEl = null;\n\n try {\n this.musicSource?.disconnect();\n } catch {\n /* already disconnected */\n }\n\n this.musicSource = null;\n this.musicWaveformData = this.musicAnalyser\n ? new Uint8Array(this.musicAnalyser.fftSize) as Uint8Array<ArrayBuffer>\n : null;\n this.setState({\n isPlaying: false,\n hasTrack: false,\n playbackError: null,\n });\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
- import { A as AudioBandsError, a as AudioBandsState, b as AudioSource, B as Bands, c as AudioBandsOptions } from './types-CiYwsfgy.cjs';
2
- export { d as AudioAnalyserConfig, e as AudioBandsCallbacks, f as AudioBandsErrorCode, g as AudioBandsErrorKind, h as BandRange, C as ClassicBandRanges, i as CustomBandRanges } from './types-CiYwsfgy.cjs';
1
+ import { A as AudioBandsError, a as AudioBandsState, b as AudioSource, c as AudioBandsSnapshot, B as Bands, d as AudioBandsOptions } from './types-0KQJLMV2.cjs';
2
+ export { e as AudioAnalyserConfig, f as AudioBandsCallbacks, g as AudioBandsErrorCode, h as AudioBandsErrorKind, i as BandRange, C as ClassicBandRanges, j as CustomBandRanges } from './types-0KQJLMV2.cjs';
3
3
 
4
4
  type UseAudioBandsReturn = {
5
5
  isPlaying: boolean;
@@ -7,11 +7,19 @@ type UseAudioBandsReturn = {
7
7
  hasTrack: boolean;
8
8
  audioError: boolean;
9
9
  loadError: AudioBandsError | null;
10
+ playbackError: AudioBandsError | null;
10
11
  micError: AudioBandsError | null;
11
12
  state: AudioBandsState;
12
13
  loadTrack: (url: string) => Promise<void>;
13
- togglePlayPause: () => void;
14
+ play: () => Promise<void>;
15
+ pause: () => void;
16
+ setLoop: (loop: boolean) => void;
17
+ seek: (seconds: number) => void;
18
+ getDuration: () => number | null;
19
+ getCurrentTime: () => number | null;
20
+ togglePlayPause: () => Promise<void>;
14
21
  toggleMic: () => Promise<void>;
22
+ snapshot: (source?: AudioSource) => AudioBandsSnapshot;
15
23
  getBands: (source?: AudioSource) => Bands;
16
24
  getCustomBands: (source?: AudioSource) => Record<string, number>;
17
25
  getFftData: (source?: AudioSource) => Uint8Array<ArrayBuffer> | null;
@@ -23,4 +31,4 @@ type UseAudioBandsReturn = {
23
31
  */
24
32
  declare function useAudioBands(options?: AudioBandsOptions): UseAudioBandsReturn;
25
33
 
26
- export { AudioBandsError, AudioBandsOptions, AudioBandsState, AudioSource, Bands, type UseAudioBandsReturn, useAudioBands };
34
+ export { AudioBandsError, AudioBandsOptions, AudioBandsSnapshot, AudioBandsState, AudioSource, Bands, type UseAudioBandsReturn, useAudioBands };