cacophony 0.18.3 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +50 -38
  2. package/dist/basePlayback.d.ts +15 -15
  3. package/dist/cache.d.ts +3 -3
  4. package/dist/cacophony.d.ts +67 -12
  5. package/dist/container.d.ts +1 -0
  6. package/dist/context.d.ts +129 -12
  7. package/dist/events.d.ts +19 -19
  8. package/dist/filters.d.ts +1 -0
  9. package/dist/group.d.ts +2 -2
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.mjs +942 -854
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/microphone.d.ts +5 -4
  16. package/dist/oscillatorMixin.d.ts +8 -8
  17. package/dist/pannerMixin.d.ts +27 -28
  18. package/dist/playback.d.ts +6 -2
  19. package/dist/sound.d.ts +8 -5
  20. package/dist/stream.d.ts +2 -2
  21. package/dist/synth.d.ts +9 -6
  22. package/dist/synthGroup.d.ts +24 -3
  23. package/dist/synthPlayback.d.ts +15 -10
  24. package/dist/volumeMixin.d.ts +11 -11
  25. package/docs/assets/navigation.js +1 -1
  26. package/docs/assets/search.js +1 -1
  27. package/docs/classes/AudioCache.html +61 -0
  28. package/docs/classes/Cacophony.html +35 -15
  29. package/docs/classes/Group.html +13 -14
  30. package/docs/classes/MicrophonePlayback.html +3 -3
  31. package/docs/classes/Playback.html +41 -46
  32. package/docs/classes/Sound.html +24 -21
  33. package/docs/classes/Synth.html +21 -26
  34. package/docs/classes/SynthGroup.html +17 -5
  35. package/docs/enums/SoundType.html +2 -2
  36. package/docs/hierarchy.html +1 -1
  37. package/docs/index.html +12 -11
  38. package/docs/interfaces/AudioBuffer.html +8 -0
  39. package/docs/interfaces/AudioBufferSourceNode.html +32 -0
  40. package/docs/interfaces/AudioEventCallbacks.html +2 -2
  41. package/docs/interfaces/AudioListener.html +10 -0
  42. package/docs/interfaces/AudioNode.html +24 -0
  43. package/docs/interfaces/AudioParam.html +10 -0
  44. package/docs/interfaces/BaseAudioEvents.html +2 -2
  45. package/docs/interfaces/BaseContext.html +20 -0
  46. package/docs/interfaces/BaseSound.html +2 -2
  47. package/docs/interfaces/BiquadFilterNode.html +30 -0
  48. package/docs/interfaces/CacheErrorEvent.html +2 -2
  49. package/docs/interfaces/CacheHitEvent.html +2 -2
  50. package/docs/interfaces/CacheMissEvent.html +2 -2
  51. package/docs/interfaces/CacophonyEvents.html +2 -2
  52. package/docs/interfaces/FadeStartEvent.html +2 -2
  53. package/docs/interfaces/GainNode.html +25 -0
  54. package/docs/interfaces/GlobalPlaybackEvent.html +2 -2
  55. package/docs/interfaces/LoadingCompleteEvent.html +2 -2
  56. package/docs/interfaces/LoadingErrorEvent.html +2 -2
  57. package/docs/interfaces/LoadingProgressEvent.html +2 -2
  58. package/docs/interfaces/LoadingStartEvent.html +2 -2
  59. package/docs/interfaces/MediaElementSourceNode.html +25 -0
  60. package/docs/interfaces/MediaStreamAudioSourceNode.html +25 -0
  61. package/docs/interfaces/OfflineOptions.html +6 -0
  62. package/docs/interfaces/OscillatorNode.html +30 -0
  63. package/docs/interfaces/PannerNode.html +38 -0
  64. package/docs/interfaces/PlayOptions.html +2 -2
  65. package/docs/interfaces/PlaybackErrorEvent.html +2 -2
  66. package/docs/interfaces/PlaybackEvents.html +2 -2
  67. package/docs/interfaces/RuntimeOptions.html +2 -0
  68. package/docs/interfaces/SoundCleanupHoldings.html +9 -0
  69. package/docs/interfaces/SoundErrorEvent.html +2 -2
  70. package/docs/interfaces/SoundEvents.html +2 -2
  71. package/docs/interfaces/StereoPannerNode.html +25 -0
  72. package/docs/interfaces/SynthEvents.html +2 -2
  73. package/docs/modules.html +20 -2
  74. package/docs/types/CacheEventCallback.html +1 -1
  75. package/docs/types/ErrorEventCallback.html +1 -1
  76. package/docs/types/FadeType.html +1 -1
  77. package/docs/types/LoadingEventCallback.html +1 -1
  78. package/docs/types/LoopCount.html +1 -1
  79. package/docs/types/Orientation.html +1 -1
  80. package/docs/types/PanType.html +1 -1
  81. package/docs/types/Position.html +1 -1
  82. package/docs/types/SourceNode.html +1 -0
  83. package/package.json +83 -70
package/README.md CHANGED
@@ -13,7 +13,7 @@ Cacophony is a powerful and intuitive audio library designed for modern web appl
13
13
  - **Synthesizer Integration**: Create and manipulate synthesized sounds with customizable oscillator options
14
14
  - **Efficient Group Management**: Organize and control multiple sounds or synthesizers as groups for streamlined audio management
15
15
  - **Live Microphone Input**: Capture and process real-time audio input from the user's microphone
16
- - **Audio Streaming**: Support for streaming audio directly from URLs
16
+ - **Network-Backed Playback**: Play audio directly from URLs using media-element-backed sounds
17
17
  - **Flexible Caching**: Implement efficient audio caching strategies for improved performance
18
18
 
19
19
  ## Installation
@@ -92,13 +92,13 @@ sound.stop(); // Stops all three playbacks
92
92
 
93
93
  ## Sound Types
94
94
 
95
- Cacophony supports three sound types, each optimized for different use cases:
95
+ Cacophony supports three sound types:
96
96
 
97
- | Type | Memory | Latency | Seeking | Multiple Instances | Best For |
98
- |------|--------|---------|---------|-------------------|----------|
99
- | **Buffer** (default) | High | None | Full | Yes | Sound effects, UI sounds, short music clips |
100
- | **HTML** | Medium | Low | Full | Limited | Background music, large audio files, podcasts |
101
- | **Streaming** | Low | Medium | Limited | No | Internet radio, live streams, very large files |
97
+ | Type | Memory | Latency | Seeking | Multiple Instances | Best For |
98
+ |------|--------|---------|---------|-------------------|----------|
99
+ | **Buffer** (default) | High | None | Full | Yes | Sound effects, UI sounds, short music clips |
100
+ | **HTML** | Medium | Low | Full | Yes | Background music, large audio files, podcasts |
101
+ | **Streaming** | Medium | Low | Full | Yes | Network-backed playback created via `createStream()` |
102
102
 
103
103
  ```typescript
104
104
  // Buffer - entire file loaded into memory
@@ -107,8 +107,8 @@ const sfx = await cacophony.createSound('explosion.mp3', SoundType.Buffer);
107
107
  // HTML - streams from network, good for large files
108
108
  const music = await cacophony.createSound('bgm.mp3', SoundType.HTML);
109
109
 
110
- // Streaming - for live streams
111
- const radio = await cacophony.createStream('https://example.com/stream.m3u8');
110
+ // Streaming - convenience helper for network-backed playback
111
+ const radio = await cacophony.createStream('https://example.com/stream.m3u8');
112
112
  ```
113
113
 
114
114
  ## Playback Control
@@ -441,22 +441,28 @@ const footsteps = await cacophony.createGroupFromUrls([
441
441
 
442
442
  footsteps.playRandom(); // Picks one at random
443
443
 
444
- // Play sounds in sequence
445
- const dialog = await cacophony.createGroupFromUrls(['line1.mp3', 'line2.mp3', 'line3.mp3']);
446
- dialog.playOrdered(true); // Plays: 1, 2, 3, 1, 2, 3...
447
-
448
- // SynthGroup for synthesizers
449
- const synthGroup = new SynthGroup();
450
- const synth1 = cacophony.createOscillator({ frequency: 440, type: 'sine' });
451
- const synth2 = cacophony.createOscillator({ frequency: 660, type: 'square' });
452
- synthGroup.addSynth(synth1);
453
- synthGroup.addSynth(synth2);
454
- synthGroup.play();
455
- synthGroup.setVolume(0.5);
456
-
457
- // Remove a synth from the group
458
- synthGroup.removeSynth(synth1);
459
- ```
444
+ // Advance through sounds in sequence, one call at a time
445
+ const dialog = await cacophony.createGroupFromUrls(['line1.mp3', 'line2.mp3', 'line3.mp3']);
446
+ dialog.playOrdered(true); // Plays line1, then advances internal order
447
+ dialog.playOrdered(true); // Plays line2
448
+ dialog.playOrdered(true); // Plays line3
449
+ dialog.playOrdered(true); // Plays line1 again because looping is enabled
450
+
451
+ // SynthGroup for synthesizers
452
+ const synthGroup = new SynthGroup();
453
+ const synth1 = cacophony.createOscillator({ frequency: 440, type: 'sine' });
454
+ const synth2 = cacophony.createOscillator({ frequency: 660, type: 'square' });
455
+ synthGroup.addSynth(synth1);
456
+ synthGroup.addSynth(synth2);
457
+ synthGroup.play();
458
+ synthGroup.volume = 0.5;
459
+ synthGroup.type = 'triangle';
460
+ synthGroup.pause();
461
+ synthGroup.resume();
462
+
463
+ // Remove a synth from the group
464
+ synthGroup.removeSynth(synth1);
465
+ ```
460
466
 
461
467
  ## Synthesizer Functionality
462
468
 
@@ -483,16 +489,22 @@ bass.addFilter(cacophony.createBiquadFilter({ type: 'lowpass', frequency: 1000 }
483
489
  bass.play();
484
490
  lead.play();
485
491
 
486
- // Modulate frequency over time
487
- let time = 0;
488
- setInterval(() => {
489
- const frequency = 440 + Math.sin(time) * 100;
490
- sineOsc.frequency = frequency;
491
- time += 0.1;
492
- }, 50);
493
- ```
494
-
495
- ## 3D Audio Positioning
492
+ // Modulate frequency over time
493
+ let time = 0;
494
+ setInterval(() => {
495
+ const frequency = 440 + Math.sin(time) * 100;
496
+ sineOsc.frequency = frequency;
497
+ time += 0.1;
498
+ }, 50);
499
+
500
+ // Pause and resume the active synth playback without losing its settings
501
+ sineOsc.pause();
502
+ sineOsc.resume();
503
+ ```
504
+
505
+ `synth.pause()` keeps the existing synth playback object so `synth.resume()` can restart it with the current frequency, detune, type, volume, pan, and filter settings. `synth.play()` still creates a fresh playback instance, just like `Sound.play()`.
506
+
507
+ ## 3D Audio Positioning
496
508
 
497
509
  Create immersive soundscapes with precise spatial audio control using HRTF (Head-Related Transfer Function) or stereo panning.
498
510
 
@@ -857,9 +869,9 @@ const group = await cacophony.createGroupFromUrls(
857
869
  Call `cleanup()` when done with sounds to free resources:
858
870
 
859
871
  ```typescript
860
- const sound = await cacophony.createSound('temp.mp3');
861
- sound.play();
862
- sound.cleanup(); // Disconnects nodes, removes playbacks, clears listeners
872
+ const sound = await cacophony.createSound('temp.mp3');
873
+ sound.play();
874
+ sound.cleanup(); // Tears down playbacks, including pausing and resetting active HTML/streaming media
863
875
 
864
876
  // Clear memory cache
865
877
  cacophony.clearMemoryCache();
@@ -1,14 +1,14 @@
1
1
  import { FadeType } from './cacophony';
2
2
  import { IPlaybackContainer } from './container';
3
3
  import { AudioNode } from './context';
4
- import { FilterManager } from './filters';
5
4
  import { TypedEventEmitter } from './eventEmitter';
6
5
  import { PlaybackEvents } from './events';
6
+ import { FilterManager } from './filters';
7
7
  declare const BasePlayback_base: (abstract new (...args: any[]) => {
8
8
  panner?: import('./context').PannerNode | import('./context').StereoPannerNode | undefined;
9
9
  _panType: import('./cacophony').PanType;
10
10
  readonly panType: import('./cacophony').PanType;
11
- setPanType(panType: import('./cacophony').PanType, audioContext: import('standardized-audio-context').IAudioContext): void;
11
+ setPanType(panType: import('./cacophony').PanType, audioContext: import('./context').BaseContext): void;
12
12
  setPannerNode(pannerNode: import('./context').PannerNode): void;
13
13
  get stereoPan(): number | null;
14
14
  set stereoPan(value: number);
@@ -16,13 +16,13 @@ declare const BasePlayback_base: (abstract new (...args: any[]) => {
16
16
  set threeDOptions(options: Partial<PannerOptions>);
17
17
  position: import('./cacophony').Position;
18
18
  cleanup(): void;
19
- _filters: BiquadFilterNode[];
20
- addFilter(filter: BiquadFilterNode): void;
21
- removeFilter(filter: BiquadFilterNode): void;
19
+ _filters: import('./context').BiquadFilterNode[];
20
+ addFilter(filter: import('./context').BiquadFilterNode): void;
21
+ removeFilter(filter: import('./context').BiquadFilterNode): void;
22
22
  applyFilters(connection: any): any;
23
- readonly filters: BiquadFilterNode[];
24
- addFilters(filters: BiquadFilterNode[]): void;
25
- removeFilters(filters: BiquadFilterNode[]): void;
23
+ readonly filters: import('./context').BiquadFilterNode[];
24
+ addFilters(filters: import('./context').BiquadFilterNode[]): void;
25
+ removeFilters(filters: import('./context').BiquadFilterNode[]): void;
26
26
  }) & (abstract new (...args: any[]) => {
27
27
  gainNode?: import('./context').GainNode | undefined;
28
28
  _fadeTimeout?: NodeJS.Timeout | undefined;
@@ -35,13 +35,13 @@ declare const BasePlayback_base: (abstract new (...args: any[]) => {
35
35
  cancelFade(): void;
36
36
  fadeIn(duration: number, type?: FadeType | undefined): Promise<void>;
37
37
  fadeOut(duration: number, type?: FadeType | undefined): Promise<void>;
38
- _filters: BiquadFilterNode[];
39
- addFilter(filter: BiquadFilterNode): void;
40
- removeFilter(filter: BiquadFilterNode): void;
38
+ _filters: import('./context').BiquadFilterNode[];
39
+ addFilter(filter: import('./context').BiquadFilterNode): void;
40
+ removeFilter(filter: import('./context').BiquadFilterNode): void;
41
41
  applyFilters(connection: any): any;
42
- readonly filters: BiquadFilterNode[];
43
- addFilters(filters: BiquadFilterNode[]): void;
44
- removeFilters(filters: BiquadFilterNode[]): void;
42
+ readonly filters: import('./context').BiquadFilterNode[];
43
+ addFilters(filters: import('./context').BiquadFilterNode[]): void;
44
+ removeFilters(filters: import('./context').BiquadFilterNode[]): void;
45
45
  }) & typeof FilterManager;
46
46
  export declare abstract class BasePlayback extends BasePlayback_base {
47
47
  source?: AudioNode;
@@ -61,7 +61,7 @@ export declare abstract class BasePlayback extends BasePlayback_base {
61
61
  * Register event listener.
62
62
  * @returns Cleanup function
63
63
  */
64
- on<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): void;
64
+ on<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): () => void;
65
65
  /**
66
66
  * Remove event listener.
67
67
  */
package/dist/cache.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { AudioContext } from './context';
1
+ import { BaseContext } from './context';
2
2
  export interface ICache {
3
- getAudioBuffer(context: AudioContext, url: string, signal?: AbortSignal, callbacks?: {
3
+ getAudioBuffer(context: BaseContext, url: string, signal?: AbortSignal, callbacks?: {
4
4
  onLoadingStart?: (event: any) => void;
5
5
  onLoadingProgress?: (event: any) => void;
6
6
  onLoadingComplete?: (event: any) => void;
@@ -85,7 +85,7 @@ export declare class AudioCache implements ICache {
85
85
  * @returns Promise that resolves to decoded AudioBuffer
86
86
  * @throws Error if audio cannot be fetched or decoded
87
87
  */
88
- getAudioBuffer(context: AudioContext, url: string, signal?: AbortSignal, callbacks?: {
88
+ getAudioBuffer(context: BaseContext, url: string, signal?: AbortSignal, callbacks?: {
89
89
  onLoadingStart?: (event: any) => void;
90
90
  onLoadingProgress?: (event: any) => void;
91
91
  onLoadingComplete?: (event: any) => void;
@@ -1,6 +1,5 @@
1
- import { AudioContext, IAudioListener, IPannerNode, IPannerOptions } from 'standardized-audio-context';
2
1
  import { ICache } from './cache';
3
- import { AudioBuffer, GainNode } from './context';
2
+ import { AudioBuffer, AudioListener, BaseContext, BiquadFilterNode, GainNode, PannerNode } from './context';
4
3
  import { CacophonyEvents } from './events';
5
4
  import { Group } from './group';
6
5
  import { MicrophoneStream } from './microphone';
@@ -12,7 +11,6 @@ export declare enum SoundType {
12
11
  Buffer = "Buffer",
13
12
  Oscillator = "oscillator"
14
13
  }
15
- type PannerNode = IPannerNode<AudioContext>;
16
14
  /**
17
15
  * Represents a 3D position in space.
18
16
  * @typedef {Array<number>} Position - An array of three numbers representing the x, y, and z coordinates.
@@ -73,20 +71,76 @@ export interface BaseSound {
73
71
  fadeOut?(duration: number, type?: FadeType): Promise<void>;
74
72
  stopWithFade?(duration: number, type?: FadeType): Promise<void>;
75
73
  }
74
+ /**
75
+ * Options for creating an offline audio context.
76
+ */
77
+ export interface OfflineOptions {
78
+ numberOfChannels: number;
79
+ length: number;
80
+ sampleRate: number;
81
+ context?: BaseContext & {
82
+ startRendering(): Promise<AudioBuffer>;
83
+ };
84
+ }
85
+ export interface RuntimeOptions {
86
+ createAudioWorkletNode?: (context: BaseContext, name: string) => any;
87
+ }
88
+ /**
89
+ * Resources belonging to a Sound that must be released when the Sound is
90
+ * garbage collected without an explicit cleanup() call. The record is a plain
91
+ * data bag with no back-references to the Sound or its Playbacks — a
92
+ * FinalizationRegistry strongly retains its held value, so holding the Sound
93
+ * itself would prevent the target from ever becoming collectable.
94
+ */
95
+ export interface SoundCleanupHoldings {
96
+ sources: Array<{
97
+ disconnect(): void;
98
+ }>;
99
+ gainNodes: GainNode[];
100
+ mediaElements: HTMLMediaElement[];
101
+ }
76
102
  export declare class Cacophony {
77
- context: AudioContext;
103
+ context: BaseContext;
78
104
  globalGainNode: GainNode;
79
- listener: IAudioListener;
105
+ listener: AudioListener;
80
106
  private prevVolume;
81
107
  private finalizationRegistry;
82
108
  private eventEmitter;
83
109
  private cache;
84
- constructor(context?: AudioContext, cache?: ICache);
110
+ private createAudioWorkletNode;
111
+ constructor(context?: BaseContext, cache?: ICache, runtimeOptions?: RuntimeOptions);
112
+ /** @internal */
113
+ registerSoundForCleanup(sound: object, holdings: SoundCleanupHoldings, unregisterToken: object): void;
114
+ /** @internal */
115
+ unregisterSoundCleanup(unregisterToken: object): void;
116
+ /**
117
+ * Creates a Cacophony instance backed by an OfflineAudioContext.
118
+ * Use this for rendering, bouncing, precomputing processed output,
119
+ * or non-realtime scenarios.
120
+ *
121
+ * @param options - Offline context configuration (channels, length, sampleRate)
122
+ * @param cache - Optional cache implementation
123
+ * @returns A Cacophony instance backed by OfflineAudioContext
124
+ */
125
+ static createOffline(options: OfflineOptions, cache?: ICache): Cacophony;
126
+ /**
127
+ * Returns true if this instance is backed by an offline audio context
128
+ * (i.e., the context has a startRendering method).
129
+ */
130
+ get isOffline(): boolean;
131
+ /**
132
+ * Renders the offline audio graph to a buffer.
133
+ * Only available when the context has a startRendering method.
134
+ *
135
+ * @returns Promise that resolves to the rendered AudioBuffer
136
+ * @throws Error if the context does not support offline rendering
137
+ */
138
+ startRendering(): Promise<AudioBuffer>;
85
139
  /**
86
140
  * Register event listener.
87
141
  * @returns Cleanup function
88
142
  */
89
- on<K extends keyof CacophonyEvents>(event: K, listener: (data: CacophonyEvents[K]) => void): void;
143
+ on<K extends keyof CacophonyEvents>(event: K, listener: (data: CacophonyEvents[K]) => void): () => void;
90
144
  /**
91
145
  * Remove event listener.
92
146
  */
@@ -94,7 +148,9 @@ export declare class Cacophony {
94
148
  emit<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): void;
95
149
  emitAsync<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): Promise<void>;
96
150
  loadWorklets(signal?: AbortSignal): Promise<void>;
97
- createWorkletNode(name: string, url: string, signal?: AbortSignal): Promise<import('standardized-audio-context').IAudioWorkletNode<import('standardized-audio-context').IAudioContext>>;
151
+ createWorkletNode(name: string, url: string, signal?: AbortSignal): Promise<any>;
152
+ private createAbortError;
153
+ private createMediaSound;
98
154
  clearMemoryCache(): void;
99
155
  createOscillator(options: OscillatorOptions, panType?: PanType): Synth;
100
156
  /**
@@ -127,10 +183,10 @@ export declare class Cacophony {
127
183
  * @returns Promise that resolves to a Sound instance for streaming
128
184
  */
129
185
  createStream(url: string, signal?: AbortSignal): Promise<Sound>;
130
- createBiquadFilter: ({ type, frequency, gain, Q, }: BiquadFilterOptions) => BiquadFilterNode;
186
+ createBiquadFilter: ({ type, frequency, gain, Q }: BiquadFilterOptions) => BiquadFilterNode;
131
187
  /**
132
188
  * Creates a PannerNode with the specified options.
133
- * @param {IPannerOptions} options - An object containing the options to use when creating the PannerNode.
189
+ * @param {PannerOptions} options - An object containing the options to use when creating the PannerNode.
134
190
  * @returns {PannerNode} A new PannerNode instance with the specified options.
135
191
  * @example
136
192
  * const panner = audio.createPanner({
@@ -142,7 +198,7 @@ export declare class Cacophony {
142
198
  * orientationZ: 0,
143
199
  * });
144
200
  */
145
- createPanner({ coneInnerAngle, coneOuterAngle, coneOuterGain, distanceModel, maxDistance, channelCount, channelCountMode, channelInterpretation, panningModel, refDistance, rolloffFactor, positionX, positionY, positionZ, orientationX, orientationY, orientationZ, }: Partial<IPannerOptions>): PannerNode;
201
+ createPanner({ coneInnerAngle, coneOuterAngle, coneOuterGain, distanceModel, maxDistance, channelCount, channelCountMode, channelInterpretation, panningModel, refDistance, rolloffFactor, positionX, positionY, positionZ, orientationX, orientationY, orientationZ, }: Partial<PannerOptions>): PannerNode;
146
202
  /**
147
203
  * Suspends the audio context.
148
204
  */
@@ -170,4 +226,3 @@ export declare class Cacophony {
170
226
  get listenerPosition(): Position;
171
227
  set listenerPosition(position: Position);
172
228
  }
173
- export {};
@@ -1,5 +1,6 @@
1
1
  import { BasePlayback } from 'basePlayback';
2
2
  import { FadeType, Position } from './cacophony';
3
+ import { BiquadFilterNode } from './context';
3
4
  import { FilterManager } from './filters';
4
5
  type Constructor<T = FilterManager> = abstract new (...args: any[]) => T;
5
6
  export interface IPlaybackContainer {
package/dist/context.d.ts CHANGED
@@ -1,13 +1,130 @@
1
- import { AudioContext, IAudioBuffer, IAudioBufferSourceNode, IAudioNode, IBiquadFilterNode, IGainNode, IMediaElementAudioSourceNode, IMediaStreamAudioSourceNode, IOscillatorNode, IPannerNode, IStereoPannerNode } from 'standardized-audio-context';
2
- export type AudioNode = IAudioNode<AudioContext>;
3
- export type BiquadFilterNode = IBiquadFilterNode<AudioContext>;
4
- export type MediaElementSourceNode = IMediaElementAudioSourceNode<AudioContext>;
5
- export type AudioBuffer = IAudioBuffer;
6
- export type AudioBufferSourceNode = IAudioBufferSourceNode<AudioContext>;
7
- export type OscillatorNode = IOscillatorNode<AudioContext>;
1
+ /**
2
+ * Cacophony's own audio type interfaces.
3
+ *
4
+ * These are minimal structural interfaces covering only what Cacophony
5
+ * actually uses. Both the native Web Audio API and standardized-audio-context
6
+ * satisfy these structurally, so users can pass either.
7
+ */
8
+ export interface AudioParam {
9
+ value: number;
10
+ setValueAtTime(value: number, startTime: number): AudioParam;
11
+ linearRampToValueAtTime(value: number, endTime: number): AudioParam;
12
+ exponentialRampToValueAtTime(value: number, endTime: number): AudioParam;
13
+ cancelScheduledValues(cancelTime: number): AudioParam;
14
+ }
15
+ export interface AudioNode extends EventTarget {
16
+ connect(destination: AudioNode, output?: number, input?: number): AudioNode;
17
+ connect(destination: AudioParam, output?: number): void;
18
+ disconnect(): void;
19
+ disconnect(output: number): void;
20
+ disconnect(destination: AudioNode): void;
21
+ disconnect(destination: AudioParam): void;
22
+ channelCount: number;
23
+ channelCountMode: ChannelCountMode;
24
+ channelInterpretation: ChannelInterpretation;
25
+ readonly context: {
26
+ readonly currentTime: number;
27
+ };
28
+ readonly numberOfInputs: number;
29
+ readonly numberOfOutputs: number;
30
+ }
31
+ export interface GainNode extends AudioNode {
32
+ readonly gain: AudioParam;
33
+ }
34
+ export interface BiquadFilterNode extends AudioNode {
35
+ type: BiquadFilterType;
36
+ readonly frequency: AudioParam;
37
+ readonly detune: AudioParam;
38
+ readonly Q: AudioParam;
39
+ readonly gain: AudioParam;
40
+ getFrequencyResponse(frequencyHz: Float32Array, magResponse: Float32Array, phaseResponse: Float32Array): void;
41
+ }
42
+ export interface PannerNode extends AudioNode {
43
+ coneInnerAngle: number;
44
+ coneOuterAngle: number;
45
+ coneOuterGain: number;
46
+ distanceModel: DistanceModelType;
47
+ maxDistance: number;
48
+ panningModel: PanningModelType;
49
+ refDistance: number;
50
+ rolloffFactor: number;
51
+ readonly positionX: AudioParam;
52
+ readonly positionY: AudioParam;
53
+ readonly positionZ: AudioParam;
54
+ readonly orientationX: AudioParam;
55
+ readonly orientationY: AudioParam;
56
+ readonly orientationZ: AudioParam;
57
+ }
58
+ export interface StereoPannerNode extends AudioNode {
59
+ readonly pan: AudioParam;
60
+ }
61
+ export interface AudioBufferSourceNode extends AudioNode {
62
+ buffer: AudioBuffer | null;
63
+ loop: boolean;
64
+ loopStart: number;
65
+ loopEnd: number;
66
+ readonly playbackRate: AudioParam;
67
+ onended: ((ev: Event) => any) | null;
68
+ start(when?: number, offset?: number, duration?: number): void;
69
+ stop(when?: number): void;
70
+ }
71
+ export interface OscillatorNode extends AudioNode {
72
+ type: OscillatorType;
73
+ readonly frequency: AudioParam;
74
+ readonly detune: AudioParam;
75
+ onended: ((ev: Event) => any) | null;
76
+ start(when?: number): void;
77
+ stop(when?: number): void;
78
+ }
79
+ export interface MediaElementSourceNode extends AudioNode {
80
+ readonly mediaElement: HTMLMediaElement;
81
+ }
82
+ export interface MediaStreamAudioSourceNode extends AudioNode {
83
+ readonly mediaStream: MediaStream;
84
+ }
85
+ export interface AudioBuffer {
86
+ readonly duration: number;
87
+ readonly length: number;
88
+ readonly sampleRate: number;
89
+ readonly numberOfChannels: number;
90
+ getChannelData(channel: number): Float32Array;
91
+ copyFromChannel(destination: Float32Array, channelNumber: number, bufferOffset?: number): void;
92
+ copyToChannel(source: Float32Array, channelNumber: number, bufferOffset?: number): void;
93
+ }
94
+ export interface AudioListener {
95
+ readonly positionX: AudioParam;
96
+ readonly positionY: AudioParam;
97
+ readonly positionZ: AudioParam;
98
+ readonly forwardX: AudioParam;
99
+ readonly forwardY: AudioParam;
100
+ readonly forwardZ: AudioParam;
101
+ readonly upX: AudioParam;
102
+ readonly upY: AudioParam;
103
+ readonly upZ: AudioParam;
104
+ }
105
+ export interface AudioWorklet {
106
+ addModule(moduleURL: string, options?: WorkletOptions): Promise<void>;
107
+ }
108
+ export interface BaseContext {
109
+ readonly currentTime: number;
110
+ readonly sampleRate: number;
111
+ readonly destination: AudioNode;
112
+ readonly listener: AudioListener;
113
+ readonly audioWorklet?: AudioWorklet;
114
+ createGain(): GainNode;
115
+ createBufferSource(): AudioBufferSourceNode;
116
+ createBiquadFilter(): BiquadFilterNode;
117
+ createPanner(): PannerNode;
118
+ createStereoPanner(): StereoPannerNode;
119
+ createOscillator(): OscillatorNode;
120
+ decodeAudioData(audioData: ArrayBuffer): Promise<AudioBuffer>;
121
+ decodeAudioData(audioData: ArrayBuffer, successCallback: (buffer: AudioBuffer) => void, errorCallback?: (error: DOMException) => void): Promise<AudioBuffer>;
122
+ createMediaElementSource?(mediaElement: HTMLMediaElement): MediaElementSourceNode;
123
+ createMediaStreamSource?(stream: MediaStream): MediaStreamAudioSourceNode;
124
+ suspend?(suspendTime?: number): Promise<void>;
125
+ resume?(): Promise<void>;
126
+ close?(): Promise<void>;
127
+ startRendering?(): Promise<AudioBuffer>;
128
+ readonly length?: number;
129
+ }
8
130
  export type SourceNode = AudioBufferSourceNode | MediaElementSourceNode | OscillatorNode;
9
- export type MediaStreamAudioSourceNode = IMediaStreamAudioSourceNode<AudioContext>;
10
- export type GainNode = IGainNode<AudioContext>;
11
- export type PannerNode = IPannerNode<AudioContext>;
12
- export type StereoPannerNode = IStereoPannerNode<AudioContext>;
13
- export { AudioContext };
package/dist/events.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { BasePlayback } from './basePlayback';
2
- import { SynthPlayback } from './synthPlayback';
3
2
  import { Sound } from './sound';
4
3
  import { Synth } from './synth';
4
+ import { SynthPlayback } from './synthPlayback';
5
5
  /**
6
6
  * Base events for all audio objects.
7
7
  */
@@ -12,21 +12,21 @@ export interface FadeStartEvent {
12
12
  }
13
13
  export interface BaseAudioEvents {
14
14
  play: BasePlayback;
15
- stop: void;
16
- pause: void;
17
- resume: void;
18
- ended: void;
15
+ stop: undefined;
16
+ pause: undefined;
17
+ resume: undefined;
18
+ ended: undefined;
19
19
  volumeChange: number;
20
20
  error: PlaybackErrorEvent;
21
21
  fadeStart: FadeStartEvent;
22
- fadeEnd: void;
23
- fadeCancel: void;
22
+ fadeEnd: undefined;
23
+ fadeCancel: undefined;
24
24
  }
25
25
  /**
26
26
  * Sound-specific events.
27
27
  */
28
28
  export interface SoundEvents extends BaseAudioEvents {
29
- loopEnd: void;
29
+ loopEnd: undefined;
30
30
  rateChange: number;
31
31
  soundError: SoundErrorEvent;
32
32
  }
@@ -39,7 +39,7 @@ export interface PlaybackEvents extends BaseAudioEvents {
39
39
  /**
40
40
  * Synthesizer-specific events.
41
41
  */
42
- export interface SynthEvents extends Omit<BaseAudioEvents, 'play'> {
42
+ export interface SynthEvents extends Omit<BaseAudioEvents, "play"> {
43
43
  play: SynthPlayback;
44
44
  frequencyChange: number;
45
45
  typeChange: OscillatorType;
@@ -57,10 +57,10 @@ export interface GlobalPlaybackEvent {
57
57
  */
58
58
  export interface CacophonyEvents {
59
59
  volumeChange: number;
60
- mute: void;
61
- unmute: void;
62
- suspend: void;
63
- resume: void;
60
+ mute: undefined;
61
+ unmute: undefined;
62
+ suspend: undefined;
63
+ resume: undefined;
64
64
  loadingStart: LoadingStartEvent;
65
65
  loadingProgress: LoadingProgressEvent;
66
66
  loadingComplete: LoadingCompleteEvent;
@@ -105,7 +105,7 @@ export interface LoadingCompleteEvent {
105
105
  export interface LoadingErrorEvent {
106
106
  url: string;
107
107
  error: Error;
108
- errorType: 'network' | 'decode' | 'abort' | 'unknown';
108
+ errorType: "network" | "decode" | "abort" | "unknown";
109
109
  timestamp: number;
110
110
  }
111
111
  /**
@@ -113,7 +113,7 @@ export interface LoadingErrorEvent {
113
113
  */
114
114
  export interface PlaybackErrorEvent {
115
115
  error: Error;
116
- errorType: 'context' | 'source' | 'decode' | 'unknown';
116
+ errorType: "context" | "source" | "decode" | "unknown";
117
117
  timestamp: number;
118
118
  recoverable: boolean;
119
119
  }
@@ -123,7 +123,7 @@ export interface PlaybackErrorEvent {
123
123
  export interface SoundErrorEvent {
124
124
  url?: string;
125
125
  error: Error;
126
- errorType: 'load' | 'playback' | 'context' | 'unknown';
126
+ errorType: "load" | "playback" | "context" | "unknown";
127
127
  timestamp: number;
128
128
  recoverable: boolean;
129
129
  }
@@ -132,7 +132,7 @@ export interface SoundErrorEvent {
132
132
  */
133
133
  export interface CacheHitEvent {
134
134
  url: string;
135
- cacheType: 'memory' | 'browser' | 'conditional';
135
+ cacheType: "memory" | "browser" | "conditional";
136
136
  timestamp: number;
137
137
  }
138
138
  /**
@@ -140,7 +140,7 @@ export interface CacheHitEvent {
140
140
  */
141
141
  export interface CacheMissEvent {
142
142
  url: string;
143
- reason: 'not-found' | 'expired' | 'invalid';
143
+ reason: "not-found" | "expired" | "invalid";
144
144
  timestamp: number;
145
145
  }
146
146
  /**
@@ -149,7 +149,7 @@ export interface CacheMissEvent {
149
149
  export interface CacheErrorEvent {
150
150
  url: string;
151
151
  error: Error;
152
- operation: 'get' | 'set' | 'delete' | 'validate';
152
+ operation: "get" | "set" | "delete" | "validate";
153
153
  timestamp: number;
154
154
  }
155
155
  /**
package/dist/filters.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { BiquadFilterNode } from './context';
1
2
  export type FilterCloneOverrides = {
2
3
  filters?: BiquadFilterNode[];
3
4
  };
package/dist/group.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { BaseSound, FadeType, LoopCount } from './cacophony';
2
+ import { BiquadFilterNode } from './context';
2
3
  import { Playback } from './playback';
3
4
  import { Sound } from './sound';
4
5
  export declare class Group implements BaseSound {
@@ -9,8 +10,7 @@ export declare class Group implements BaseSound {
9
10
  constructor(sounds?: Sound[]);
10
11
  /**
11
12
  * Prepares a random sound from the group for playback.
12
- * @returns The playback object representing the prepared sound.
13
- * @throws Error if the group is empty and there are no sounds to prepare.
13
+ * @returns The playback object representing the prepared sound, or undefined if the group is empty.
14
14
  */
15
15
  preplayRandom(): Playback | undefined;
16
16
  /**