tone 14.8.32 → 14.8.36

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.
@@ -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
- const player = new Player(buffer).toDestination();
25
- player.start(0.1).stop(0.2);
26
- player.playbackRate = 2;
27
- }, "player.wav", 0.005);
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 = (shorterBuffer.get() as AudioBuffer).getChannelData(0);
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 = buffer.toArray(0)[Math.floor(0.1 * getContext().sampleRate)];
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 < (playDur - 0.01)) {
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(Math.floor(getContext().sampleRate * 0.3));
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 / (ramp.length)) * 0.3;
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 = buffer.toArray(0)[Math.floor(0.1 * getContext().sampleRate)];
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(Math.floor(getContext().sampleRate * 0.3));
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] = (i / (ramp.length - 1));
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(Math.floor(getContext().sampleRate * 0.3));
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 / (ramp.length)) * 0.3;
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((sample) => {
479
- expect(sample).to.equal(0);
480
- }, 0.11, 0.15);
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.start(0, 0, 0.05).start(0.1, 0, 0.05).start(0.2, 0, 0.05);
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).sync().start(0).toDestination();
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 = buffer.toArray(0)[Math.floor(0.13125 * getContext().sampleRate)];
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).sync().start(0, 0.1).toDestination();
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(Math.floor(getContext().sampleRate * 0.3));
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] = (i / (ramp.length));
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(Math.floor(getContext().sampleRate * 0.3));
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 / (ramp.length)) * 0.3;
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(Math.floor(getContext().sampleRate * 0.3));
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 / (ramp.length)) * 0.3;
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(getContext().sampleRate * duration);
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({ url: onesBuffer, fadeOut: 0.1, fadeIn: 0.1 }).toDestination();
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(0.5, 0.1);
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
  });
@@ -31,7 +31,6 @@ export interface PlayerOptions extends SourceOptions {
31
31
  * @category Source
32
32
  */
33
33
  export class Player extends Source<PlayerOptions> {
34
-
35
34
  readonly name: string = "Player";
36
35
 
37
36
  /**
@@ -86,12 +85,22 @@ export class Player extends Source<PlayerOptions> {
86
85
  * @param url Either the AudioBuffer or the url from which to load the AudioBuffer
87
86
  * @param onload The function to invoke when the buffer is loaded.
88
87
  */
89
- constructor(url?: string | AudioBuffer | ToneAudioBuffer, onload?: () => void);
88
+ constructor(
89
+ url?: string | AudioBuffer | ToneAudioBuffer,
90
+ onload?: () => void
91
+ );
90
92
  constructor(options?: Partial<PlayerOptions>);
91
93
  constructor() {
92
-
93
- super(optionsFromArguments(Player.getDefaults(), arguments, ["url", "onload"]));
94
- const options = optionsFromArguments(Player.getDefaults(), arguments, ["url", "onload"]);
94
+ super(
95
+ optionsFromArguments(Player.getDefaults(), arguments, [
96
+ "url",
97
+ "onload",
98
+ ])
99
+ );
100
+ const options = optionsFromArguments(Player.getDefaults(), arguments, [
101
+ "url",
102
+ "onload",
103
+ ]);
95
104
 
96
105
  this._buffer = new ToneAudioBuffer({
97
106
  onload: this._onload.bind(this, options.onload),
@@ -157,8 +166,11 @@ export class Player extends Source<PlayerOptions> {
157
166
 
158
167
  // delete the source from the active sources
159
168
  this._activeSources.delete(source);
160
- if (this._activeSources.size === 0 && !this._synced &&
161
- this._state.getValueAtTime(this.now()) === "started") {
169
+ if (
170
+ this._activeSources.size === 0 &&
171
+ !this._synced &&
172
+ this._state.getValueAtTime(this.now()) === "started"
173
+ ) {
162
174
  // remove the 'implicitEnd' event and replace with an explicit end
163
175
  this._state.cancel(this.now());
164
176
  this._state.setStateAtTime("stopped", this.now());
@@ -196,7 +208,10 @@ export class Player extends Source<PlayerOptions> {
196
208
 
197
209
  // compute the duration which is either the passed in duration of the buffer.duration - offset
198
210
  const origDuration = duration;
199
- duration = defaultArg(duration, Math.max(this._buffer.duration - computedOffset, 0));
211
+ duration = defaultArg(
212
+ duration,
213
+ Math.max(this._buffer.duration - computedOffset, 0)
214
+ );
200
215
  let computedDuration = this.toSeconds(duration);
201
216
 
202
217
  // scale it by the playback rate
@@ -223,9 +238,13 @@ export class Player extends Source<PlayerOptions> {
223
238
  // cancel the previous stop
224
239
  this._state.cancel(startTime + computedDuration);
225
240
  // if it's not looping, set the state change at the end of the sample
226
- this._state.setStateAtTime("stopped", startTime + computedDuration, {
227
- implicitEnd: true,
228
- });
241
+ this._state.setStateAtTime(
242
+ "stopped",
243
+ startTime + computedDuration,
244
+ {
245
+ implicitEnd: true,
246
+ }
247
+ );
229
248
  }
230
249
 
231
250
  // add it to the array of active sources
@@ -236,7 +255,11 @@ export class Player extends Source<PlayerOptions> {
236
255
  source.start(startTime, computedOffset);
237
256
  } else {
238
257
  // subtract the fade out time
239
- source.start(startTime, computedOffset, computedDuration - this.toSeconds(this.fadeOut));
258
+ source.start(
259
+ startTime,
260
+ computedOffset,
261
+ computedDuration - this.toSeconds(this.fadeOut)
262
+ );
240
263
  }
241
264
  }
242
265
 
@@ -245,14 +268,14 @@ export class Player extends Source<PlayerOptions> {
245
268
  */
246
269
  protected _stop(time?: Time): void {
247
270
  const computedTime = this.toSeconds(time);
248
- this._activeSources.forEach(source => source.stop(computedTime));
271
+ this._activeSources.forEach((source) => source.stop(computedTime));
249
272
  }
250
273
 
251
274
  /**
252
275
  * Stop and then restart the player from the beginning (or offset)
253
276
  * @param time When the player should start.
254
277
  * @param offset The offset from the beginning of the sample to start at.
255
- * @param duration How long the sample should play. If no duration is given,
278
+ * @param duration How long the sample should play. If no duration is given,
256
279
  * it will default to the full length of the sample (minus any offset)
257
280
  */
258
281
  restart(time?: Seconds, offset?: Time, duration?: Time): this {
@@ -318,7 +341,7 @@ export class Player extends Source<PlayerOptions> {
318
341
  assertRange(this.toSeconds(loopStart), 0, this.buffer.duration);
319
342
  }
320
343
  // get the current source
321
- this._activeSources.forEach(source => {
344
+ this._activeSources.forEach((source) => {
322
345
  source.loopStart = loopStart;
323
346
  });
324
347
  }
@@ -335,7 +358,7 @@ export class Player extends Source<PlayerOptions> {
335
358
  assertRange(this.toSeconds(loopEnd), 0, this.buffer.duration);
336
359
  }
337
360
  // get the current source
338
- this._activeSources.forEach(source => {
361
+ this._activeSources.forEach((source) => {
339
362
  source.loopEnd = loopEnd;
340
363
  });
341
364
  }
@@ -367,7 +390,7 @@ export class Player extends Source<PlayerOptions> {
367
390
  }
368
391
  this._loop = loop;
369
392
  // set the loop of all of the sources
370
- this._activeSources.forEach(source => {
393
+ this._activeSources.forEach((source) => {
371
394
  source.loop = loop;
372
395
  });
373
396
  if (loop) {
@@ -399,17 +422,18 @@ export class Player extends Source<PlayerOptions> {
399
422
  const stopEvent = this._state.getNextState("stopped", now);
400
423
  if (stopEvent && stopEvent.implicitEnd) {
401
424
  this._state.cancel(stopEvent.time);
402
- this._activeSources.forEach(source => source.cancelStop());
425
+ this._activeSources.forEach((source) => source.cancelStop());
403
426
  }
404
427
 
405
428
  // set all the sources
406
- this._activeSources.forEach(source => {
429
+ this._activeSources.forEach((source) => {
407
430
  source.playbackRate.setValueAtTime(rate, now);
408
431
  });
409
432
  }
410
433
 
411
434
  /**
412
- * If the buffer should be reversed
435
+ * If the buffer should be reversed. Note that this sets the underlying [[ToneAudioBuffer.reverse]], so
436
+ * if multiple players are pointing at the same ToneAudioBuffer, they will all be reversed.
413
437
  * @example
414
438
  * const player = new Tone.Player("https://tonejs.github.io/audio/berklee/chime_1.mp3").toDestination();
415
439
  * player.autostart = true;
@@ -432,7 +456,7 @@ export class Player extends Source<PlayerOptions> {
432
456
  dispose(): this {
433
457
  super.dispose();
434
458
  // disconnect all of the players
435
- this._activeSources.forEach(source => source.dispose());
459
+ this._activeSources.forEach((source) => source.dispose());
436
460
  this._activeSources.clear();
437
461
  this._buffer.dispose();
438
462
  return this;
package/Tone/version.ts CHANGED
@@ -1 +1 @@
1
- export const version: string = "14.8.32";
1
+ export const version: string = "14.8.36";