tone 14.8.33 → 14.8.34
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/Tone/source/Source.ts +84 -27
- package/Tone/source/buffer/Player.test.ts +98 -49
- package/Tone/version.ts +1 -1
- package/build/Tone.js +1 -1
- package/build/Tone.js.map +1 -1
- package/build/esm/source/Source.js +27 -14
- package/build/esm/source/Source.js.map +1 -1
- package/build/esm/version.js +1 -1
- package/docs/tone.json +1 -1
- package/package.json +1 -1
package/Tone/source/Source.ts
CHANGED
|
@@ -2,11 +2,19 @@ import { Volume } from "../component/channel/Volume";
|
|
|
2
2
|
import "../core/context/Destination";
|
|
3
3
|
import "../core/clock/Transport";
|
|
4
4
|
import { Param } from "../core/context/Param";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
OutputNode,
|
|
7
|
+
ToneAudioNode,
|
|
8
|
+
ToneAudioNodeOptions,
|
|
9
|
+
} from "../core/context/ToneAudioNode";
|
|
6
10
|
import { Decibels, Seconds, Time } from "../core/type/Units";
|
|
7
11
|
import { defaultArg } from "../core/util/Defaults";
|
|
8
12
|
import { noOp, readOnly } from "../core/util/Interface";
|
|
9
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BasicPlaybackState,
|
|
15
|
+
StateTimeline,
|
|
16
|
+
StateTimelineEvent,
|
|
17
|
+
} from "../core/util/StateTimeline";
|
|
10
18
|
import { isDefined, isUndef } from "../core/util/TypeCheck";
|
|
11
19
|
import { assert, assertContextRunning } from "../core/util/Debug";
|
|
12
20
|
import { GT } from "../core/util/Math";
|
|
@@ -20,9 +28,9 @@ export interface SourceOptions extends ToneAudioNodeOptions {
|
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
/**
|
|
23
|
-
* Base class for sources.
|
|
31
|
+
* Base class for sources.
|
|
24
32
|
* start/stop of this.context.transport.
|
|
25
|
-
*
|
|
33
|
+
*
|
|
26
34
|
* ```
|
|
27
35
|
* // Multiple state change events can be chained together,
|
|
28
36
|
* // but must be set in the correct order and with ascending times
|
|
@@ -36,8 +44,9 @@ export interface SourceOptions extends ToneAudioNodeOptions {
|
|
|
36
44
|
* state.start("+0.3").stop("+0.2");
|
|
37
45
|
* ```
|
|
38
46
|
*/
|
|
39
|
-
export abstract class Source<
|
|
40
|
-
|
|
47
|
+
export abstract class Source<
|
|
48
|
+
Options extends SourceOptions
|
|
49
|
+
> extends ToneAudioNode<Options> {
|
|
41
50
|
/**
|
|
42
51
|
* The output volume node
|
|
43
52
|
*/
|
|
@@ -129,7 +138,9 @@ export abstract class Source<Options extends SourceOptions> extends ToneAudioNod
|
|
|
129
138
|
get state(): BasicPlaybackState {
|
|
130
139
|
if (this._synced) {
|
|
131
140
|
if (this.context.transport.state === "started") {
|
|
132
|
-
return this._state.getValueAtTime(
|
|
141
|
+
return this._state.getValueAtTime(
|
|
142
|
+
this.context.transport.seconds
|
|
143
|
+
) as BasicPlaybackState;
|
|
133
144
|
} else {
|
|
134
145
|
return "stopped";
|
|
135
146
|
}
|
|
@@ -155,7 +166,11 @@ export abstract class Source<Options extends SourceOptions> extends ToneAudioNod
|
|
|
155
166
|
// overwrite these functions
|
|
156
167
|
protected abstract _start(time: Time, offset?: Time, duration?: Time): void;
|
|
157
168
|
protected abstract _stop(time: Time): void;
|
|
158
|
-
protected abstract _restart(
|
|
169
|
+
protected abstract _restart(
|
|
170
|
+
time: Seconds,
|
|
171
|
+
offset?: Time,
|
|
172
|
+
duration?: Time
|
|
173
|
+
): void;
|
|
159
174
|
|
|
160
175
|
/**
|
|
161
176
|
* Ensure that the scheduled time is not before the current time.
|
|
@@ -178,12 +193,24 @@ export abstract class Source<Options extends SourceOptions> extends ToneAudioNod
|
|
|
178
193
|
* source.start("+0.5"); // starts the source 0.5 seconds from now
|
|
179
194
|
*/
|
|
180
195
|
start(time?: Time, offset?: Time, duration?: Time): this {
|
|
181
|
-
let computedTime =
|
|
196
|
+
let computedTime =
|
|
197
|
+
isUndef(time) && this._synced
|
|
198
|
+
? this.context.transport.seconds
|
|
199
|
+
: this.toSeconds(time);
|
|
182
200
|
computedTime = this._clampToCurrentTime(computedTime);
|
|
183
201
|
// if it's started, stop it and restart it
|
|
184
|
-
if (
|
|
202
|
+
if (
|
|
203
|
+
!this._synced &&
|
|
204
|
+
this._state.getValueAtTime(computedTime) === "started"
|
|
205
|
+
) {
|
|
185
206
|
// time should be strictly greater than the previous start time
|
|
186
|
-
assert(
|
|
207
|
+
assert(
|
|
208
|
+
GT(
|
|
209
|
+
computedTime,
|
|
210
|
+
(this._state.get(computedTime) as StateTimelineEvent).time
|
|
211
|
+
),
|
|
212
|
+
"Start time must be strictly greater than previous start time"
|
|
213
|
+
);
|
|
187
214
|
this._state.cancel(computedTime);
|
|
188
215
|
this._state.setStateAtTime("started", computedTime);
|
|
189
216
|
this.log("restart", computedTime);
|
|
@@ -196,18 +223,26 @@ export abstract class Source<Options extends SourceOptions> extends ToneAudioNod
|
|
|
196
223
|
const event = this._state.get(computedTime);
|
|
197
224
|
if (event) {
|
|
198
225
|
event.offset = this.toSeconds(defaultArg(offset, 0));
|
|
199
|
-
event.duration = duration
|
|
226
|
+
event.duration = duration
|
|
227
|
+
? this.toSeconds(duration)
|
|
228
|
+
: undefined;
|
|
200
229
|
}
|
|
201
|
-
const sched = this.context.transport.schedule(t => {
|
|
230
|
+
const sched = this.context.transport.schedule((t) => {
|
|
202
231
|
this._start(t, offset, duration);
|
|
203
232
|
}, computedTime);
|
|
204
233
|
this._scheduled.push(sched);
|
|
205
234
|
|
|
206
235
|
// if the transport is already started
|
|
207
236
|
// and the time is greater than where the transport is
|
|
208
|
-
if (
|
|
209
|
-
this.context.transport.
|
|
210
|
-
this.
|
|
237
|
+
if (
|
|
238
|
+
this.context.transport.state === "started" &&
|
|
239
|
+
this.context.transport.getSecondsAtTime(this.immediate()) >
|
|
240
|
+
computedTime
|
|
241
|
+
) {
|
|
242
|
+
this._syncedStart(
|
|
243
|
+
this.now(),
|
|
244
|
+
this.context.transport.seconds
|
|
245
|
+
);
|
|
211
246
|
}
|
|
212
247
|
} else {
|
|
213
248
|
assertContextRunning(this.context);
|
|
@@ -227,14 +262,23 @@ export abstract class Source<Options extends SourceOptions> extends ToneAudioNod
|
|
|
227
262
|
* source.stop("+0.5"); // stops the source 0.5 seconds from now
|
|
228
263
|
*/
|
|
229
264
|
stop(time?: Time): this {
|
|
230
|
-
let computedTime =
|
|
265
|
+
let computedTime =
|
|
266
|
+
isUndef(time) && this._synced
|
|
267
|
+
? this.context.transport.seconds
|
|
268
|
+
: this.toSeconds(time);
|
|
231
269
|
computedTime = this._clampToCurrentTime(computedTime);
|
|
232
|
-
if (
|
|
270
|
+
if (
|
|
271
|
+
this._state.getValueAtTime(computedTime) === "started" ||
|
|
272
|
+
isDefined(this._state.getNextState("started", computedTime))
|
|
273
|
+
) {
|
|
233
274
|
this.log("stop", computedTime);
|
|
234
275
|
if (!this._synced) {
|
|
235
276
|
this._stop(computedTime);
|
|
236
277
|
} else {
|
|
237
|
-
const sched = this.context.transport.schedule(
|
|
278
|
+
const sched = this.context.transport.schedule(
|
|
279
|
+
this._stop.bind(this),
|
|
280
|
+
computedTime
|
|
281
|
+
);
|
|
238
282
|
this._scheduled.push(sched);
|
|
239
283
|
}
|
|
240
284
|
this._state.cancel(computedTime);
|
|
@@ -274,23 +318,36 @@ export abstract class Source<Options extends SourceOptions> extends ToneAudioNod
|
|
|
274
318
|
if (!this._synced) {
|
|
275
319
|
this._synced = true;
|
|
276
320
|
this._syncedStart = (time, offset) => {
|
|
277
|
-
if (offset
|
|
321
|
+
if (GT(offset, 0)) {
|
|
278
322
|
// get the playback state at that time
|
|
279
323
|
const stateEvent = this._state.get(offset);
|
|
280
324
|
// listen for start events which may occur in the middle of the sync'ed time
|
|
281
|
-
if (
|
|
325
|
+
if (
|
|
326
|
+
stateEvent &&
|
|
327
|
+
stateEvent.state === "started" &&
|
|
328
|
+
stateEvent.time !== offset
|
|
329
|
+
) {
|
|
282
330
|
// get the offset
|
|
283
|
-
const startOffset =
|
|
331
|
+
const startOffset =
|
|
332
|
+
offset - this.toSeconds(stateEvent.time);
|
|
284
333
|
let duration: number | undefined;
|
|
285
334
|
if (stateEvent.duration) {
|
|
286
|
-
duration =
|
|
335
|
+
duration =
|
|
336
|
+
this.toSeconds(stateEvent.duration) -
|
|
337
|
+
startOffset;
|
|
287
338
|
}
|
|
288
|
-
this._start(
|
|
339
|
+
this._start(
|
|
340
|
+
time,
|
|
341
|
+
this.toSeconds(stateEvent.offset) + startOffset,
|
|
342
|
+
duration
|
|
343
|
+
);
|
|
289
344
|
}
|
|
290
345
|
}
|
|
291
346
|
};
|
|
292
|
-
this._syncedStop = time => {
|
|
293
|
-
const seconds = this.context.transport.getSecondsAtTime(
|
|
347
|
+
this._syncedStop = (time) => {
|
|
348
|
+
const seconds = this.context.transport.getSecondsAtTime(
|
|
349
|
+
Math.max(time - this.sampleTime, 0)
|
|
350
|
+
);
|
|
294
351
|
if (this._state.getValueAtTime(seconds) === "started") {
|
|
295
352
|
this._stop(time);
|
|
296
353
|
}
|
|
@@ -317,7 +374,7 @@ export abstract class Source<Options extends SourceOptions> extends ToneAudioNod
|
|
|
317
374
|
}
|
|
318
375
|
this._synced = false;
|
|
319
376
|
// clear all of the scheduled ids
|
|
320
|
-
this._scheduled.forEach(id => this.context.transport.clear(id));
|
|
377
|
+
this._scheduled.forEach((id) => this.context.transport.clear(id));
|
|
321
378
|
this._scheduled = [];
|
|
322
379
|
this._state.cancel(0);
|
|
323
380
|
// stop it also
|
|
@@ -8,7 +8,6 @@ import { getContext } from "Tone/core/Global";
|
|
|
8
8
|
import { Player } from "./Player";
|
|
9
9
|
|
|
10
10
|
describe("Player", () => {
|
|
11
|
-
|
|
12
11
|
const buffer = new ToneAudioBuffer();
|
|
13
12
|
|
|
14
13
|
beforeEach(() => {
|
|
@@ -20,15 +19,18 @@ describe("Player", () => {
|
|
|
20
19
|
SourceTests(Player, buffer);
|
|
21
20
|
|
|
22
21
|
it("matches a file", () => {
|
|
23
|
-
return CompareToFile(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
return CompareToFile(
|
|
23
|
+
() => {
|
|
24
|
+
const player = new Player(buffer).toDestination();
|
|
25
|
+
player.start(0.1).stop(0.2);
|
|
26
|
+
player.playbackRate = 2;
|
|
27
|
+
},
|
|
28
|
+
"player.wav",
|
|
29
|
+
0.005
|
|
30
|
+
);
|
|
28
31
|
});
|
|
29
32
|
|
|
30
33
|
context("Constructor", () => {
|
|
31
|
-
|
|
32
34
|
it("can be constructed with a Tone.Buffer", () => {
|
|
33
35
|
const player = new Player(buffer);
|
|
34
36
|
expect(player.buffer.get()).to.equal(buffer.get());
|
|
@@ -60,7 +62,6 @@ describe("Player", () => {
|
|
|
60
62
|
});
|
|
61
63
|
|
|
62
64
|
context("onstop", () => {
|
|
63
|
-
|
|
64
65
|
it("invokes the onstop method when the player is explicitly stopped", () => {
|
|
65
66
|
let wasInvoked = false;
|
|
66
67
|
return Offline(() => {
|
|
@@ -105,7 +106,6 @@ describe("Player", () => {
|
|
|
105
106
|
});
|
|
106
107
|
|
|
107
108
|
context("Loading", () => {
|
|
108
|
-
|
|
109
109
|
it("loads a url which was passed in", (done) => {
|
|
110
110
|
const player = new Player("./audio/sine.wav", () => {
|
|
111
111
|
expect(player.loaded).to.be.true;
|
|
@@ -131,12 +131,12 @@ describe("Player", () => {
|
|
|
131
131
|
|
|
132
132
|
it("invokes onerror if no url", (done) => {
|
|
133
133
|
const source = new Player({
|
|
134
|
-
url: "./nosuchfile.wav",
|
|
134
|
+
url: "./nosuchfile.wav",
|
|
135
135
|
onerror(e) {
|
|
136
136
|
expect(e).to.be.instanceOf(Error);
|
|
137
137
|
source.dispose();
|
|
138
138
|
done();
|
|
139
|
-
}
|
|
139
|
+
},
|
|
140
140
|
});
|
|
141
141
|
});
|
|
142
142
|
|
|
@@ -152,11 +152,9 @@ describe("Player", () => {
|
|
|
152
152
|
url: "./audio/sine.wav",
|
|
153
153
|
});
|
|
154
154
|
});
|
|
155
|
-
|
|
156
155
|
});
|
|
157
156
|
|
|
158
157
|
context("Reverse", () => {
|
|
159
|
-
|
|
160
158
|
it("can get/set reverse", () => {
|
|
161
159
|
const player = new Player();
|
|
162
160
|
player.reverse = true;
|
|
@@ -166,7 +164,9 @@ describe("Player", () => {
|
|
|
166
164
|
|
|
167
165
|
it("can be played in reverse", () => {
|
|
168
166
|
const shorterBuffer = buffer.slice(0, buffer.duration / 2);
|
|
169
|
-
const audioBuffer = (
|
|
167
|
+
const audioBuffer = (
|
|
168
|
+
shorterBuffer.get() as AudioBuffer
|
|
169
|
+
).getChannelData(0);
|
|
170
170
|
const lastSample = audioBuffer[audioBuffer.length - 1];
|
|
171
171
|
expect(lastSample).to.not.equal(0);
|
|
172
172
|
return Offline(() => {
|
|
@@ -180,11 +180,9 @@ describe("Player", () => {
|
|
|
180
180
|
expect(firstSample).to.equal(lastSample);
|
|
181
181
|
});
|
|
182
182
|
});
|
|
183
|
-
|
|
184
183
|
});
|
|
185
184
|
|
|
186
185
|
context("Looping", () => {
|
|
187
|
-
|
|
188
186
|
beforeEach(() => {
|
|
189
187
|
return buffer.load("./audio/short_sine.wav");
|
|
190
188
|
});
|
|
@@ -243,7 +241,7 @@ describe("Player", () => {
|
|
|
243
241
|
player.toDestination();
|
|
244
242
|
player.start(0);
|
|
245
243
|
player.loop = true;
|
|
246
|
-
}, buffer.duration * 1.5).then(buff => {
|
|
244
|
+
}, buffer.duration * 1.5).then((buff) => {
|
|
247
245
|
expect(buff.getRmsAtTime(0)).to.be.above(0);
|
|
248
246
|
expect(buff.getRmsAtTime(buffer.duration * 0.5)).to.be.above(0);
|
|
249
247
|
expect(buff.getRmsAtTime(buffer.duration)).to.be.above(0);
|
|
@@ -252,7 +250,8 @@ describe("Player", () => {
|
|
|
252
250
|
});
|
|
253
251
|
|
|
254
252
|
it("offset is the loopStart when set to loop", () => {
|
|
255
|
-
const testSample =
|
|
253
|
+
const testSample =
|
|
254
|
+
buffer.toArray(0)[Math.floor(0.1 * getContext().sampleRate)];
|
|
256
255
|
return Offline(() => {
|
|
257
256
|
const player = new Player(buffer);
|
|
258
257
|
player.loopStart = 0.1;
|
|
@@ -271,10 +270,10 @@ describe("Player", () => {
|
|
|
271
270
|
player.loop = true;
|
|
272
271
|
player.toDestination();
|
|
273
272
|
player.start(0, 0, playDur);
|
|
274
|
-
}, buffer.duration * 2).then(buff => {
|
|
273
|
+
}, buffer.duration * 2).then((buff) => {
|
|
275
274
|
for (let time = 0; time < buffer.duration * 2; time += 0.1) {
|
|
276
275
|
const val = buff.getRmsAtTime(time);
|
|
277
|
-
if (time <
|
|
276
|
+
if (time < playDur - 0.01) {
|
|
278
277
|
expect(val).to.be.greaterThan(0);
|
|
279
278
|
} else if (time > playDur) {
|
|
280
279
|
expect(val).to.equal(0);
|
|
@@ -286,9 +285,11 @@ describe("Player", () => {
|
|
|
286
285
|
it("correctly compensates if the offset is greater than the loopEnd", () => {
|
|
287
286
|
return Offline(() => {
|
|
288
287
|
// make a ramp between 0-1
|
|
289
|
-
const ramp = new Float32Array(
|
|
288
|
+
const ramp = new Float32Array(
|
|
289
|
+
Math.floor(getContext().sampleRate * 0.3)
|
|
290
|
+
);
|
|
290
291
|
for (let i = 0; i < ramp.length; i++) {
|
|
291
|
-
ramp[i] = (i /
|
|
292
|
+
ramp[i] = (i / ramp.length) * 0.3;
|
|
292
293
|
}
|
|
293
294
|
const buff = ToneAudioBuffer.fromArray(ramp);
|
|
294
295
|
const player = new Player(buff).toDestination();
|
|
@@ -306,7 +307,6 @@ describe("Player", () => {
|
|
|
306
307
|
});
|
|
307
308
|
});
|
|
308
309
|
});
|
|
309
|
-
|
|
310
310
|
});
|
|
311
311
|
|
|
312
312
|
context("PlaybackRate", () => {
|
|
@@ -344,7 +344,6 @@ describe("Player", () => {
|
|
|
344
344
|
});
|
|
345
345
|
|
|
346
346
|
context("Get/Set", () => {
|
|
347
|
-
|
|
348
347
|
it("can be set with an options object", () => {
|
|
349
348
|
const player = new Player();
|
|
350
349
|
expect(player.loop).to.be.false;
|
|
@@ -399,13 +398,12 @@ describe("Player", () => {
|
|
|
399
398
|
expect(player.playbackRate).to.equal(0.5);
|
|
400
399
|
player.dispose();
|
|
401
400
|
});
|
|
402
|
-
|
|
403
401
|
});
|
|
404
402
|
|
|
405
403
|
context("Start Scheduling", () => {
|
|
406
|
-
|
|
407
404
|
it("can be start with an offset", () => {
|
|
408
|
-
const testSample =
|
|
405
|
+
const testSample =
|
|
406
|
+
buffer.toArray(0)[Math.floor(0.1 * getContext().sampleRate)];
|
|
409
407
|
return Offline(() => {
|
|
410
408
|
const player = new Player(buffer.get());
|
|
411
409
|
player.toDestination();
|
|
@@ -418,9 +416,11 @@ describe("Player", () => {
|
|
|
418
416
|
it("is stopped and restarted when start is called twice", () => {
|
|
419
417
|
return Offline(() => {
|
|
420
418
|
// make a ramp between 0-1
|
|
421
|
-
const ramp = new Float32Array(
|
|
419
|
+
const ramp = new Float32Array(
|
|
420
|
+
Math.floor(getContext().sampleRate * 0.3)
|
|
421
|
+
);
|
|
422
422
|
for (let i = 0; i < ramp.length; i++) {
|
|
423
|
-
ramp[i] =
|
|
423
|
+
ramp[i] = i / (ramp.length - 1);
|
|
424
424
|
}
|
|
425
425
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
426
426
|
const player = new Player(buff).toDestination();
|
|
@@ -442,9 +442,11 @@ describe("Player", () => {
|
|
|
442
442
|
|
|
443
443
|
it("can seek to a position at the given time", () => {
|
|
444
444
|
return Offline(() => {
|
|
445
|
-
const ramp = new Float32Array(
|
|
445
|
+
const ramp = new Float32Array(
|
|
446
|
+
Math.floor(getContext().sampleRate * 0.3)
|
|
447
|
+
);
|
|
446
448
|
for (let i = 0; i < ramp.length; i++) {
|
|
447
|
-
ramp[i] = (i /
|
|
449
|
+
ramp[i] = (i / ramp.length) * 0.3;
|
|
448
450
|
}
|
|
449
451
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
450
452
|
const player = new Player(buff).toDestination();
|
|
@@ -466,7 +468,7 @@ describe("Player", () => {
|
|
|
466
468
|
const player = new Player(buffer);
|
|
467
469
|
player.toDestination();
|
|
468
470
|
player.start(0).stop(0.1);
|
|
469
|
-
return time => {
|
|
471
|
+
return (time) => {
|
|
470
472
|
whenBetween(time, 0.1, Infinity, () => {
|
|
471
473
|
expect(player.state).to.equal("stopped");
|
|
472
474
|
});
|
|
@@ -475,9 +477,13 @@ describe("Player", () => {
|
|
|
475
477
|
});
|
|
476
478
|
};
|
|
477
479
|
}, 0.3).then((buff) => {
|
|
478
|
-
buff.forEachBetween(
|
|
479
|
-
|
|
480
|
-
|
|
480
|
+
buff.forEachBetween(
|
|
481
|
+
(sample) => {
|
|
482
|
+
expect(sample).to.equal(0);
|
|
483
|
+
},
|
|
484
|
+
0.11,
|
|
485
|
+
0.15
|
|
486
|
+
);
|
|
481
487
|
});
|
|
482
488
|
});
|
|
483
489
|
|
|
@@ -495,7 +501,10 @@ describe("Player", () => {
|
|
|
495
501
|
return Offline(() => {
|
|
496
502
|
const player = new Player(buffer);
|
|
497
503
|
player.toDestination();
|
|
498
|
-
player
|
|
504
|
+
player
|
|
505
|
+
.start(0, 0, 0.05)
|
|
506
|
+
.start(0.1, 0, 0.05)
|
|
507
|
+
.start(0.2, 0, 0.05);
|
|
499
508
|
player.stop(0.1);
|
|
500
509
|
}, 0.3).then((buff) => {
|
|
501
510
|
expect(buff.getTimeOfLastSound()).to.be.closeTo(0.1, 0.02);
|
|
@@ -518,7 +527,7 @@ describe("Player", () => {
|
|
|
518
527
|
const player = new Player(buffer);
|
|
519
528
|
player.toDestination();
|
|
520
529
|
player.start(0, 0, 0.1);
|
|
521
|
-
return time => {
|
|
530
|
+
return (time) => {
|
|
522
531
|
whenBetween(time, 0.1, Infinity, () => {
|
|
523
532
|
expect(player.state).to.equal("stopped");
|
|
524
533
|
});
|
|
@@ -549,17 +558,42 @@ describe("Player", () => {
|
|
|
549
558
|
|
|
550
559
|
it("plays synced to the Transport", () => {
|
|
551
560
|
return Offline(({ transport }) => {
|
|
552
|
-
const player = new Player(buffer)
|
|
561
|
+
const player = new Player(buffer)
|
|
562
|
+
.sync()
|
|
563
|
+
.start(0)
|
|
564
|
+
.toDestination();
|
|
553
565
|
transport.start(0);
|
|
554
566
|
}, 0.05).then((buff) => {
|
|
555
567
|
expect(buff.isSilent()).to.be.false;
|
|
556
568
|
});
|
|
557
569
|
});
|
|
558
570
|
|
|
571
|
+
it("does not play twice when the offset is very small", () => {
|
|
572
|
+
// addresses #999 and #944
|
|
573
|
+
return CompareToFile(
|
|
574
|
+
() => {
|
|
575
|
+
const player = new Player(buffer).toDestination();
|
|
576
|
+
player.sync().start(0);
|
|
577
|
+
getContext().transport.bpm.value = 125;
|
|
578
|
+
getContext().transport.setLoopPoints(0, "1:0:0");
|
|
579
|
+
getContext().transport.loop = true;
|
|
580
|
+
getContext().transport.start(0);
|
|
581
|
+
},
|
|
582
|
+
"playerSyncLoop.wav",
|
|
583
|
+
0.01
|
|
584
|
+
);
|
|
585
|
+
});
|
|
586
|
+
|
|
559
587
|
it("offsets correctly when started by the Transport", () => {
|
|
560
|
-
const testSample =
|
|
588
|
+
const testSample =
|
|
589
|
+
buffer.toArray(0)[
|
|
590
|
+
Math.floor(0.13125 * getContext().sampleRate)
|
|
591
|
+
];
|
|
561
592
|
return Offline(({ transport }) => {
|
|
562
|
-
const player = new Player(buffer)
|
|
593
|
+
const player = new Player(buffer)
|
|
594
|
+
.sync()
|
|
595
|
+
.start(0, 0.1)
|
|
596
|
+
.toDestination();
|
|
563
597
|
transport.start(0, 0.03125);
|
|
564
598
|
}, 0.05).then((buff) => {
|
|
565
599
|
expect(buff.toArray()[0][0]).to.equal(testSample);
|
|
@@ -569,9 +603,11 @@ describe("Player", () => {
|
|
|
569
603
|
it("starts at the correct position when Transport is offset and playbackRate is not 1", () => {
|
|
570
604
|
return Offline(({ transport }) => {
|
|
571
605
|
// make a ramp between 0-1
|
|
572
|
-
const ramp = new Float32Array(
|
|
606
|
+
const ramp = new Float32Array(
|
|
607
|
+
Math.floor(getContext().sampleRate * 0.3)
|
|
608
|
+
);
|
|
573
609
|
for (let i = 0; i < ramp.length; i++) {
|
|
574
|
-
ramp[i] =
|
|
610
|
+
ramp[i] = i / ramp.length;
|
|
575
611
|
}
|
|
576
612
|
const buff = ToneAudioBuffer.fromArray(ramp);
|
|
577
613
|
const player = new Player(buff).toDestination();
|
|
@@ -586,9 +622,11 @@ describe("Player", () => {
|
|
|
586
622
|
|
|
587
623
|
it("starts with an offset when synced and started after Transport is running", () => {
|
|
588
624
|
return Offline(({ transport }) => {
|
|
589
|
-
const ramp = new Float32Array(
|
|
625
|
+
const ramp = new Float32Array(
|
|
626
|
+
Math.floor(getContext().sampleRate * 0.3)
|
|
627
|
+
);
|
|
590
628
|
for (let i = 0; i < ramp.length; i++) {
|
|
591
|
-
ramp[i] = (i /
|
|
629
|
+
ramp[i] = (i / ramp.length) * 0.3;
|
|
592
630
|
}
|
|
593
631
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
594
632
|
const player = new Player(buff).toDestination();
|
|
@@ -606,9 +644,11 @@ describe("Player", () => {
|
|
|
606
644
|
|
|
607
645
|
it("can pass in an offset when synced and started after Transport is running", () => {
|
|
608
646
|
return Offline(({ transport }) => {
|
|
609
|
-
const ramp = new Float32Array(
|
|
647
|
+
const ramp = new Float32Array(
|
|
648
|
+
Math.floor(getContext().sampleRate * 0.3)
|
|
649
|
+
);
|
|
610
650
|
for (let i = 0; i < ramp.length; i++) {
|
|
611
|
-
ramp[i] = (i /
|
|
651
|
+
ramp[i] = (i / ramp.length) * 0.3;
|
|
612
652
|
}
|
|
613
653
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
614
654
|
const player = new Player(buff).toDestination();
|
|
@@ -630,12 +670,18 @@ describe("Player", () => {
|
|
|
630
670
|
it("fades in and out correctly", () => {
|
|
631
671
|
let duration = 0.5;
|
|
632
672
|
return Offline(() => {
|
|
633
|
-
const onesArray = new Float32Array(
|
|
673
|
+
const onesArray = new Float32Array(
|
|
674
|
+
getContext().sampleRate * duration
|
|
675
|
+
);
|
|
634
676
|
onesArray.forEach((sample, index) => {
|
|
635
677
|
onesArray[index] = 1;
|
|
636
678
|
});
|
|
637
679
|
const onesBuffer = ToneAudioBuffer.fromArray(onesArray);
|
|
638
|
-
const player = new Player({
|
|
680
|
+
const player = new Player({
|
|
681
|
+
url: onesBuffer,
|
|
682
|
+
fadeOut: 0.1,
|
|
683
|
+
fadeIn: 0.1,
|
|
684
|
+
}).toDestination();
|
|
639
685
|
player.start(0);
|
|
640
686
|
}, 0.6).then((buff) => {
|
|
641
687
|
expect(buff.getRmsAtTime(0)).to.be.closeTo(0, 0.1);
|
|
@@ -643,7 +689,10 @@ describe("Player", () => {
|
|
|
643
689
|
expect(buff.getRmsAtTime(0.1)).to.be.closeTo(1, 0.1);
|
|
644
690
|
duration -= 0.1;
|
|
645
691
|
expect(buff.getRmsAtTime(duration)).to.be.closeTo(1, 0.1);
|
|
646
|
-
expect(buff.getRmsAtTime(duration + 0.05)).to.be.closeTo(
|
|
692
|
+
expect(buff.getRmsAtTime(duration + 0.05)).to.be.closeTo(
|
|
693
|
+
0.5,
|
|
694
|
+
0.1
|
|
695
|
+
);
|
|
647
696
|
expect(buff.getRmsAtTime(duration + 0.1)).to.be.closeTo(0, 0.1);
|
|
648
697
|
});
|
|
649
698
|
});
|
package/Tone/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version: string = "14.8.
|
|
1
|
+
export const version: string = "14.8.34";
|