cacophony 0.17.1 → 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 +31 -15
  3. package/dist/cache.d.ts +3 -3
  4. package/dist/cacophony.d.ts +81 -12
  5. package/dist/container.d.ts +31 -1
  6. package/dist/context.d.ts +129 -12
  7. package/dist/events.d.ts +25 -17
  8. package/dist/filters.d.ts +1 -0
  9. package/dist/group.d.ts +7 -3
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts +3 -0
  13. package/dist/index.mjs +1232 -973
  14. package/dist/index.mjs.map +1 -1
  15. package/dist/microphone.d.ts +5 -4
  16. package/dist/oscillatorMixin.d.ts +15 -8
  17. package/dist/pannerMixin.d.ts +27 -28
  18. package/dist/playback.d.ts +41 -3
  19. package/dist/sound.d.ts +13 -6
  20. package/dist/stream.d.ts +2 -2
  21. package/dist/synth.d.ts +13 -9
  22. package/dist/synthGroup.d.ts +24 -3
  23. package/dist/synthPlayback.d.ts +22 -10
  24. package/dist/volumeMixin.d.ts +43 -10
  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 +17 -14
  30. package/docs/classes/MicrophonePlayback.html +3 -3
  31. package/docs/classes/Playback.html +67 -41
  32. package/docs/classes/Sound.html +41 -17
  33. package/docs/classes/Synth.html +38 -22
  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 +11 -0
  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 +11 -0
  45. package/docs/interfaces/BaseContext.html +20 -0
  46. package/docs/interfaces/BaseSound.html +6 -2
  47. package/docs/interfaces/BiquadFilterNode.html +30 -0
  48. package/docs/interfaces/CacheErrorEvent.html +6 -0
  49. package/docs/interfaces/CacheHitEvent.html +5 -0
  50. package/docs/interfaces/CacheMissEvent.html +5 -0
  51. package/docs/interfaces/CacophonyEvents.html +17 -0
  52. package/docs/interfaces/FadeStartEvent.html +5 -0
  53. package/docs/interfaces/GainNode.html +25 -0
  54. package/docs/interfaces/GlobalPlaybackEvent.html +4 -0
  55. package/docs/interfaces/LoadingCompleteEvent.html +6 -0
  56. package/docs/interfaces/LoadingErrorEvent.html +6 -0
  57. package/docs/interfaces/LoadingProgressEvent.html +8 -0
  58. package/docs/interfaces/LoadingStartEvent.html +4 -0
  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 +7 -0
  65. package/docs/interfaces/PlaybackErrorEvent.html +6 -0
  66. package/docs/interfaces/PlaybackEvents.html +13 -0
  67. package/docs/interfaces/RuntimeOptions.html +2 -0
  68. package/docs/interfaces/SoundCleanupHoldings.html +9 -0
  69. package/docs/interfaces/SoundErrorEvent.html +7 -0
  70. package/docs/interfaces/SoundEvents.html +15 -0
  71. package/docs/interfaces/StereoPannerNode.html +25 -0
  72. package/docs/interfaces/SynthEvents.html +15 -0
  73. package/docs/modules.html +42 -3
  74. package/docs/types/CacheEventCallback.html +2 -0
  75. package/docs/types/ErrorEventCallback.html +2 -0
  76. package/docs/types/FadeType.html +1 -1
  77. package/docs/types/LoadingEventCallback.html +2 -0
  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,13 +1,14 @@
1
+ import { FadeType } from './cacophony';
1
2
  import { IPlaybackContainer } from './container';
2
3
  import { AudioNode } from './context';
3
- import { FilterManager } from './filters';
4
4
  import { TypedEventEmitter } from './eventEmitter';
5
5
  import { PlaybackEvents } from './events';
6
+ import { FilterManager } from './filters';
6
7
  declare const BasePlayback_base: (abstract new (...args: any[]) => {
7
8
  panner?: import('./context').PannerNode | import('./context').StereoPannerNode | undefined;
8
9
  _panType: import('./cacophony').PanType;
9
10
  readonly panType: import('./cacophony').PanType;
10
- setPanType(panType: import('./cacophony').PanType, audioContext: import('standardized-audio-context').IAudioContext): void;
11
+ setPanType(panType: import('./cacophony').PanType, audioContext: import('./context').BaseContext): void;
11
12
  setPannerNode(pannerNode: import('./context').PannerNode): void;
12
13
  get stereoPan(): number | null;
13
14
  set stereoPan(value: number);
@@ -15,25 +16,32 @@ declare const BasePlayback_base: (abstract new (...args: any[]) => {
15
16
  set threeDOptions(options: Partial<PannerOptions>);
16
17
  position: import('./cacophony').Position;
17
18
  cleanup(): void;
18
- _filters: BiquadFilterNode[];
19
- addFilter(filter: BiquadFilterNode): void;
20
- removeFilter(filter: BiquadFilterNode): void;
19
+ _filters: import('./context').BiquadFilterNode[];
20
+ addFilter(filter: import('./context').BiquadFilterNode): void;
21
+ removeFilter(filter: import('./context').BiquadFilterNode): void;
21
22
  applyFilters(connection: any): any;
22
- readonly filters: BiquadFilterNode[];
23
- addFilters(filters: BiquadFilterNode[]): void;
24
- removeFilters(filters: BiquadFilterNode[]): void;
23
+ readonly filters: import('./context').BiquadFilterNode[];
24
+ addFilters(filters: import('./context').BiquadFilterNode[]): void;
25
+ removeFilters(filters: import('./context').BiquadFilterNode[]): void;
25
26
  }) & (abstract new (...args: any[]) => {
26
27
  gainNode?: import('./context').GainNode | undefined;
28
+ _fadeTimeout?: NodeJS.Timeout | undefined;
29
+ _isFading: boolean;
27
30
  setGainNode(gainNode: import('./context').GainNode): void;
28
31
  cleanup(): void;
29
32
  volume: number;
30
- _filters: BiquadFilterNode[];
31
- addFilter(filter: BiquadFilterNode): void;
32
- removeFilter(filter: BiquadFilterNode): void;
33
+ readonly isFading: boolean;
34
+ fadeTo(value: number, duration: number, type?: FadeType): Promise<void>;
35
+ cancelFade(): void;
36
+ fadeIn(duration: number, type?: FadeType | undefined): Promise<void>;
37
+ fadeOut(duration: number, type?: FadeType | undefined): Promise<void>;
38
+ _filters: import('./context').BiquadFilterNode[];
39
+ addFilter(filter: import('./context').BiquadFilterNode): void;
40
+ removeFilter(filter: import('./context').BiquadFilterNode): void;
33
41
  applyFilters(connection: any): any;
34
- readonly filters: BiquadFilterNode[];
35
- addFilters(filters: BiquadFilterNode[]): void;
36
- removeFilters(filters: BiquadFilterNode[]): void;
42
+ readonly filters: import('./context').BiquadFilterNode[];
43
+ addFilters(filters: import('./context').BiquadFilterNode[]): void;
44
+ removeFilters(filters: import('./context').BiquadFilterNode[]): void;
37
45
  }) & typeof FilterManager;
38
46
  export declare abstract class BasePlayback extends BasePlayback_base {
39
47
  source?: AudioNode;
@@ -53,13 +61,21 @@ export declare abstract class BasePlayback extends BasePlayback_base {
53
61
  * Register event listener.
54
62
  * @returns Cleanup function
55
63
  */
56
- 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;
57
65
  /**
58
66
  * Remove event listener.
59
67
  */
60
68
  off<K extends keyof PlaybackEvents>(event: K, listener: (data: PlaybackEvents[K]) => void): void;
61
69
  emit<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): void;
62
70
  emitAsync<K extends keyof PlaybackEvents>(event: K, data: PlaybackEvents[K]): Promise<void>;
71
+ /**
72
+ * Fades the volume to a target value, emitting fadeStart and fadeEnd events.
73
+ */
74
+ fadeTo(value: number, duration: number, type?: FadeType): Promise<void>;
75
+ /**
76
+ * Cancels any in-progress fade, emitting fadeCancel if a fade was active.
77
+ */
78
+ cancelFade(): void;
63
79
  cleanup(): void;
64
80
  }
65
81
  export {};
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.
@@ -43,6 +41,16 @@ export type FadeType = "linear" | "exponential";
43
41
  * @typedef {'HRTF' | 'stereo'} PanType - The pan type, either 'HRTF' for 3D audio or 'stereo' for traditional stereo panning.
44
42
  */
45
43
  export type PanType = "HRTF" | "stereo";
44
+ /**
45
+ * Options for configuring fade behavior when starting playback via Sound.play().
46
+ * @interface PlayOptions
47
+ */
48
+ export interface PlayOptions {
49
+ fadeIn?: number;
50
+ fadeOut?: number;
51
+ fadeType?: FadeType;
52
+ fadeInPerLoop?: boolean;
53
+ }
46
54
  /**
47
55
  * The base interface for any sound-producing entity, including individual sounds, groups, and playbacks.
48
56
  * @interface BaseSound
@@ -58,21 +66,81 @@ export interface BaseSound {
58
66
  volume: number;
59
67
  position?: Position;
60
68
  threeDOptions?: any;
69
+ fadeTo?(value: number, duration: number, type?: FadeType): Promise<void>;
70
+ fadeIn?(duration: number, type?: FadeType): Promise<void>;
71
+ fadeOut?(duration: number, type?: FadeType): Promise<void>;
72
+ stopWithFade?(duration: number, type?: FadeType): Promise<void>;
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[];
61
101
  }
62
102
  export declare class Cacophony {
63
- context: AudioContext;
103
+ context: BaseContext;
64
104
  globalGainNode: GainNode;
65
- listener: IAudioListener;
105
+ listener: AudioListener;
66
106
  private prevVolume;
67
107
  private finalizationRegistry;
68
108
  private eventEmitter;
69
109
  private cache;
70
- 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>;
71
139
  /**
72
140
  * Register event listener.
73
141
  * @returns Cleanup function
74
142
  */
75
- 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;
76
144
  /**
77
145
  * Remove event listener.
78
146
  */
@@ -80,7 +148,9 @@ export declare class Cacophony {
80
148
  emit<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): void;
81
149
  emitAsync<K extends keyof CacophonyEvents>(event: K, data: CacophonyEvents[K]): Promise<void>;
82
150
  loadWorklets(signal?: AbortSignal): Promise<void>;
83
- 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;
84
154
  clearMemoryCache(): void;
85
155
  createOscillator(options: OscillatorOptions, panType?: PanType): Synth;
86
156
  /**
@@ -113,10 +183,10 @@ export declare class Cacophony {
113
183
  * @returns Promise that resolves to a Sound instance for streaming
114
184
  */
115
185
  createStream(url: string, signal?: AbortSignal): Promise<Sound>;
116
- createBiquadFilter: ({ type, frequency, gain, Q, }: BiquadFilterOptions) => BiquadFilterNode;
186
+ createBiquadFilter: ({ type, frequency, gain, Q }: BiquadFilterOptions) => BiquadFilterNode;
117
187
  /**
118
188
  * Creates a PannerNode with the specified options.
119
- * @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.
120
190
  * @returns {PannerNode} A new PannerNode instance with the specified options.
121
191
  * @example
122
192
  * const panner = audio.createPanner({
@@ -128,7 +198,7 @@ export declare class Cacophony {
128
198
  * orientationZ: 0,
129
199
  * });
130
200
  */
131
- 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;
132
202
  /**
133
203
  * Suspends the audio context.
134
204
  */
@@ -156,4 +226,3 @@ export declare class Cacophony {
156
226
  get listenerPosition(): Position;
157
227
  set listenerPosition(position: Position);
158
228
  }
159
- export {};
@@ -1,5 +1,6 @@
1
1
  import { BasePlayback } from 'basePlayback';
2
- import { Position } from './cacophony';
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 {
@@ -73,6 +74,35 @@ export declare function PlaybackContainer<TBase extends Constructor>(Base: TBase
73
74
  * @returns {number} The current volume of the sound.
74
75
  */
75
76
  volume: number;
77
+ /**
78
+ * Fades the volume of all playbacks to a target value over a duration.
79
+ * @param {number} value - The target volume (0 to 1).
80
+ * @param {number} duration - The fade duration in milliseconds.
81
+ * @param {FadeType} type - The fade curve type. Defaults to "linear".
82
+ * @returns {Promise<void>} Resolves when all fades complete.
83
+ */
84
+ fadeTo(value: number, duration: number, type?: FadeType): Promise<void>;
85
+ /**
86
+ * Fades in all playbacks from silence to their current volume.
87
+ * @param {number} duration - The fade duration in milliseconds.
88
+ * @param {FadeType} type - The fade curve type. Defaults to "linear".
89
+ * @returns {Promise<void>} Resolves when all fades complete.
90
+ */
91
+ fadeIn(duration: number, type?: FadeType): Promise<void>;
92
+ /**
93
+ * Fades out all playbacks from their current volume to silence.
94
+ * @param {number} duration - The fade duration in milliseconds.
95
+ * @param {FadeType} type - The fade curve type. Defaults to "linear".
96
+ * @returns {Promise<void>} Resolves when all fades complete.
97
+ */
98
+ fadeOut(duration: number, type?: FadeType): Promise<void>;
99
+ /**
100
+ * Fades out all playbacks then stops them.
101
+ * @param {number} duration - The fade-out duration in milliseconds.
102
+ * @param {FadeType} type - The fade curve type. Defaults to "linear".
103
+ * @returns {Promise<void>} Resolves when the fade completes and all playbacks are stopped.
104
+ */
105
+ stopWithFade(duration: number, type?: FadeType): Promise<void>;
76
106
  _filters: BiquadFilterNode[];
77
107
  applyFilters(connection: any): any;
78
108
  readonly filters: BiquadFilterNode[];
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 };