cacophony 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +26 -26
- package/src/cacophony.ts +107 -19
- package/src/cacophony.test.ts +0 -5
package/package.json
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "cacophony",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Typescript audio library with caching",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"build": "npm run clean && tsc --build",
|
|
9
|
-
"clean": "tsc --build --clean",
|
|
10
|
-
"prepublishOnly": "npm run build",
|
|
11
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
"audio",
|
|
15
|
-
"webaudio"
|
|
16
|
-
],
|
|
17
|
-
"author": "Christopher Toth",
|
|
18
|
-
"license": "ISC",
|
|
19
|
-
"devDependencies": {
|
|
20
|
-
"standardized-audio-context-mock": "^9.6.29",
|
|
21
|
-
"typescript": "^5.1.6"
|
|
22
|
-
},
|
|
23
|
-
"dependencies": {
|
|
24
|
-
"standardized-audio-context": "^25.3.55"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "cacophony",
|
|
3
|
+
"version": "0.1.4",
|
|
4
|
+
"description": "Typescript audio library with caching",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "npm run clean && tsc --build",
|
|
9
|
+
"clean": "tsc --build --clean",
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"audio",
|
|
15
|
+
"webaudio"
|
|
16
|
+
],
|
|
17
|
+
"author": "Christopher Toth",
|
|
18
|
+
"license": "ISC",
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"standardized-audio-context-mock": "^9.6.29",
|
|
21
|
+
"typescript": "^5.1.6"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"standardized-audio-context": "^25.3.55"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/cacophony.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface BaseSound {
|
|
|
21
21
|
addFilter(filter: BiquadFilterNode): void;
|
|
22
22
|
removeFilter(filter: BiquadFilterNode): void;
|
|
23
23
|
moveTo(x: number, y: number, z: number): void;
|
|
24
|
+
volume: number;
|
|
25
|
+
|
|
24
26
|
loop(loopCount?: LoopCount): LoopCount;
|
|
25
27
|
}
|
|
26
28
|
|
|
@@ -68,7 +70,6 @@ export class Cacophony {
|
|
|
68
70
|
return filter;
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
72
73
|
pause() {
|
|
73
74
|
if ('suspend' in this.context) {
|
|
74
75
|
this.context.suspend();
|
|
@@ -91,6 +92,14 @@ export class Cacophony {
|
|
|
91
92
|
this.globalGainNode.gain.value = volume;
|
|
92
93
|
}
|
|
93
94
|
|
|
95
|
+
get volume(): number {
|
|
96
|
+
return this.globalGainNode.gain.value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
set volume(volume: number) {
|
|
100
|
+
this.setGlobalVolume(volume);
|
|
101
|
+
}
|
|
102
|
+
|
|
94
103
|
mute() {
|
|
95
104
|
this.prevVolume = this.globalGainNode.gain.value;
|
|
96
105
|
this.setGlobalVolume(0);
|
|
@@ -154,7 +163,7 @@ export class Sound extends FilterManager implements BaseSound {
|
|
|
154
163
|
|
|
155
164
|
play(): Playback[] {
|
|
156
165
|
const playback = this.preplay();
|
|
157
|
-
playback.forEach(p => p.source
|
|
166
|
+
playback.forEach(p => p.source!.start());
|
|
158
167
|
return playback;
|
|
159
168
|
}
|
|
160
169
|
|
|
@@ -184,7 +193,7 @@ export class Sound extends FilterManager implements BaseSound {
|
|
|
184
193
|
return this.loopCount;
|
|
185
194
|
}
|
|
186
195
|
this.loopCount = loopCount;
|
|
187
|
-
this.playbacks.forEach(p => p.source
|
|
196
|
+
this.playbacks.forEach(p => p.source!.loop = true);
|
|
188
197
|
return this.loopCount;
|
|
189
198
|
}
|
|
190
199
|
|
|
@@ -197,14 +206,22 @@ export class Sound extends FilterManager implements BaseSound {
|
|
|
197
206
|
super.removeFilter(filter);
|
|
198
207
|
this.playbacks.forEach(p => p.removeFilter(filter));
|
|
199
208
|
}
|
|
200
|
-
}
|
|
201
209
|
|
|
210
|
+
get volume(): number {
|
|
211
|
+
return this.globalGainNode.gain.value;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
set volume(volume: number) {
|
|
215
|
+
this.globalGainNode.gain.value = volume;
|
|
216
|
+
this.playbacks.forEach(p => p.volume = volume);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
202
219
|
|
|
203
220
|
class Playback extends FilterManager implements BaseSound {
|
|
204
221
|
context: AudioContext;
|
|
205
|
-
source
|
|
206
|
-
gainNode
|
|
207
|
-
panner
|
|
222
|
+
source?: AudioBufferSourceNode;
|
|
223
|
+
gainNode?: GainNode;
|
|
224
|
+
panner?: PannerNode;
|
|
208
225
|
loopCount: LoopCount = 0;
|
|
209
226
|
currentLoop: number = 0;
|
|
210
227
|
buffer: IAudioBuffer | null = null;
|
|
@@ -223,7 +240,6 @@ class Playback extends FilterManager implements BaseSound {
|
|
|
223
240
|
source.start();
|
|
224
241
|
}
|
|
225
242
|
|
|
226
|
-
|
|
227
243
|
handleLoop(): void {
|
|
228
244
|
if (this.loopCount === 'infinite' || this.currentLoop < this.loopCount) {
|
|
229
245
|
this.currentLoop++;
|
|
@@ -234,35 +250,68 @@ class Playback extends FilterManager implements BaseSound {
|
|
|
234
250
|
}
|
|
235
251
|
|
|
236
252
|
play() {
|
|
253
|
+
if (!this.source) {
|
|
254
|
+
throw new Error('Cannot play a sound that has been cleaned up');
|
|
255
|
+
}
|
|
237
256
|
this.source.start();
|
|
238
257
|
return [this];
|
|
239
258
|
}
|
|
240
259
|
|
|
260
|
+
get volume(): number {
|
|
261
|
+
if (!this.gainNode) {
|
|
262
|
+
throw new Error('Cannot get volume of a sound that has been cleaned up');
|
|
263
|
+
}
|
|
264
|
+
return this.gainNode.gain.value;
|
|
265
|
+
}
|
|
266
|
+
|
|
241
267
|
set volume(v: number) {
|
|
268
|
+
if (!this.gainNode) {
|
|
269
|
+
throw new Error('Cannot set volume of a sound that has been cleaned up');
|
|
270
|
+
}
|
|
242
271
|
this.gainNode.gain.value = v;
|
|
243
272
|
}
|
|
244
273
|
|
|
245
|
-
fadeIn(time: number,
|
|
274
|
+
fadeIn(time: number, fadeType: FadeType = 'linear'): Promise<void> {
|
|
246
275
|
return new Promise(resolve => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
276
|
+
if (!this.gainNode) {
|
|
277
|
+
throw new Error('Cannot fade in a sound that has been cleaned up');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const initialVolume = this.gainNode.gain.value;
|
|
281
|
+
const targetVolume = 1; // Assuming the target volume after fade-in is 1 (full volume)
|
|
282
|
+
|
|
283
|
+
// Reset volume to 0 to start the fade-in process
|
|
284
|
+
this.gainNode.gain.value = 0;
|
|
285
|
+
|
|
286
|
+
switch (fadeType) {
|
|
250
287
|
case 'exponential':
|
|
251
|
-
//
|
|
252
|
-
this.gainNode.gain.
|
|
288
|
+
// Start at a low value (0.01) because exponentialRampToValueAtTime cannot ramp from 0
|
|
289
|
+
this.gainNode.gain.setValueAtTime(0.01, this.context.currentTime);
|
|
290
|
+
this.gainNode.gain.exponentialRampToValueAtTime(targetVolume, this.context.currentTime + time);
|
|
253
291
|
break;
|
|
254
292
|
case 'linear':
|
|
255
|
-
|
|
256
|
-
|
|
293
|
+
this.gainNode.gain.linearRampToValueAtTime(targetVolume, this.context.currentTime + time);
|
|
294
|
+
break;
|
|
257
295
|
}
|
|
258
|
-
|
|
259
|
-
|
|
296
|
+
|
|
297
|
+
// Resolve the Promise after the fade-in time
|
|
298
|
+
setTimeout(() => {
|
|
299
|
+
// Ensure the final volume is set to the target volume
|
|
300
|
+
if (!this.gainNode) {
|
|
301
|
+
throw new Error('Cannot fade in a sound that has been cleaned up');
|
|
302
|
+
}
|
|
303
|
+
this.gainNode.gain.value = targetVolume;
|
|
304
|
+
resolve();
|
|
305
|
+
}, time * 1000);
|
|
260
306
|
});
|
|
261
307
|
}
|
|
262
308
|
|
|
263
309
|
fadeOut(time: number, fadeType: FadeType = 'linear'): Promise<void> {
|
|
264
310
|
return new Promise(resolve => {
|
|
265
311
|
// Storing the current gain value
|
|
312
|
+
if (!this.gainNode) {
|
|
313
|
+
throw new Error('Cannot fade out a sound that has been cleaned up');
|
|
314
|
+
}
|
|
266
315
|
const initialVolume = this.gainNode.gain.value;
|
|
267
316
|
switch (fadeType) {
|
|
268
317
|
case 'exponential':
|
|
@@ -279,7 +328,23 @@ class Playback extends FilterManager implements BaseSound {
|
|
|
279
328
|
});
|
|
280
329
|
}
|
|
281
330
|
|
|
331
|
+
cleanup(): void {
|
|
332
|
+
if (this.source) {
|
|
333
|
+
this.source.disconnect();
|
|
334
|
+
this.source = undefined;
|
|
335
|
+
}
|
|
336
|
+
if (this.gainNode) {
|
|
337
|
+
this.gainNode.disconnect();
|
|
338
|
+
this.gainNode = undefined;
|
|
339
|
+
}
|
|
340
|
+
this.filters.forEach(filter => filter.disconnect());
|
|
341
|
+
this.filters = [];
|
|
342
|
+
}
|
|
343
|
+
|
|
282
344
|
loop(loopCount?: LoopCount): LoopCount {
|
|
345
|
+
if (!this.source) {
|
|
346
|
+
throw new Error('Cannot loop a sound that has been cleaned up');
|
|
347
|
+
}
|
|
283
348
|
if (loopCount === undefined) {
|
|
284
349
|
return this.source.loop === true ? 'infinite' : 0;
|
|
285
350
|
}
|
|
@@ -290,16 +355,25 @@ class Playback extends FilterManager implements BaseSound {
|
|
|
290
355
|
}
|
|
291
356
|
|
|
292
357
|
stop(): void {
|
|
358
|
+
if (!this.source) {
|
|
359
|
+
throw new Error('Cannot stop a sound that has been cleaned up');
|
|
360
|
+
}
|
|
293
361
|
this.source.stop();
|
|
294
362
|
}
|
|
295
363
|
|
|
296
364
|
pause(): void {
|
|
365
|
+
if (!this.source) {
|
|
366
|
+
throw new Error('Cannot pause a sound that has been cleaned up');
|
|
367
|
+
}
|
|
297
368
|
if ('suspend' in this.source.context) {
|
|
298
369
|
this.source.context.suspend();
|
|
299
370
|
}
|
|
300
|
-
|
|
301
371
|
}
|
|
372
|
+
|
|
302
373
|
resume(): void {
|
|
374
|
+
if (!this.source) {
|
|
375
|
+
throw new Error('Cannot resume a sound that has been cleaned up');
|
|
376
|
+
}
|
|
303
377
|
if ('resume' in this.source.context) {
|
|
304
378
|
this.source.context.resume();
|
|
305
379
|
}
|
|
@@ -316,12 +390,18 @@ class Playback extends FilterManager implements BaseSound {
|
|
|
316
390
|
}
|
|
317
391
|
|
|
318
392
|
moveTo(x: number, y: number, z: number): void {
|
|
393
|
+
if (!this.panner) {
|
|
394
|
+
throw new Error('Cannot move a sound that has been cleaned up');
|
|
395
|
+
}
|
|
319
396
|
this.panner.positionX.value = x;
|
|
320
397
|
this.panner.positionY.value = y;
|
|
321
398
|
this.panner.positionZ.value = z;
|
|
322
399
|
}
|
|
323
400
|
|
|
324
401
|
private refreshFilters(): void {
|
|
402
|
+
if (!this.source || !this.gainNode) {
|
|
403
|
+
throw new Error('Cannot update filters on a sound that has been cleaned up');
|
|
404
|
+
}
|
|
325
405
|
let connection = this.source;
|
|
326
406
|
this.source.disconnect();
|
|
327
407
|
connection = this.applyFilters(connection);
|
|
@@ -391,5 +471,13 @@ export class Group implements BaseSound {
|
|
|
391
471
|
this.sounds.forEach(sound => sound.moveTo(x, y, z));
|
|
392
472
|
}
|
|
393
473
|
|
|
474
|
+
get volume(): number {
|
|
475
|
+
return this.sounds[0].volume;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
set volume(volume: number) {
|
|
479
|
+
this.sounds.forEach(sound => sound.volume = volume);
|
|
480
|
+
}
|
|
481
|
+
|
|
394
482
|
}
|
|
395
483
|
|