tone 15.3.1 → 15.3.3

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/LICENSE.md CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2014-2020 Yotam Mann
3
+ Copyright (c) 2014-2025 Yotam Mann
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -330,14 +330,17 @@ describe("Transport", () => {
330
330
  expect(transport.getTicksAtTime(0)).to.be.equal(
331
331
  Math.floor(transport.PPQ * 0)
332
332
  );
333
- expect(transport.getTicksAtTime(0.05)).to.be.equal(
334
- Math.floor(transport.PPQ * 0.1)
333
+ expect(transport.getTicksAtTime(0.05)).to.be.closeTo(
334
+ Math.floor(transport.PPQ * 0.1),
335
+ 0.5
335
336
  );
336
- expect(transport.getTicksAtTime(0.1)).to.be.equal(
337
- Math.floor(transport.PPQ * 0.2)
337
+ expect(transport.getTicksAtTime(0.1)).to.be.closeTo(
338
+ Math.floor(transport.PPQ * 0.2),
339
+ 0.5
338
340
  );
339
- expect(transport.getTicksAtTime(0.15)).to.be.equal(
340
- Math.floor(transport.PPQ * 0.2)
341
+ expect(transport.getTicksAtTime(0.15)).to.be.closeTo(
342
+ Math.floor(transport.PPQ * 0.2),
343
+ 0.5
341
344
  );
342
345
  expect(transport.getTicksAtTime(0.2)).to.be.equal(0);
343
346
  }, 0.3);
@@ -59,7 +59,7 @@ describe("Destination", () => {
59
59
  );
60
60
  });
61
61
 
62
- it("can set the audio channel configuration", () => {
62
+ it.skip("can set the audio channel configuration", () => {
63
63
  return Offline(
64
64
  (context) => {
65
65
  expect(context.destination.channelCount).to.equal(4);
@@ -5,9 +5,8 @@ import { Plot } from "../../../test/helper/compare/index.js";
5
5
  import { atTime, Offline } from "../../../test/helper/Offline.js";
6
6
  import { Signal } from "../../signal/Signal.js";
7
7
  import { getContext } from "../Global.js";
8
- import {
9
- UnitName,
10
- } from "../type/Units.js";
8
+ import { UnitName } from "../type/Units.js";
9
+ import { Gain } from "./Gain.js";
11
10
  import { Param } from "./Param.js";
12
11
  import { connect } from "./ToneAudioNode.js";
13
12
 
@@ -120,7 +119,6 @@ describe("Param", () => {
120
119
  1,
121
120
  sampleRate
122
121
  );
123
- // document.body.appendChild(await Plot.signal(testBuffer));
124
122
  matchesOutputCurve(param, testBuffer);
125
123
  });
126
124
 
@@ -155,7 +153,6 @@ describe("Param", () => {
155
153
  1,
156
154
  sampleRate
157
155
  );
158
- // document.body.appendChild(await Plot.signal(testBuffer));
159
156
  matchesOutputCurve(param, testBuffer);
160
157
  });
161
158
 
@@ -199,30 +196,7 @@ describe("Param", () => {
199
196
  sampleRate
200
197
  );
201
198
  matchesOutputCurve(param, testBuffer);
202
- // document.body.appendChild(await Plot.signal(testBuffer));
203
199
  });
204
-
205
- // it ("matches known values", async () => {
206
- // await Compare.toFile(context => {
207
- // const source = context.createConstantSource();
208
- // source.connect(context.rawContext.destination);
209
- // source.start(0);
210
- // const param = new Param({
211
- // context,
212
- // param: source.offset,
213
- // value: 0.1,
214
- // });
215
- // param.setValueAtTime(0, 0);
216
- // param.setValueAtTime(1, 0.2);
217
- // param.cancelAndHoldAtTime(0.1);
218
- // param.linearRampToValueAtTime(1, 0.3);
219
- // param.cancelAndHoldAtTime(0.2);
220
- // param.exponentialRampToValueAtTime(0, 0.4);
221
- // param.cancelAndHoldAtTime(0.25);
222
- // param.setTargetAtTime(1, 0.3, 0.1);
223
- // param.cancelAndHoldAtTime(0.4);
224
- // }, "/base/test/audio/param/curve_0.wav", 0.01, 0.5, 1, 11025);
225
- // });
226
200
  });
227
201
 
228
202
  context("Units", () => {
@@ -453,7 +427,6 @@ describe("Param", () => {
453
427
  testUnitConversion("frequency", 0.1, 0.1, 0.1);
454
428
  testUnitConversion("normalRange", 0, 0, 0);
455
429
  testUnitConversion("normalRange", 0.5, 0.5, 0.5);
456
- testUnitConversion("normalRange", 1.5, 1, 1);
457
430
  testUnitConversion("audioRange", -1, -1, -1);
458
431
  testUnitConversion("audioRange", 0.5, 0.5, 0.5);
459
432
  testUnitConversion("audioRange", 1, 1, 1);
@@ -533,9 +506,9 @@ describe("Param", () => {
533
506
  context("setValueAtTime", () => {
534
507
  function testSetValueAtTime(
535
508
  units: UnitName,
536
- value0,
537
- value1,
538
- value2
509
+ value0: number,
510
+ value1: number,
511
+ value2: number
539
512
  ): void {
540
513
  it(`can schedule value with units ${units}`, async () => {
541
514
  const testBuffer = await Offline(
@@ -601,6 +574,20 @@ describe("Param", () => {
601
574
  testSetValueAtTime(unit, 0, 1, 0.5);
602
575
  }
603
576
  });
577
+
578
+ it("asserts a value range", async () => {
579
+ let errored = false;
580
+ try {
581
+ const source = new Gain();
582
+ const param = new Param({
583
+ param: source.gain,
584
+ units: "normalRange",
585
+ });
586
+ param.setValueAtTime(2, 0);
587
+ } catch (e) {
588
+ errored = true;
589
+ }
590
+ });
604
591
  });
605
592
 
606
593
  ["linearRampToValueAtTime", "exponentialRampToValueAtTime"].forEach(
@@ -608,9 +595,9 @@ describe("Param", () => {
608
595
  context(method, () => {
609
596
  function testRampToValueAtTime(
610
597
  units: UnitName,
611
- value0,
612
- value1,
613
- value2
598
+ value0: number,
599
+ value1: number,
600
+ value2: number
614
601
  ): void {
615
602
  it(`can schedule value with units ${units}`, async () => {
616
603
  const testBuffer = await Offline(
@@ -689,9 +676,9 @@ describe("Param", () => {
689
676
  context(method, () => {
690
677
  function testRampToValueAtTime(
691
678
  units: UnitName,
692
- value0,
693
- value1,
694
- value2
679
+ value0: number,
680
+ value1: number,
681
+ value2: number
695
682
  ): void {
696
683
  it(`can schedule value with units ${units}`, async () => {
697
684
  const testBuffer = await Offline(
@@ -161,11 +161,14 @@ describe("TimeClass", () => {
161
161
 
162
162
  it("converts time into ticks", () => {
163
163
  return Offline(({ transport }) => {
164
- expect(Time("2n").toTicks()).to.equal(2 * transport.PPQ);
164
+ expect(Time("2n").toTicks()).to.closeTo(2 * transport.PPQ, 0.1);
165
165
  // floating point checks
166
166
  const bpmOrig = transport.bpm.value;
167
167
  transport.bpm.value = 100;
168
- expect(Time("0:1:3").toTicks()).to.equal(1.75 * transport.PPQ);
168
+ expect(Time("0:1:3").toTicks()).to.closeTo(
169
+ 1.75 * transport.PPQ,
170
+ 0.1
171
+ );
169
172
  transport.bpm.value = bpmOrig;
170
173
  });
171
174
  });
@@ -61,13 +61,20 @@ describe("Sequence", () => {
61
61
  });
62
62
 
63
63
  it("loops by default with the loopEnd as the duration of the loop", async () => {
64
- await Offline(() => {
65
- const seq = new Sequence(noOp, [0, 1, 2, 3], "8n");
64
+ const values: number[] = [];
65
+ await Offline(({ transport }) => {
66
+ const seq = new Sequence(
67
+ (_, value) => {
68
+ values.push(value);
69
+ },
70
+ [0, 1, 2, 3],
71
+ "8n"
72
+ ).start(0);
66
73
  expect(seq.loop).to.be.true;
67
74
  expect(seq.length).to.equal(4);
68
- expect(seq.loopEnd).to.equal(4);
69
- seq.dispose();
70
- });
75
+ transport.start(0);
76
+ }, 2);
77
+ expect(values).to.deep.equal([0, 1, 2, 3, 0, 1, 2, 3]);
71
78
  });
72
79
  });
73
80
 
@@ -9,7 +9,7 @@ import { MetalSynth } from "./MetalSynth.js";
9
9
  describe("MetalSynth", () => {
10
10
  BasicTests(MetalSynth);
11
11
 
12
- InstrumentTest(MetalSynth, "C2");
12
+ InstrumentTest(MetalSynth, "C2", undefined, undefined, true);
13
13
  MonophonicTest(MetalSynth, "C4");
14
14
 
15
15
  it("matches a file", () => {
@@ -4,7 +4,7 @@ import { BasicTests } from "../../test/helper/Basic.js";
4
4
  import { ConstantOutput } from "../../test/helper/ConstantOutput.js";
5
5
  import { Offline } from "../../test/helper/Offline.js";
6
6
  import { Gain } from "../core/context/Gain.js";
7
- import { Signal } from "./Signal.js";
7
+ import { connectSignal, disconnectSignal, Signal } from "./Signal.js";
8
8
 
9
9
  describe("Signal", () => {
10
10
  BasicTests(Signal);
@@ -142,32 +142,8 @@ describe("Signal", () => {
142
142
  expect(buffer.getValueAtTime(1)).to.be.closeTo(0, 0.001);
143
143
  });
144
144
 
145
- it("can disconnect from all the connected notes", async () => {
146
- await ConstantOutput(async (context) => {
147
- const output0 = new Signal(1).toDestination();
148
- const output1 = new Signal(1).toDestination();
149
- const sig = new Signal(0).connect(output0);
150
- sig.connect(output1);
151
- sig.disconnect();
152
- sig.setValueAtTime(0, 0);
153
- sig.linearRampToValueAtTime(0.5, 0.5);
154
- sig.linearRampToValueAtTime(0, 1);
155
- }, 0);
156
- });
157
-
158
- it("can disconnect from a specific node", async () => {
159
- await ConstantOutput(async (context) => {
160
- const output = new Signal(1).toDestination();
161
- const sig = new Signal(0).connect(output);
162
- sig.disconnect(output);
163
- sig.setValueAtTime(0, 0);
164
- sig.linearRampToValueAtTime(0.5, 0.5);
165
- sig.linearRampToValueAtTime(0, 1);
166
- }, 0);
167
- });
168
-
169
145
  it("can schedule multiple automations from a connected signal through a multiple nodes", async () => {
170
- const buffer = await Offline(async () => {
146
+ const buffer = await Offline(() => {
171
147
  const output = new Signal(0).toDestination();
172
148
  const proxy = new Signal(0).connect(output);
173
149
  const gain = new Gain(1).connect(proxy);
@@ -427,6 +403,8 @@ describe("Signal", () => {
427
403
  }).to.throw(RangeError);
428
404
  const signal = new Signal(1, "normalRange");
429
405
  expect(signal.value).to.be.closeTo(1, 0.01);
406
+ expect(signal.minValue).to.be.equal(0);
407
+ expect(signal.maxValue).to.be.equal(1);
430
408
  signal.dispose();
431
409
  });
432
410
 
@@ -436,6 +414,8 @@ describe("Signal", () => {
436
414
  }).to.throw(RangeError);
437
415
  const signal = new Signal(-1, "audioRange");
438
416
  expect(signal.value).to.be.closeTo(-1, 0.01);
417
+ expect(signal.minValue).to.be.equal(-1);
418
+ expect(signal.maxValue).to.be.equal(1);
439
419
  signal.dispose();
440
420
  });
441
421
 
@@ -445,6 +425,7 @@ describe("Signal", () => {
445
425
  }).to.throw(RangeError);
446
426
  const signal = new Signal(100, "positive");
447
427
  expect(signal.value).to.be.closeTo(100, 0.01);
428
+ expect(signal.minValue).to.be.equal(0);
448
429
  signal.dispose();
449
430
  });
450
431
  });
@@ -510,4 +491,229 @@ describe("Signal", () => {
510
491
  }, 5);
511
492
  });
512
493
  });
494
+
495
+ context("connectSignal/disconnectSignal", () => {
496
+ it("can connect a Signal to an AudioNode", () => {
497
+ return ConstantOutput((context) => {
498
+ const sig = new Signal({
499
+ value: 3,
500
+ context,
501
+ });
502
+ const gain = new Gain({
503
+ gain: 0.5,
504
+ context,
505
+ }).toDestination();
506
+ connectSignal(sig, gain);
507
+ }, 1.5);
508
+ });
509
+
510
+ it("can connect a Signal to a Param", () => {
511
+ return ConstantOutput((context) => {
512
+ const sig = new Signal({
513
+ value: 3,
514
+ context,
515
+ });
516
+ const scalar = new Signal({
517
+ value: 2,
518
+ context,
519
+ });
520
+ const gain = new Gain({
521
+ gain: 0.5,
522
+ context,
523
+ }).toDestination();
524
+ connectSignal(sig, gain);
525
+ connectSignal(scalar, gain.gain);
526
+ // gain of 0.5 is overridden
527
+ expect(gain.gain.value).to.equal(0);
528
+ }, 6);
529
+ });
530
+
531
+ it("can connect a Signal to a Signal", () => {
532
+ return ConstantOutput((context) => {
533
+ const sig = new Signal({
534
+ value: 3,
535
+ context,
536
+ });
537
+ const output = new Signal({
538
+ value: 0.5,
539
+ context,
540
+ }).toDestination();
541
+
542
+ connectSignal(sig, output);
543
+ expect(output.overridden).to.be.true;
544
+ }, 3);
545
+ });
546
+
547
+ it("can disconnect a Signal from an AudioNode", () => {
548
+ return ConstantOutput((context) => {
549
+ const sig = new Signal({
550
+ value: 3,
551
+ context,
552
+ });
553
+ const gain = new Gain({
554
+ gain: 0.5,
555
+ context,
556
+ }).toDestination();
557
+ connectSignal(sig, gain);
558
+ disconnectSignal(sig, gain);
559
+ }, 0);
560
+ });
561
+
562
+ it("can disconnect a Signal from a Param", () => {
563
+ return ConstantOutput((context) => {
564
+ const sig = new Signal({
565
+ value: 3,
566
+ context,
567
+ });
568
+ const scalar = new Signal({
569
+ value: 2,
570
+ context,
571
+ });
572
+ const gain = new Gain({
573
+ gain: 0.5,
574
+ context,
575
+ }).toDestination();
576
+ connectSignal(sig, gain);
577
+ connectSignal(scalar, gain.gain);
578
+ // gain of 0.5 is overridden
579
+ expect(gain.gain.value).to.equal(0);
580
+
581
+ disconnectSignal(scalar, gain.gain);
582
+ // the original value is restored
583
+ expect(gain.gain.value).to.equal(0.5);
584
+ }, 1.5);
585
+ });
586
+
587
+ it("can disconnect a Signal from a Signal", () => {
588
+ return ConstantOutput((context) => {
589
+ const sig = new Signal({
590
+ value: 3,
591
+ context,
592
+ });
593
+ const output = new Signal({
594
+ value: 2,
595
+ context,
596
+ }).toDestination();
597
+
598
+ connectSignal(sig, output);
599
+ expect(output.overridden).to.be.true;
600
+ expect(output.value).to.equal(0);
601
+
602
+ disconnectSignal(sig, output);
603
+ expect(output.value).to.equal(2);
604
+ expect(output.overridden).to.be.false;
605
+ }, 2);
606
+ });
607
+
608
+ it("can disconnect from all the connected notes", async () => {
609
+ await ConstantOutput(async (context) => {
610
+ // initially destination is 2
611
+ const output0 = new Signal(1).toDestination();
612
+ const output1 = new Signal(1).toDestination();
613
+
614
+ // both should now equal 0
615
+ const sig = new Signal(0).connect(output0).connect(output1);
616
+
617
+ // disconnect from both
618
+ sig.disconnect();
619
+ }, 2);
620
+ });
621
+
622
+ it("can disconnect from a specific node", async () => {
623
+ await ConstantOutput(() => {
624
+ // initially destination is 1
625
+ const output = new Signal(1).toDestination();
626
+
627
+ // overwrites it with 0
628
+ const sig = new Signal(0).connect(output);
629
+
630
+ // disconnects the signal and goes back to 1
631
+ sig.disconnect(output);
632
+ }, 1);
633
+ });
634
+
635
+ it("disconnects every input when no input is passed in", async () => {
636
+ await ConstantOutput(() => {
637
+ // initially destination is 1
638
+ const output = new Signal(1).toDestination();
639
+
640
+ // overwrites it with 0
641
+ const sig = new Signal(0).connect(output, 0);
642
+
643
+ // disconnects the signal and goes back to 1
644
+ sig.disconnect(output);
645
+ }, 1);
646
+ });
647
+
648
+ it("disconnects every output when no output is passed in", async () => {
649
+ await ConstantOutput(() => {
650
+ // initially destination is 1
651
+ const output = new Signal(1).toDestination();
652
+
653
+ // overwrites it with 0
654
+ const sig = new Signal(0).connect(output, undefined, 0);
655
+
656
+ // disconnects the signal and goes back to 1
657
+ sig.disconnect(output);
658
+ }, 1);
659
+ });
660
+
661
+ it("disconnects everything if no destination is passed in", () => {
662
+ return ConstantOutput((context) => {
663
+ const sig = new Signal({
664
+ value: 3,
665
+ context,
666
+ });
667
+ const output = new Signal({
668
+ value: 2,
669
+ context,
670
+ }).toDestination();
671
+
672
+ // overridden with value of 3
673
+ connectSignal(sig, output);
674
+ expect(output.overridden).to.be.true;
675
+
676
+ // disconnect goes back to 2
677
+ sig.disconnect();
678
+ expect(output.overridden).to.be.false;
679
+ }, 2);
680
+ });
681
+
682
+ it("can connect multiple times with no affect", () => {
683
+ return ConstantOutput((context) => {
684
+ const sig = new Signal({
685
+ value: 3,
686
+ context,
687
+ });
688
+ const output = new Signal({
689
+ value: 2,
690
+ context,
691
+ }).toDestination();
692
+
693
+ connectSignal(sig, output);
694
+ expect(output.overridden).to.be.true;
695
+
696
+ // no affect when called again
697
+ connectSignal(sig, output);
698
+ }, 3);
699
+ });
700
+
701
+ it("disconnecting multiple times throws an error", () => {
702
+ return ConstantOutput((context) => {
703
+ const sig = new Signal({
704
+ value: 3,
705
+ context,
706
+ });
707
+ const output = new Signal({
708
+ value: 2,
709
+ context,
710
+ }).toDestination();
711
+
712
+ connectSignal(sig, output);
713
+
714
+ disconnectSignal(sig, output);
715
+ expect(() => disconnectSignal(sig, output)).to.throw(Error);
716
+ }, 2);
717
+ });
718
+ });
513
719
  });
@@ -1,6 +1,7 @@
1
1
  import { AbstractParam } from "../core/context/AbstractParam.js";
2
2
  import { Param } from "../core/context/Param.js";
3
3
  import {
4
+ disconnect,
4
5
  InputNode,
5
6
  OutputNode,
6
7
  ToneAudioNode,
@@ -10,6 +11,7 @@ import { connect } from "../core/context/ToneAudioNode.js";
10
11
  import { Time, UnitMap, UnitName } from "../core/type/Units.js";
11
12
  import { isAudioParam } from "../core/util/AdvancedTypeCheck.js";
12
13
  import { optionsFromArguments } from "../core/util/Defaults.js";
14
+ import { isUndef } from "../core/util/TypeCheck.js";
13
15
  import { ToneConstantSource } from "./ToneConstantSource.js";
14
16
 
15
17
  export interface SignalOptions<TypeName extends UnitName>
@@ -98,6 +100,16 @@ export class Signal<TypeName extends UnitName = "number">
98
100
  return this;
99
101
  }
100
102
 
103
+ disconnect(
104
+ destination?: InputNode,
105
+ outputNum?: number,
106
+ inputNum?: number
107
+ ): this {
108
+ // disconnect the signal
109
+ disconnectSignal(this, destination, outputNum, inputNum);
110
+ return this;
111
+ }
112
+
101
113
  dispose(): this {
102
114
  super.dispose();
103
115
  this._param.dispose();
@@ -233,6 +245,22 @@ export class Signal<TypeName extends UnitName = "number">
233
245
  }
234
246
  }
235
247
 
248
+ /**
249
+ * Keep track of connected signals so they can be disconnected and restored to their previous value
250
+ */
251
+ const connectedSignals = new WeakMap<
252
+ OutputNode,
253
+ Array<{
254
+ destination: Param | AudioParam | Signal;
255
+ outputNum: number;
256
+ inputNum: number;
257
+ /**
258
+ * The value before overriding
259
+ */
260
+ previousValue: number;
261
+ }>
262
+ >();
263
+
236
264
  /**
237
265
  * When connecting from a signal, it's necessary to zero out the node destination
238
266
  * node if that node is also a signal. If the destination is not 0, then the values
@@ -254,6 +282,7 @@ export function connectSignal(
254
282
  isAudioParam(destination) ||
255
283
  (destination instanceof Signal && destination.override)
256
284
  ) {
285
+ const previousValue = destination.value;
257
286
  // cancel changes
258
287
  destination.cancelScheduledValues(0);
259
288
  // reset the value
@@ -262,6 +291,75 @@ export function connectSignal(
262
291
  if (destination instanceof Signal) {
263
292
  destination.overridden = true;
264
293
  }
294
+ // store the connection
295
+ if (!connectedSignals.has(signal)) {
296
+ connectedSignals.set(signal, []);
297
+ }
298
+ connectedSignals.get(signal)?.push({
299
+ destination,
300
+ outputNum: outputNum || 0,
301
+ inputNum: inputNum || 0,
302
+ previousValue,
303
+ });
265
304
  }
266
305
  connect(signal, destination, outputNum, inputNum);
267
306
  }
307
+
308
+ /**
309
+ * Disconnect a signal connection and restore the value of the destination if
310
+ * it was a signal that was overridden by the connection.
311
+ * @param signal
312
+ * @param destination
313
+ * @param outputNum
314
+ * @param inputNum
315
+ */
316
+ export function disconnectSignal(
317
+ signal: OutputNode,
318
+ destination?: InputNode,
319
+ outputNum?: number,
320
+ inputNum?: number
321
+ ): void {
322
+ if (
323
+ destination instanceof Param ||
324
+ isAudioParam(destination) ||
325
+ (destination instanceof Signal && destination.override) ||
326
+ destination === undefined
327
+ ) {
328
+ if (connectedSignals.has(signal)) {
329
+ let connections = connectedSignals.get(signal)!;
330
+
331
+ if (destination) {
332
+ connections = connections.filter((conn) => {
333
+ return (
334
+ conn.destination === destination &&
335
+ (isUndef(outputNum) || conn.outputNum === outputNum) &&
336
+ (isUndef(inputNum) || conn.inputNum === inputNum)
337
+ );
338
+ });
339
+ }
340
+
341
+ if (!connections.length) {
342
+ throw new Error("Not connected to destination node");
343
+ }
344
+
345
+ // restore the value
346
+ connections.forEach((connection) => {
347
+ if (connection.destination instanceof Signal) {
348
+ connection.destination.overridden = false;
349
+ }
350
+ connection.destination.setValueAtTime(
351
+ connection.previousValue,
352
+ 0
353
+ );
354
+ });
355
+ // remove the connection from the stored array
356
+ connectedSignals.set(
357
+ signal,
358
+ connectedSignals
359
+ .get(signal)!
360
+ .filter((conn) => !connections.includes(conn))
361
+ );
362
+ }
363
+ }
364
+ disconnect(signal, destination, outputNum, inputNum);
365
+ }
package/Tone/version.ts CHANGED
@@ -1 +1 @@
1
- export const version: string = "15.3.1";
1
+ export const version: string = "15.3.3";