tone 14.8.36 → 14.8.39

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.
Files changed (62) hide show
  1. package/Tone/component/analysis/Meter.test.ts +25 -0
  2. package/Tone/component/analysis/Meter.ts +7 -5
  3. package/Tone/component/filter/FeedbackCombFilter.test.ts +16 -0
  4. package/Tone/core/clock/Ticker.ts +11 -5
  5. package/Tone/core/context/BaseContext.ts +1 -2
  6. package/Tone/core/context/Context.test.ts +2 -0
  7. package/Tone/core/context/Context.ts +35 -50
  8. package/Tone/core/context/DummyContext.test.ts +1 -1
  9. package/Tone/core/context/DummyContext.ts +1 -1
  10. package/Tone/core/context/ToneAudioBuffer.ts +52 -27
  11. package/Tone/core/worklet/ToneAudioWorklet.ts +1 -1
  12. package/Tone/effect/BitCrusher.test.ts +16 -0
  13. package/Tone/event/Pattern.test.ts +31 -5
  14. package/Tone/event/Pattern.ts +19 -6
  15. package/Tone/event/PatternGenerator.test.ts +18 -26
  16. package/Tone/event/PatternGenerator.ts +52 -59
  17. package/Tone/event/Sequence.ts +1 -1
  18. package/Tone/instrument/MetalSynth.ts +48 -21
  19. package/Tone/signal/ToneConstantSource.test.ts +1 -1
  20. package/Tone/source/OneShotSource.ts +1 -1
  21. package/Tone/source/buffer/Player.test.ts +25 -0
  22. package/Tone/source/buffer/Player.ts +1 -1
  23. package/Tone/source/oscillator/ToneOscillatorNode.test.ts +1 -1
  24. package/Tone/version.ts +1 -1
  25. package/build/Tone.js +1 -1
  26. package/build/Tone.js.map +1 -1
  27. package/build/esm/component/analysis/Meter.d.ts +1 -1
  28. package/build/esm/component/analysis/Meter.js +5 -7
  29. package/build/esm/component/analysis/Meter.js.map +1 -1
  30. package/build/esm/core/clock/Ticker.d.ts +5 -1
  31. package/build/esm/core/clock/Ticker.js +6 -4
  32. package/build/esm/core/clock/Ticker.js.map +1 -1
  33. package/build/esm/core/context/BaseContext.d.ts +1 -1
  34. package/build/esm/core/context/BaseContext.js.map +1 -1
  35. package/build/esm/core/context/Context.d.ts +14 -15
  36. package/build/esm/core/context/Context.js +32 -38
  37. package/build/esm/core/context/Context.js.map +1 -1
  38. package/build/esm/core/context/DummyContext.d.ts +1 -1
  39. package/build/esm/core/context/DummyContext.js +1 -1
  40. package/build/esm/core/context/DummyContext.js.map +1 -1
  41. package/build/esm/core/context/ToneAudioBuffer.js +25 -15
  42. package/build/esm/core/context/ToneAudioBuffer.js.map +1 -1
  43. package/build/esm/core/worklet/ToneAudioWorklet.js +1 -1
  44. package/build/esm/core/worklet/ToneAudioWorklet.js.map +1 -1
  45. package/build/esm/event/Pattern.d.ts +8 -0
  46. package/build/esm/event/Pattern.js +11 -4
  47. package/build/esm/event/Pattern.js.map +1 -1
  48. package/build/esm/event/PatternGenerator.d.ts +4 -4
  49. package/build/esm/event/PatternGenerator.js +52 -58
  50. package/build/esm/event/PatternGenerator.js.map +1 -1
  51. package/build/esm/event/Sequence.d.ts +1 -1
  52. package/build/esm/event/Sequence.js +1 -1
  53. package/build/esm/instrument/MetalSynth.d.ts +0 -1
  54. package/build/esm/instrument/MetalSynth.js +15 -13
  55. package/build/esm/instrument/MetalSynth.js.map +1 -1
  56. package/build/esm/source/OneShotSource.js +1 -1
  57. package/build/esm/source/OneShotSource.js.map +1 -1
  58. package/build/esm/source/buffer/Player.js +2 -1
  59. package/build/esm/source/buffer/Player.js.map +1 -1
  60. package/build/esm/version.js +1 -1
  61. package/docs/tone.json +1 -1
  62. package/package.json +2 -2
@@ -27,12 +27,17 @@ export class Pattern<ValueType> extends Loop<PatternOptions<ValueType>> {
27
27
  /**
28
28
  * The pattern generator function
29
29
  */
30
- private _pattern: Iterator<ValueType>;
30
+ private _pattern: Iterator<number>;
31
+
32
+ /**
33
+ * The current index
34
+ */
35
+ private _index?: number;
31
36
 
32
37
  /**
33
38
  * The current value
34
39
  */
35
- private _value?: ValueType;
40
+ private _value?: ValueType;
36
41
 
37
42
  /**
38
43
  * Hold the pattern type
@@ -67,7 +72,7 @@ export class Pattern<ValueType> extends Loop<PatternOptions<ValueType>> {
67
72
 
68
73
  this.callback = options.callback;
69
74
  this._values = options.values;
70
- this._pattern = PatternGenerator(options.values, options.pattern);
75
+ this._pattern = PatternGenerator(options.values.length, options.pattern);
71
76
  this._type = options.pattern;
72
77
  }
73
78
 
@@ -83,8 +88,9 @@ export class Pattern<ValueType> extends Loop<PatternOptions<ValueType>> {
83
88
  * Internal function called when the notes should be called
84
89
  */
85
90
  protected _tick(time: Seconds): void {
86
- const value = this._pattern.next() as IteratorResult<ValueType>;
87
- this._value = value.value;
91
+ const index = this._pattern.next() as IteratorResult<ValueType>;
92
+ this._index = index.value;
93
+ this._value = this._values[index.value];
88
94
  this.callback(time, this._value);
89
95
  }
90
96
 
@@ -107,6 +113,13 @@ export class Pattern<ValueType> extends Loop<PatternOptions<ValueType>> {
107
113
  return this._value;
108
114
  }
109
115
 
116
+ /**
117
+ * The current index of the pattern.
118
+ */
119
+ get index(): number | undefined {
120
+ return this._index;
121
+ }
122
+
110
123
  /**
111
124
  * The pattern type. See Tone.CtrlPattern for the full list of patterns.
112
125
  */
@@ -115,7 +128,7 @@ export class Pattern<ValueType> extends Loop<PatternOptions<ValueType>> {
115
128
  }
116
129
  set pattern(pattern) {
117
130
  this._type = pattern;
118
- this._pattern = PatternGenerator(this._values, this._type);
131
+ this._pattern = PatternGenerator(this._values.length, this._type);
119
132
  }
120
133
  }
121
134
 
@@ -13,22 +13,14 @@ describe("PatternGenerator", () => {
13
13
 
14
14
  context("API", () => {
15
15
 
16
- it("can be constructed with an array and type", () => {
17
- const pattern = PatternGenerator([0, 1, 2, 3], "down");
16
+ it("can be constructed with an number and type", () => {
17
+ const pattern = PatternGenerator(4, "down");
18
18
  expect(getArrayValues(pattern, 10)).to.deep.equal([3, 2, 1, 0, 3, 2, 1, 0, 3, 2]);
19
19
  });
20
20
 
21
- it("can be resized smaller when the index is after the previous length", () => {
22
- const values = [0, 1, 2, 3, 4];
23
- const pattern = PatternGenerator(values);
24
- expect(pattern.next().value).to.equal(0);
25
- values.shift();
26
- expect(pattern.next().value).to.equal(2);
27
- });
28
-
29
- it("throws an error with an empty array", () => {
21
+ it("throws an error with a number less than 1", () => {
30
22
  expect(() => {
31
- const pattern = PatternGenerator([]);
23
+ const pattern = PatternGenerator(0);
32
24
  pattern.next();
33
25
  }).to.throw(Error);
34
26
  });
@@ -37,54 +29,54 @@ describe("PatternGenerator", () => {
37
29
  context("Patterns", () => {
38
30
 
39
31
  it("does the up pattern", () => {
40
- const pattern = PatternGenerator([0, 1, 2, 3], "up");
32
+ const pattern = PatternGenerator(4, "up");
41
33
  expect(getArrayValues(pattern, 6)).to.deep.equal([0, 1, 2, 3, 0, 1]);
42
34
  });
43
35
 
44
36
  it("does the down pattern", () => {
45
- const pattern = PatternGenerator([0, 1, 2, 3], "down");
37
+ const pattern = PatternGenerator(4, "down");
46
38
  expect(getArrayValues(pattern, 6)).to.deep.equal([3, 2, 1, 0, 3, 2]);
47
39
  });
48
40
 
49
41
  it("does the upDown pattern", () => {
50
- const pattern = PatternGenerator([0, 1, 2, 3], "upDown");
42
+ const pattern = PatternGenerator(4, "upDown");
51
43
  expect(getArrayValues(pattern, 10)).to.deep.equal([0, 1, 2, 3, 2, 1, 0, 1, 2, 3]);
52
44
  });
53
45
 
54
46
  it("does the downUp pattern", () => {
55
- const pattern = PatternGenerator([0, 1, 2, 3], "downUp");
47
+ const pattern = PatternGenerator(4, "downUp");
56
48
  expect(getArrayValues(pattern, 10)).to.deep.equal([3, 2, 1, 0, 1, 2, 3, 2, 1, 0]);
57
49
  });
58
50
 
59
51
  it("does the alternateUp pattern", () => {
60
- const pattern = PatternGenerator([0, 1, 2, 3, 4], "alternateUp");
52
+ const pattern = PatternGenerator(5, "alternateUp");
61
53
  expect(getArrayValues(pattern, 10)).to.deep.equal([0, 2, 1, 3, 2, 4, 3, 0, 2, 1]);
62
54
  });
63
55
 
64
56
  it("does the alternateDown pattern", () => {
65
- const pattern = PatternGenerator([0, 1, 2, 3, 4], "alternateDown");
57
+ const pattern = PatternGenerator(5, "alternateDown");
66
58
  expect(getArrayValues(pattern, 10)).to.deep.equal([4, 2, 3, 1, 2, 0, 1, 4, 2, 3]);
67
59
  });
68
60
 
69
61
  it("outputs random elements from the values", () => {
70
- const values = [0, 1, 2, 3, 4];
71
- const pattern = PatternGenerator(values, "random");
62
+ const numValues = 5;
63
+ const pattern = PatternGenerator(numValues, "random");
72
64
  for (let i = 0; i < 10; i++) {
73
- expect(values.indexOf(pattern.next().value)).to.not.equal(-1);
65
+ expect(pattern.next().value).to.be.at.least(0).and.at.most(numValues - 1);
74
66
  }
75
67
  });
76
68
 
77
69
  it("does randomOnce pattern", () => {
78
- const pattern = PatternGenerator([4, 5, 6, 7, 8], "randomOnce");
79
- expect(getArrayValues(pattern, 10).sort()).to.deep.equal([4, 4, 5, 5, 6, 6, 7, 7, 8, 8]);
70
+ const pattern = PatternGenerator(5, "randomOnce");
71
+ expect(getArrayValues(pattern, 10).sort()).to.deep.equal([0, 0, 1, 1, 2, 2, 3, 3, 4, 4]);
80
72
  });
81
73
 
82
74
  it("randomly walks up or down 1 step without repeating", () => {
83
75
  const values = [0, 1, 2, 3, 4];
84
- const pattern = PatternGenerator(values, "randomWalk");
85
- let currentIndex = values.indexOf(pattern.next().value);
76
+ const pattern = PatternGenerator(5, "randomWalk");
77
+ let currentIndex = pattern.next().value;
86
78
  for (let i = 0; i < 10; i++) {
87
- const nextIndex = values.indexOf(pattern.next().value);
79
+ const nextIndex = pattern.next().value;
88
80
  expect(currentIndex).to.not.equal(nextIndex);
89
81
  // change always equals 1
90
82
  expect(Math.abs(currentIndex - nextIndex)).to.equal(1);
@@ -9,11 +9,11 @@ export type PatternName = "up" | "down" | "upDown" | "downUp" | "alternateUp" |
9
9
  /**
10
10
  * Start at the first value and go up to the last
11
11
  */
12
- function* upPatternGen<T>(values: T[]): IterableIterator<T> {
12
+ function* upPatternGen<T>(numValues: number): IterableIterator<number> {
13
13
  let index = 0;
14
- while (index < values.length) {
15
- index = clampToArraySize(index, values);
16
- yield values[index];
14
+ while (index < numValues) {
15
+ index = clamp(index, 0, numValues - 1);
16
+ yield index;
17
17
  index++;
18
18
  }
19
19
  }
@@ -21,11 +21,11 @@ function* upPatternGen<T>(values: T[]): IterableIterator<T> {
21
21
  /**
22
22
  * Start at the last value and go down to 0
23
23
  */
24
- function* downPatternGen<T>(values: T[]): IterableIterator<T> {
25
- let index = values.length - 1;
24
+ function* downPatternGen<T>(numValues: number): IterableIterator<number> {
25
+ let index = numValues - 1;
26
26
  while (index >= 0) {
27
- index = clampToArraySize(index, values);
28
- yield values[index];
27
+ index = clamp(index, 0, numValues - 1);
28
+ yield index;
29
29
  index--;
30
30
  }
31
31
  }
@@ -33,30 +33,23 @@ function* downPatternGen<T>(values: T[]): IterableIterator<T> {
33
33
  /**
34
34
  * Infinitely yield the generator
35
35
  */
36
- function* infiniteGen<T>(values: T[], gen: typeof upPatternGen): IterableIterator<T> {
36
+ function* infiniteGen<T>(numValues: number, gen: typeof upPatternGen): IterableIterator<number> {
37
37
  while (true) {
38
- yield* gen(values);
38
+ yield* gen(numValues);
39
39
  }
40
40
  }
41
41
 
42
- /**
43
- * Make sure that the index is in the given range
44
- */
45
- function clampToArraySize(index: number, values: any[]): number {
46
- return clamp(index, 0, values.length - 1);
47
- }
48
-
49
42
  /**
50
43
  * Alternate between two generators
51
44
  */
52
- function* alternatingGenerator<T>(values: T[], directionUp: boolean): IterableIterator<T> {
53
- let index = directionUp ? 0 : values.length - 1;
45
+ function* alternatingGenerator<T>(numValues: number, directionUp: boolean): IterableIterator<number> {
46
+ let index = directionUp ? 0 : numValues - 1;
54
47
  while (true) {
55
- index = clampToArraySize(index, values);
56
- yield values[index];
48
+ index = clamp(index, 0, numValues - 1);
49
+ yield index;
57
50
  if (directionUp) {
58
51
  index++;
59
- if (index >= values.length - 1) {
52
+ if (index >= numValues - 1) {
60
53
  directionUp = false;
61
54
  }
62
55
  } else {
@@ -71,12 +64,12 @@ function* alternatingGenerator<T>(values: T[], directionUp: boolean): IterableIt
71
64
  /**
72
65
  * Starting from the bottom move up 2, down 1
73
66
  */
74
- function* jumpUp<T>(values: T[]): IterableIterator<T> {
67
+ function* jumpUp<T>(numValues: number): IterableIterator<number> {
75
68
  let index = 0;
76
69
  let stepIndex = 0;
77
- while (index < values.length) {
78
- index = clampToArraySize(index, values);
79
- yield values[index];
70
+ while (index < numValues) {
71
+ index = clamp(index, 0, numValues - 1);
72
+ yield index;
80
73
  stepIndex++;
81
74
  index += (stepIndex % 2 ? 2 : -1);
82
75
  }
@@ -85,12 +78,12 @@ function* jumpUp<T>(values: T[]): IterableIterator<T> {
85
78
  /**
86
79
  * Starting from the top move down 2, up 1
87
80
  */
88
- function* jumpDown<T>(values: T[]): IterableIterator<T> {
89
- let index = values.length - 1;
81
+ function* jumpDown<T>(numValues: number): IterableIterator<number> {
82
+ let index = numValues - 1;
90
83
  let stepIndex = 0;
91
84
  while (index >= 0) {
92
- index = clampToArraySize(index, values);
93
- yield values[index];
85
+ index = clamp(index, 0, numValues - 1);
86
+ yield index;
94
87
  stepIndex++;
95
88
  index += (stepIndex % 2 ? -2 : 1);
96
89
  }
@@ -99,78 +92,78 @@ function* jumpDown<T>(values: T[]): IterableIterator<T> {
99
92
  /**
100
93
  * Choose a random index each time
101
94
  */
102
- function* randomGen<T>(values: T[]): IterableIterator<T> {
95
+ function* randomGen<T>(numValues: number): IterableIterator<number> {
103
96
  while (true) {
104
- const randomIndex = Math.floor(Math.random() * values.length);
105
- yield values[randomIndex];
97
+ const randomIndex = Math.floor(Math.random() * numValues);
98
+ yield randomIndex;
106
99
  }
107
100
  }
108
101
 
109
102
  /**
110
103
  * Randomly go through all of the values once before choosing a new random order
111
104
  */
112
- function* randomOnce<T>(values: T[]): IterableIterator<T> {
105
+ function* randomOnce<T>(numValues: number): IterableIterator<number> {
113
106
  // create an array of indices
114
107
  const copy: number[] = [];
115
- for (let i = 0; i < values.length; i++) {
108
+ for (let i = 0; i < numValues; i++) {
116
109
  copy.push(i);
117
110
  }
118
111
  while (copy.length > 0) {
119
112
  // random choose an index, and then remove it so it's not chosen again
120
113
  const randVal = copy.splice(Math.floor(copy.length * Math.random()), 1);
121
- const index = clampToArraySize(randVal[0], values);
122
- yield values[index];
114
+ const index = clamp(randVal[0], 0, numValues - 1);
115
+ yield index;
123
116
  }
124
117
  }
125
118
 
126
119
  /**
127
- * Randomly choose to walk up or down 1 index in the values array
120
+ * Randomly choose to walk up or down 1 index
128
121
  */
129
- function* randomWalk<T>(values: T[]): IterableIterator<T> {
130
- // randomly choose a starting index in the values array
131
- let index = Math.floor(Math.random() * values.length);
122
+ function* randomWalk<T>(numValues: number): IterableIterator<number> {
123
+ // randomly choose a starting index
124
+ let index = Math.floor(Math.random() * numValues);
132
125
  while (true) {
133
126
  if (index === 0) {
134
- index++; // at bottom of array, so force upward step
135
- } else if (index === values.length - 1) {
136
- index--; // at top of array, so force downward step
127
+ index++; // at bottom, so force upward step
128
+ } else if (index === numValues - 1) {
129
+ index--; // at top, so force downward step
137
130
  } else if (Math.random() < 0.5) { // else choose random downward or upward step
138
131
  index--;
139
132
  } else {
140
133
  index++;
141
134
  }
142
- yield values[index];
135
+ yield index;
143
136
  }
144
137
  }
145
138
 
146
139
  /**
147
- * PatternGenerator returns a generator which will iterate over the given array
148
- * of values and yield the items according to the passed in pattern
149
- * @param values An array of values to iterate over
140
+ * PatternGenerator returns a generator which will yield numbers between 0 and numValues
141
+ * according to the passed in pattern that can be used as indexes into an array of size numValues.
142
+ * @param numValues The size of the array to emit indexes for
150
143
  * @param pattern The name of the pattern use when iterating over
151
144
  * @param index Where to start in the offset of the values array
152
145
  */
153
- export function* PatternGenerator<T>(values: T[], pattern: PatternName = "up", index = 0): Iterator<T> {
146
+ export function* PatternGenerator(numValues: number, pattern: PatternName = "up", index = 0): Iterator<number> {
154
147
  // safeguards
155
- assert(values.length > 0, "The array must have more than one value in it");
148
+ assert(numValues >= 1, "The number of values must be at least one");
156
149
  switch (pattern) {
157
150
  case "up" :
158
- yield* infiniteGen(values, upPatternGen);
151
+ yield* infiniteGen(numValues, upPatternGen);
159
152
  case "down" :
160
- yield* infiniteGen(values, downPatternGen);
153
+ yield* infiniteGen(numValues, downPatternGen);
161
154
  case "upDown" :
162
- yield* alternatingGenerator(values, true);
155
+ yield* alternatingGenerator(numValues, true);
163
156
  case "downUp" :
164
- yield* alternatingGenerator(values, false);
157
+ yield* alternatingGenerator(numValues, false);
165
158
  case "alternateUp":
166
- yield* infiniteGen(values, jumpUp);
159
+ yield* infiniteGen(numValues, jumpUp);
167
160
  case "alternateDown":
168
- yield* infiniteGen(values, jumpDown);
161
+ yield* infiniteGen(numValues, jumpDown);
169
162
  case "random":
170
- yield* randomGen(values);
163
+ yield* randomGen(numValues);
171
164
  case "randomOnce":
172
- yield* infiniteGen(values, randomOnce);
165
+ yield* infiniteGen(numValues, randomOnce);
173
166
  case "randomWalk":
174
- yield* randomWalk(values);
167
+ yield* randomWalk(numValues);
175
168
  }
176
169
  }
@@ -20,7 +20,7 @@ interface SequenceOptions<T> extends Omit<ToneEventOptions<T>, "value"> {
20
20
  * in an array of events which will be spaced at the
21
21
  * given subdivision. Sub-arrays will subdivide that beat
22
22
  * by the number of items are in the array.
23
- * Sequence notation inspiration from [Tidal](http://yaxu.org/tidal/)
23
+ * Sequence notation inspiration from [Tidal Cycles](http://tidalcycles.org/)
24
24
  * @example
25
25
  * const synth = new Tone.Synth().toDestination();
26
26
  * const seq = new Tone.Sequence((time, note) => {
@@ -1,9 +1,22 @@
1
1
  import { Envelope, EnvelopeOptions } from "../component/envelope/Envelope";
2
2
  import { Filter } from "../component/filter/Filter";
3
3
  import { Gain } from "../core/context/Gain";
4
- import { ToneAudioNode, ToneAudioNodeOptions } from "../core/context/ToneAudioNode";
5
- import { Frequency, NormalRange, Positive, Seconds, Time } from "../core/type/Units";
6
- import { deepMerge, omitFromObject, optionsFromArguments } from "../core/util/Defaults";
4
+ import {
5
+ ToneAudioNode,
6
+ ToneAudioNodeOptions,
7
+ } from "../core/context/ToneAudioNode";
8
+ import {
9
+ Frequency,
10
+ NormalRange,
11
+ Positive,
12
+ Seconds,
13
+ Time,
14
+ } from "../core/type/Units";
15
+ import {
16
+ deepMerge,
17
+ omitFromObject,
18
+ optionsFromArguments,
19
+ } from "../core/util/Defaults";
7
20
  import { noOp, RecursivePartial } from "../core/util/Interface";
8
21
  import { Multiply } from "../signal/Multiply";
9
22
  import { Scale } from "../signal/Scale";
@@ -23,17 +36,15 @@ export interface MetalSynthOptions extends MonophonicOptions {
23
36
  * Inharmonic ratio of frequencies based on the Roland TR-808
24
37
  * Taken from https://ccrma.stanford.edu/papers/tr-808-cymbal-physically-informed-circuit-bendable-digital-model
25
38
  */
26
- const inharmRatios: number[] = [1.0, 1.483, 1.932, 2.546, 2.630, 3.897];
39
+ const inharmRatios: number[] = [1.0, 1.483, 1.932, 2.546, 2.63, 3.897];
27
40
 
28
41
  /**
29
42
  * A highly inharmonic and spectrally complex source with a highpass filter
30
43
  * and amplitude envelope which is good for making metallophone sounds.
31
44
  * Based on CymbalSynth by [@polyrhythmatic](https://github.com/polyrhythmatic).
32
- * Inspiration from [Sound on Sound](https://shorturl.at/rSZ12).
33
45
  * @category Instrument
34
46
  */
35
47
  export class MetalSynth extends Monophonic<MetalSynthOptions> {
36
-
37
48
  readonly name: string = "MetalSynth";
38
49
 
39
50
  /**
@@ -84,10 +95,13 @@ export class MetalSynth extends Monophonic<MetalSynthOptions> {
84
95
  */
85
96
  readonly envelope: Envelope;
86
97
 
87
- constructor(options?: RecursivePartial<MetalSynthOptions>)
98
+ constructor(options?: RecursivePartial<MetalSynthOptions>);
88
99
  constructor() {
89
100
  super(optionsFromArguments(MetalSynth.getDefaults(), arguments));
90
- const options = optionsFromArguments(MetalSynth.getDefaults(), arguments);
101
+ const options = optionsFromArguments(
102
+ MetalSynth.getDefaults(),
103
+ arguments
104
+ );
91
105
 
92
106
  this.detune = new Signal({
93
107
  context: this.context,
@@ -158,12 +172,15 @@ export class MetalSynth extends Monophonic<MetalSynthOptions> {
158
172
  static getDefaults(): MetalSynthOptions {
159
173
  return deepMerge(Monophonic.getDefaults(), {
160
174
  envelope: Object.assign(
161
- omitFromObject(Envelope.getDefaults(), Object.keys(ToneAudioNode.getDefaults())),
175
+ omitFromObject(
176
+ Envelope.getDefaults(),
177
+ Object.keys(ToneAudioNode.getDefaults())
178
+ ),
162
179
  {
163
180
  attack: 0.001,
164
181
  decay: 1.4,
165
182
  release: 0.2,
166
- },
183
+ }
167
184
  ),
168
185
  harmonicity: 5.1,
169
186
  modulationIndex: 32,
@@ -177,24 +194,33 @@ export class MetalSynth extends Monophonic<MetalSynthOptions> {
177
194
  * @param time When the attack should be triggered.
178
195
  * @param velocity The velocity that the envelope should be triggered at.
179
196
  */
180
- protected _triggerEnvelopeAttack(time: Seconds, velocity: NormalRange = 1): this {
197
+ protected _triggerEnvelopeAttack(
198
+ time: Seconds,
199
+ velocity: NormalRange = 1
200
+ ): this {
181
201
  this.envelope.triggerAttack(time, velocity);
182
- this._oscillators.forEach(osc => osc.start(time));
202
+ this._oscillators.forEach((osc) => osc.start(time));
183
203
  if (this.envelope.sustain === 0) {
184
- this._oscillators.forEach(osc => {
185
- osc.stop(time + this.toSeconds(this.envelope.attack) + this.toSeconds(this.envelope.decay));
204
+ this._oscillators.forEach((osc) => {
205
+ osc.stop(
206
+ time +
207
+ this.toSeconds(this.envelope.attack) +
208
+ this.toSeconds(this.envelope.decay)
209
+ );
186
210
  });
187
211
  }
188
212
  return this;
189
213
  }
190
-
214
+
191
215
  /**
192
216
  * Trigger the release of the envelope.
193
217
  * @param time When the release should be triggered.
194
218
  */
195
219
  protected _triggerEnvelopeRelease(time: Seconds): this {
196
220
  this.envelope.triggerRelease(time);
197
- this._oscillators.forEach(osc => osc.stop(time + this.toSeconds(this.envelope.release)));
221
+ this._oscillators.forEach((osc) =>
222
+ osc.stop(time + this.toSeconds(this.envelope.release))
223
+ );
198
224
  return this;
199
225
  }
200
226
 
@@ -213,7 +239,7 @@ export class MetalSynth extends Monophonic<MetalSynthOptions> {
213
239
  return this._oscillators[0].modulationIndex.value;
214
240
  }
215
241
  set modulationIndex(val) {
216
- this._oscillators.forEach(osc => (osc.modulationIndex.value = val));
242
+ this._oscillators.forEach((osc) => (osc.modulationIndex.value = val));
217
243
  }
218
244
 
219
245
  /**
@@ -226,7 +252,7 @@ export class MetalSynth extends Monophonic<MetalSynthOptions> {
226
252
  return this._oscillators[0].harmonicity.value;
227
253
  }
228
254
  set harmonicity(val) {
229
- this._oscillators.forEach(osc => (osc.harmonicity.value = val));
255
+ this._oscillators.forEach((osc) => (osc.harmonicity.value = val));
230
256
  }
231
257
 
232
258
  /**
@@ -254,13 +280,14 @@ export class MetalSynth extends Monophonic<MetalSynthOptions> {
254
280
  }
255
281
  set octaves(val) {
256
282
  this._octaves = val;
257
- this._filterFreqScaler.max = this._filterFreqScaler.min * Math.pow(2, val);
283
+ this._filterFreqScaler.max =
284
+ this._filterFreqScaler.min * Math.pow(2, val);
258
285
  }
259
286
 
260
287
  dispose(): this {
261
288
  super.dispose();
262
- this._oscillators.forEach(osc => osc.dispose());
263
- this._freqMultipliers.forEach(freqMult => freqMult.dispose());
289
+ this._oscillators.forEach((osc) => osc.dispose());
290
+ this._freqMultipliers.forEach((freqMult) => freqMult.dispose());
264
291
  this.frequency.dispose();
265
292
  this.detune.dispose();
266
293
  this._filterFreqScaler.dispose();
@@ -152,7 +152,7 @@ describe("ToneConstantSource", () => {
152
152
  expect(source.getStateAtTime(0)).to.equal("stopped");
153
153
  expect(source.getStateAtTime(currentTime)).to.equal("started");
154
154
  setTimeout(() => {
155
- currentTime = source.context.currentTime;
155
+ currentTime = source.now();
156
156
  source.stop(0);
157
157
  expect(source.getStateAtTime(currentTime + 0.01)).to.equal("stopped");
158
158
  source.dispose();
@@ -168,7 +168,7 @@ export abstract class OneShotSource<
168
168
 
169
169
  // schedule the stop callback
170
170
  this._stopTime = this.toSeconds(time) + fadeOutTime;
171
- this._stopTime = Math.max(this._stopTime, this.context.currentTime);
171
+ this._stopTime = Math.max(this._stopTime, this.now());
172
172
  if (fadeOutTime > 0) {
173
173
  // start the fade out curve at the given time
174
174
  if (this._curve === "linear") {
@@ -696,5 +696,30 @@ describe("Player", () => {
696
696
  expect(buff.getRmsAtTime(duration + 0.1)).to.be.closeTo(0, 0.1);
697
697
  });
698
698
  });
699
+
700
+ it("stops only last activeSource when restarting at intervals < latencyHint", (done) => {
701
+ const originalLookAhead = getContext().lookAhead;
702
+ getContext().lookAhead = .3;
703
+ const player = new Player({
704
+ onload(): void {
705
+ player.start(undefined, undefined, 1);
706
+ setTimeout(() => player.restart(undefined, undefined, 1), 50);
707
+ setTimeout(() => player.restart(undefined, undefined, 1), 100);
708
+ setTimeout(() => player.restart(undefined, undefined, 1), 150);
709
+ setTimeout(() => {
710
+ player.restart(undefined, undefined, 1);
711
+ const checkStopTimes = new Set();
712
+ player["_activeSources"].forEach(source => {
713
+ checkStopTimes.add(source["_stopTime"]);
714
+ });
715
+ getContext().lookAhead = originalLookAhead;
716
+ // ensure each source has a different stopTime
717
+ expect(checkStopTimes.size).to.equal(player["_activeSources"].size);
718
+ done();
719
+ }, 250);
720
+ },
721
+ url: "./audio/sine.wav",
722
+ });
723
+ });
699
724
  });
700
725
  });
@@ -284,7 +284,7 @@ export class Player extends Source<PlayerOptions> {
284
284
  }
285
285
 
286
286
  protected _restart(time?: Seconds, offset?: Time, duration?: Time): void {
287
- this._stop(time);
287
+ [...this._activeSources].pop()?.stop(time); // explicitly stop only the most recently created source, to avoid edge case when > 1 source exists and _stop() erroneously sets all stop times past original end offset
288
288
  this._start(time, offset, duration);
289
289
  }
290
290
 
@@ -191,7 +191,7 @@ describe("ToneOscillatorNode", () => {
191
191
  expect(osc.getStateAtTime(0)).to.equal("stopped");
192
192
  expect(osc.getStateAtTime(currentTime)).to.equal("started");
193
193
  setTimeout(() => {
194
- currentTime = osc.context.currentTime;
194
+ currentTime = osc.now();
195
195
  osc.stop(0);
196
196
  expect(osc.getStateAtTime(currentTime + 0.01)).to.equal("stopped");
197
197
  osc.dispose();
package/Tone/version.ts CHANGED
@@ -1 +1 @@
1
- export const version: string = "14.8.36";
1
+ export const version: string = "14.8.39";