/
ToneAudioBuffers.ts
163 lines (145 loc) · 4.25 KB
/
ToneAudioBuffers.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import { Tone } from "../Tone";
import { optionsFromArguments } from "../util/Defaults";
import { noOp } from "../util/Interface";
import { isString } from "../util/TypeCheck";
import { ToneAudioBuffer } from "./ToneAudioBuffer";
import { assert } from "../util/Debug";
export interface ToneAudioBuffersUrlMap {
[name: string]: string | AudioBuffer | ToneAudioBuffer;
[name: number]: string | AudioBuffer | ToneAudioBuffer;
}
interface ToneAudioBuffersOptions {
urls: ToneAudioBuffersUrlMap;
onload: () => void;
onerror?: (error: Error) => void;
baseUrl: string;
}
/**
* A data structure for holding multiple buffers in a Map-like datastructure.
*
* @example
* const pianoSamples = new Tone.ToneAudioBuffers({
* A1: "https://tonejs.github.io/audio/casio/A1.mp3",
* A2: "https://tonejs.github.io/audio/casio/A2.mp3",
* }, () => {
* const player = new Tone.Player().toDestination();
* // play one of the samples when they all load
* player.buffer = pianoSamples.get("A2");
* player.start();
* });
* @example
* // To pass in additional parameters in the second parameter
* const buffers = new Tone.ToneAudioBuffers({
* urls: {
* A1: "A1.mp3",
* A2: "A2.mp3",
* },
* onload: () => console.log("loaded"),
* baseUrl: "https://tonejs.github.io/audio/casio/"
* });
* @category Core
*/
export class ToneAudioBuffers extends Tone {
readonly name: string = "ToneAudioBuffers";
/**
* All of the buffers
*/
private _buffers: Map<string, ToneAudioBuffer> = new Map();
/**
* A path which is prefixed before every url.
*/
baseUrl: string;
/**
* Keep track of the number of loaded buffers
*/
private _loadingCount = 0;
/**
* @param urls An object literal or array of urls to load.
* @param onload The callback to invoke when the buffers are loaded.
* @param baseUrl A prefix url to add before all the urls
*/
constructor(
urls?: ToneAudioBuffersUrlMap,
onload?: () => void,
baseUrl?: string,
);
constructor(options?: Partial<ToneAudioBuffersOptions>);
constructor() {
super();
const options = optionsFromArguments(
ToneAudioBuffers.getDefaults(), arguments, ["urls", "onload", "baseUrl"], "urls",
);
this.baseUrl = options.baseUrl;
// add each one
Object.keys(options.urls).forEach(name => {
this._loadingCount++;
const url = options.urls[name];
this.add(name, url, this._bufferLoaded.bind(this, options.onload), options.onerror);
});
}
static getDefaults(): ToneAudioBuffersOptions {
return {
baseUrl: "",
onerror: noOp,
onload: noOp,
urls: {},
};
}
/**
* True if the buffers object has a buffer by that name.
* @param name The key or index of the buffer.
*/
has(name: string | number): boolean {
return this._buffers.has(name.toString());
}
/**
* Get a buffer by name. If an array was loaded,
* then use the array index.
* @param name The key or index of the buffer.
*/
get(name: string | number): ToneAudioBuffer {
assert(this.has(name), `ToneAudioBuffers has no buffer named: ${name}`);
return this._buffers.get(name.toString()) as ToneAudioBuffer;
}
/**
* A buffer was loaded. decrement the counter.
*/
private _bufferLoaded(callback: () => void): void {
this._loadingCount--;
if (this._loadingCount === 0 && callback) {
callback();
}
}
/**
* If the buffers are loaded or not
*/
get loaded(): boolean {
return Array.from(this._buffers).every(([_, buffer]) => buffer.loaded);
}
/**
* Add a buffer by name and url to the Buffers
* @param name A unique name to give the buffer
* @param url Either the url of the bufer, or a buffer which will be added with the given name.
* @param callback The callback to invoke when the url is loaded.
* @param onerror Invoked if the buffer can't be loaded
*/
add(
name: string | number,
url: string | AudioBuffer | ToneAudioBuffer,
callback: () => void = noOp,
onerror: (e: Error) => void = noOp,
): this {
if (isString(url)) {
this._buffers.set(name.toString(), new ToneAudioBuffer(this.baseUrl + url, callback, onerror));
} else {
this._buffers.set(name.toString(), new ToneAudioBuffer(url, callback, onerror));
}
return this;
}
dispose(): this {
super.dispose();
this._buffers.forEach(buffer => buffer.dispose());
this._buffers.clear();
return this;
}
}