1 | import { __awaiter } from "tslib";
|
2 | import { getContext } from "../Global.js";
|
3 | import { Tone } from "../Tone.js";
|
4 | import { optionsFromArguments } from "../util/Defaults.js";
|
5 | import { noOp } from "../util/Interface.js";
|
6 | import { isArray, isNumber, isString } from "../util/TypeCheck.js";
|
7 | import { assert } from "../util/Debug.js";
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | export class ToneAudioBuffer extends Tone {
|
19 | constructor() {
|
20 | super();
|
21 | this.name = "ToneAudioBuffer";
|
22 | |
23 |
|
24 |
|
25 | this.onload = noOp;
|
26 | const options = optionsFromArguments(ToneAudioBuffer.getDefaults(), arguments, ["url", "onload", "onerror"]);
|
27 | this.reverse = options.reverse;
|
28 | this.onload = options.onload;
|
29 | if (isString(options.url)) {
|
30 |
|
31 | this.load(options.url).catch(options.onerror);
|
32 | }
|
33 | else if (options.url) {
|
34 | this.set(options.url);
|
35 | }
|
36 | }
|
37 | static getDefaults() {
|
38 | return {
|
39 | onerror: noOp,
|
40 | onload: noOp,
|
41 | reverse: false,
|
42 | };
|
43 | }
|
44 | |
45 |
|
46 |
|
47 | get sampleRate() {
|
48 | if (this._buffer) {
|
49 | return this._buffer.sampleRate;
|
50 | }
|
51 | else {
|
52 | return getContext().sampleRate;
|
53 | }
|
54 | }
|
55 | |
56 |
|
57 |
|
58 | set(buffer) {
|
59 | if (buffer instanceof ToneAudioBuffer) {
|
60 |
|
61 | if (buffer.loaded) {
|
62 | this._buffer = buffer.get();
|
63 | }
|
64 | else {
|
65 |
|
66 | buffer.onload = () => {
|
67 | this.set(buffer);
|
68 | this.onload(this);
|
69 | };
|
70 | }
|
71 | }
|
72 | else {
|
73 | this._buffer = buffer;
|
74 | }
|
75 |
|
76 | if (this._reversed) {
|
77 | this._reverse();
|
78 | }
|
79 | return this;
|
80 | }
|
81 | |
82 |
|
83 |
|
84 | get() {
|
85 | return this._buffer;
|
86 | }
|
87 | |
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | load(url) {
|
94 | return __awaiter(this, void 0, void 0, function* () {
|
95 | const doneLoading = ToneAudioBuffer.load(url).then((audioBuffer) => {
|
96 | this.set(audioBuffer);
|
97 |
|
98 | this.onload(this);
|
99 | });
|
100 | ToneAudioBuffer.downloads.push(doneLoading);
|
101 | try {
|
102 | yield doneLoading;
|
103 | }
|
104 | finally {
|
105 |
|
106 | const index = ToneAudioBuffer.downloads.indexOf(doneLoading);
|
107 | ToneAudioBuffer.downloads.splice(index, 1);
|
108 | }
|
109 | return this;
|
110 | });
|
111 | }
|
112 | |
113 |
|
114 |
|
115 | dispose() {
|
116 | super.dispose();
|
117 | this._buffer = undefined;
|
118 | return this;
|
119 | }
|
120 | |
121 |
|
122 |
|
123 |
|
124 |
|
125 | fromArray(array) {
|
126 | const isMultidimensional = isArray(array) && array[0].length > 0;
|
127 | const channels = isMultidimensional ? array.length : 1;
|
128 | const len = isMultidimensional
|
129 | ? array[0].length
|
130 | : array.length;
|
131 | const context = getContext();
|
132 | const buffer = context.createBuffer(channels, len, context.sampleRate);
|
133 | const multiChannelArray = !isMultidimensional && channels === 1
|
134 | ? [array]
|
135 | : array;
|
136 | for (let c = 0; c < channels; c++) {
|
137 | buffer.copyToChannel(multiChannelArray[c], c);
|
138 | }
|
139 | this._buffer = buffer;
|
140 | return this;
|
141 | }
|
142 | |
143 |
|
144 |
|
145 |
|
146 | toMono(chanNum) {
|
147 | if (isNumber(chanNum)) {
|
148 | this.fromArray(this.toArray(chanNum));
|
149 | }
|
150 | else {
|
151 | let outputArray = new Float32Array(this.length);
|
152 | const numChannels = this.numberOfChannels;
|
153 | for (let channel = 0; channel < numChannels; channel++) {
|
154 | const channelArray = this.toArray(channel);
|
155 | for (let i = 0; i < channelArray.length; i++) {
|
156 | outputArray[i] += channelArray[i];
|
157 | }
|
158 | }
|
159 |
|
160 | outputArray = outputArray.map((sample) => sample / numChannels);
|
161 | this.fromArray(outputArray);
|
162 | }
|
163 | return this;
|
164 | }
|
165 | |
166 |
|
167 |
|
168 |
|
169 |
|
170 | toArray(channel) {
|
171 | if (isNumber(channel)) {
|
172 | return this.getChannelData(channel);
|
173 | }
|
174 | else if (this.numberOfChannels === 1) {
|
175 | return this.toArray(0);
|
176 | }
|
177 | else {
|
178 | const ret = [];
|
179 | for (let c = 0; c < this.numberOfChannels; c++) {
|
180 | ret[c] = this.getChannelData(c);
|
181 | }
|
182 | return ret;
|
183 | }
|
184 | }
|
185 | |
186 |
|
187 |
|
188 |
|
189 |
|
190 | getChannelData(channel) {
|
191 | if (this._buffer) {
|
192 | return this._buffer.getChannelData(channel);
|
193 | }
|
194 | else {
|
195 | return new Float32Array(0);
|
196 | }
|
197 | }
|
198 | |
199 |
|
200 |
|
201 |
|
202 |
|
203 |
|
204 | slice(start, end = this.duration) {
|
205 | assert(this.loaded, "Buffer is not loaded");
|
206 | const startSamples = Math.floor(start * this.sampleRate);
|
207 | const endSamples = Math.floor(end * this.sampleRate);
|
208 | assert(startSamples < endSamples, "The start time must be less than the end time");
|
209 | const length = endSamples - startSamples;
|
210 | const retBuffer = getContext().createBuffer(this.numberOfChannels, length, this.sampleRate);
|
211 | for (let channel = 0; channel < this.numberOfChannels; channel++) {
|
212 | retBuffer.copyToChannel(this.getChannelData(channel).subarray(startSamples, endSamples), channel);
|
213 | }
|
214 | return new ToneAudioBuffer(retBuffer);
|
215 | }
|
216 | |
217 |
|
218 |
|
219 | _reverse() {
|
220 | if (this.loaded) {
|
221 | for (let i = 0; i < this.numberOfChannels; i++) {
|
222 | this.getChannelData(i).reverse();
|
223 | }
|
224 | }
|
225 | return this;
|
226 | }
|
227 | |
228 |
|
229 |
|
230 | get loaded() {
|
231 | return this.length > 0;
|
232 | }
|
233 | |
234 |
|
235 |
|
236 | get duration() {
|
237 | if (this._buffer) {
|
238 | return this._buffer.duration;
|
239 | }
|
240 | else {
|
241 | return 0;
|
242 | }
|
243 | }
|
244 | |
245 |
|
246 |
|
247 | get length() {
|
248 | if (this._buffer) {
|
249 | return this._buffer.length;
|
250 | }
|
251 | else {
|
252 | return 0;
|
253 | }
|
254 | }
|
255 | |
256 |
|
257 |
|
258 | get numberOfChannels() {
|
259 | if (this._buffer) {
|
260 | return this._buffer.numberOfChannels;
|
261 | }
|
262 | else {
|
263 | return 0;
|
264 | }
|
265 | }
|
266 | |
267 |
|
268 |
|
269 | get reverse() {
|
270 | return this._reversed;
|
271 | }
|
272 | set reverse(rev) {
|
273 | if (this._reversed !== rev) {
|
274 | this._reversed = rev;
|
275 | this._reverse();
|
276 | }
|
277 | }
|
278 | |
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 | static fromArray(array) {
|
285 | return new ToneAudioBuffer().fromArray(array);
|
286 | }
|
287 | |
288 |
|
289 |
|
290 |
|
291 |
|
292 | static fromUrl(url) {
|
293 | return __awaiter(this, void 0, void 0, function* () {
|
294 | const buffer = new ToneAudioBuffer();
|
295 | return yield buffer.load(url);
|
296 | });
|
297 | }
|
298 | |
299 |
|
300 |
|
301 | static load(url) {
|
302 | return __awaiter(this, void 0, void 0, function* () {
|
303 |
|
304 | const matches = url.match(/\[([^\]\[]+\|.+)\]$/);
|
305 | if (matches) {
|
306 | const extensions = matches[1].split("|");
|
307 | let extension = extensions[0];
|
308 | for (const ext of extensions) {
|
309 | if (ToneAudioBuffer.supportsType(ext)) {
|
310 | extension = ext;
|
311 | break;
|
312 | }
|
313 | }
|
314 | url = url.replace(matches[0], extension);
|
315 | }
|
316 |
|
317 | const baseUrl = ToneAudioBuffer.baseUrl === "" ||
|
318 | ToneAudioBuffer.baseUrl.endsWith("/")
|
319 | ? ToneAudioBuffer.baseUrl
|
320 | : ToneAudioBuffer.baseUrl + "/";
|
321 |
|
322 | const location = document.createElement("a");
|
323 | location.href = baseUrl + url;
|
324 | location.pathname = (location.pathname + location.hash)
|
325 | .split("/")
|
326 | .map(encodeURIComponent)
|
327 | .join("/");
|
328 | const response = yield fetch(location.href);
|
329 | if (!response.ok) {
|
330 | throw new Error(`could not load url: ${url}`);
|
331 | }
|
332 | const arrayBuffer = yield response.arrayBuffer();
|
333 | const audioBuffer = yield getContext().decodeAudioData(arrayBuffer);
|
334 | return audioBuffer;
|
335 | });
|
336 | }
|
337 | |
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 | static supportsType(url) {
|
347 | const extensions = url.split(".");
|
348 | const extension = extensions[extensions.length - 1];
|
349 | const response = document
|
350 | .createElement("audio")
|
351 | .canPlayType("audio/" + extension);
|
352 | return response !== "";
|
353 | }
|
354 | |
355 |
|
356 |
|
357 | static loaded() {
|
358 | return __awaiter(this, void 0, void 0, function* () {
|
359 |
|
360 | yield Promise.resolve();
|
361 | while (ToneAudioBuffer.downloads.length) {
|
362 | yield ToneAudioBuffer.downloads[0];
|
363 | }
|
364 | });
|
365 | }
|
366 | }
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 | ToneAudioBuffer.baseUrl = "";
|
374 |
|
375 |
|
376 |
|
377 | ToneAudioBuffer.downloads = [];
|
378 |
|
\ | No newline at end of file |