tone 14.8.35 → 14.8.38

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 (51) hide show
  1. package/Tone/component/analysis/Meter.test.ts +25 -0
  2. package/Tone/component/analysis/Meter.ts +7 -5
  3. package/Tone/core/clock/TickSource.test.ts +52 -0
  4. package/Tone/core/clock/TickSource.ts +70 -9
  5. package/Tone/core/clock/Ticker.ts +11 -5
  6. package/Tone/core/context/Context.test.ts +2 -0
  7. package/Tone/core/context/Context.ts +29 -38
  8. package/Tone/core/context/ToneAudioBuffer.ts +52 -27
  9. package/Tone/event/Pattern.test.ts +31 -5
  10. package/Tone/event/Pattern.ts +19 -6
  11. package/Tone/event/PatternGenerator.test.ts +18 -26
  12. package/Tone/event/PatternGenerator.ts +52 -59
  13. package/Tone/instrument/MetalSynth.ts +48 -21
  14. package/Tone/signal/ToneConstantSource.test.ts +1 -1
  15. package/Tone/source/OneShotSource.ts +1 -1
  16. package/Tone/source/buffer/Player.test.ts +25 -0
  17. package/Tone/source/buffer/Player.ts +1 -1
  18. package/Tone/source/oscillator/ToneOscillatorNode.test.ts +1 -1
  19. package/Tone/version.ts +1 -1
  20. package/build/Tone.js +1 -1
  21. package/build/Tone.js.map +1 -1
  22. package/build/esm/component/analysis/Meter.d.ts +1 -1
  23. package/build/esm/component/analysis/Meter.js +5 -7
  24. package/build/esm/component/analysis/Meter.js.map +1 -1
  25. package/build/esm/core/clock/TickSource.d.ts +8 -0
  26. package/build/esm/core/clock/TickSource.js +49 -7
  27. package/build/esm/core/clock/TickSource.js.map +1 -1
  28. package/build/esm/core/clock/Ticker.d.ts +5 -1
  29. package/build/esm/core/clock/Ticker.js +6 -4
  30. package/build/esm/core/clock/Ticker.js.map +1 -1
  31. package/build/esm/core/context/Context.d.ts +12 -12
  32. package/build/esm/core/context/Context.js +26 -29
  33. package/build/esm/core/context/Context.js.map +1 -1
  34. package/build/esm/core/context/ToneAudioBuffer.js +25 -15
  35. package/build/esm/core/context/ToneAudioBuffer.js.map +1 -1
  36. package/build/esm/event/Pattern.d.ts +8 -0
  37. package/build/esm/event/Pattern.js +11 -4
  38. package/build/esm/event/Pattern.js.map +1 -1
  39. package/build/esm/event/PatternGenerator.d.ts +4 -4
  40. package/build/esm/event/PatternGenerator.js +52 -58
  41. package/build/esm/event/PatternGenerator.js.map +1 -1
  42. package/build/esm/instrument/MetalSynth.d.ts +0 -1
  43. package/build/esm/instrument/MetalSynth.js +15 -13
  44. package/build/esm/instrument/MetalSynth.js.map +1 -1
  45. package/build/esm/source/OneShotSource.js +1 -1
  46. package/build/esm/source/OneShotSource.js.map +1 -1
  47. package/build/esm/source/buffer/Player.js +2 -1
  48. package/build/esm/source/buffer/Player.js.map +1 -1
  49. package/build/esm/version.js +1 -1
  50. package/docs/tone.json +1 -1
  51. package/package.json +2 -2
@@ -1,7 +1,6 @@
1
1
  import { getContext } from "../Global";
2
2
  import { Tone } from "../Tone";
3
3
  import { Samples, Seconds } from "../type/Units";
4
- import { isAudioBuffer } from "../util/AdvancedTypeCheck";
5
4
  import { optionsFromArguments } from "../util/Defaults";
6
5
  import { noOp } from "../util/Interface";
7
6
  import { isArray, isNumber, isString } from "../util/TypeCheck";
@@ -25,7 +24,6 @@ interface ToneAudioBufferOptions {
25
24
  * @category Core
26
25
  */
27
26
  export class ToneAudioBuffer extends Tone {
28
-
29
27
  readonly name: string = "ToneAudioBuffer";
30
28
 
31
29
  /**
@@ -54,23 +52,26 @@ export class ToneAudioBuffer extends Tone {
54
52
  constructor(
55
53
  url?: string | ToneAudioBuffer | AudioBuffer,
56
54
  onload?: (buffer: ToneAudioBuffer) => void,
57
- onerror?: (error: Error) => void,
55
+ onerror?: (error: Error) => void
58
56
  );
59
57
  constructor(options?: Partial<ToneAudioBufferOptions>);
60
58
  constructor() {
61
-
62
59
  super();
63
60
 
64
- const options = optionsFromArguments(ToneAudioBuffer.getDefaults(), arguments, ["url", "onload", "onerror"]);
61
+ const options = optionsFromArguments(
62
+ ToneAudioBuffer.getDefaults(),
63
+ arguments,
64
+ ["url", "onload", "onerror"]
65
+ );
65
66
 
66
67
  this.reverse = options.reverse;
67
68
  this.onload = options.onload;
68
69
 
69
- if (options.url && isAudioBuffer(options.url) || options.url instanceof ToneAudioBuffer) {
70
- this.set(options.url);
71
- } else if (isString(options.url)) {
70
+ if (isString(options.url)) {
72
71
  // initiate the download
73
72
  this.load(options.url).catch(options.onerror);
73
+ } else if (options.url) {
74
+ this.set(options.url);
74
75
  }
75
76
  }
76
77
 
@@ -132,11 +133,13 @@ export class ToneAudioBuffer extends Tone {
132
133
  * @returns A Promise which resolves with this ToneAudioBuffer
133
134
  */
134
135
  async load(url: string): Promise<this> {
135
- const doneLoading: Promise<void> = ToneAudioBuffer.load(url).then(audioBuffer => {
136
- this.set(audioBuffer);
137
- // invoke the onload method
138
- this.onload(this);
139
- });
136
+ const doneLoading: Promise<void> = ToneAudioBuffer.load(url).then(
137
+ (audioBuffer) => {
138
+ this.set(audioBuffer);
139
+ // invoke the onload method
140
+ this.onload(this);
141
+ }
142
+ );
140
143
  ToneAudioBuffer.downloads.push(doneLoading);
141
144
  try {
142
145
  await doneLoading;
@@ -165,11 +168,15 @@ export class ToneAudioBuffer extends Tone {
165
168
  fromArray(array: Float32Array | Float32Array[]): this {
166
169
  const isMultidimensional = isArray(array) && array[0].length > 0;
167
170
  const channels = isMultidimensional ? array.length : 1;
168
- const len = isMultidimensional ? (array[0] as Float32Array).length : array.length;
171
+ const len = isMultidimensional
172
+ ? (array[0] as Float32Array).length
173
+ : array.length;
169
174
  const context = getContext();
170
175
  const buffer = context.createBuffer(channels, len, context.sampleRate);
171
- const multiChannelArray: Float32Array[] = !isMultidimensional && channels === 1 ?
172
- [array as Float32Array] : array as Float32Array[];
176
+ const multiChannelArray: Float32Array[] =
177
+ !isMultidimensional && channels === 1
178
+ ? [array as Float32Array]
179
+ : (array as Float32Array[]);
173
180
 
174
181
  for (let c = 0; c < channels; c++) {
175
182
  buffer.copyToChannel(multiChannelArray[c], c);
@@ -195,7 +202,7 @@ export class ToneAudioBuffer extends Tone {
195
202
  }
196
203
  }
197
204
  // divide by the number of channels
198
- outputArray = outputArray.map(sample => sample / numChannels);
205
+ outputArray = outputArray.map((sample) => sample / numChannels);
199
206
  this.fromArray(outputArray);
200
207
  }
201
208
  return this;
@@ -243,11 +250,21 @@ export class ToneAudioBuffer extends Tone {
243
250
  assert(this.loaded, "Buffer is not loaded");
244
251
  const startSamples = Math.floor(start * this.sampleRate);
245
252
  const endSamples = Math.floor(end * this.sampleRate);
246
- assert(startSamples < endSamples, "The start time must be less than the end time");
253
+ assert(
254
+ startSamples < endSamples,
255
+ "The start time must be less than the end time"
256
+ );
247
257
  const length = endSamples - startSamples;
248
- const retBuffer = getContext().createBuffer(this.numberOfChannels, length, this.sampleRate);
258
+ const retBuffer = getContext().createBuffer(
259
+ this.numberOfChannels,
260
+ length,
261
+ this.sampleRate
262
+ );
249
263
  for (let channel = 0; channel < this.numberOfChannels; channel++) {
250
- retBuffer.copyToChannel(this.getChannelData(channel).subarray(startSamples, endSamples), channel);
264
+ retBuffer.copyToChannel(
265
+ this.getChannelData(channel).subarray(startSamples, endSamples),
266
+ channel
267
+ );
251
268
  }
252
269
  return new ToneAudioBuffer(retBuffer);
253
270
  }
@@ -333,7 +350,7 @@ export class ToneAudioBuffer extends Tone {
333
350
  * @return A ToneAudioBuffer created from the array
334
351
  */
335
352
  static fromArray(array: Float32Array | Float32Array[]): ToneAudioBuffer {
336
- return (new ToneAudioBuffer()).fromArray(array);
353
+ return new ToneAudioBuffer().fromArray(array);
337
354
  }
338
355
 
339
356
  /**
@@ -355,7 +372,6 @@ export class ToneAudioBuffer extends Tone {
355
372
  * Loads a url using fetch and returns the AudioBuffer.
356
373
  */
357
374
  static async load(url: string): Promise<AudioBuffer> {
358
-
359
375
  // test if the url contains multiple extensions
360
376
  const matches = url.match(/\[([^\]\[]+\|.+)\]$/);
361
377
  if (matches) {
@@ -371,12 +387,19 @@ export class ToneAudioBuffer extends Tone {
371
387
  }
372
388
 
373
389
  // make sure there is a slash between the baseUrl and the url
374
- const baseUrl = ToneAudioBuffer.baseUrl === "" || ToneAudioBuffer.baseUrl.endsWith("/") ? ToneAudioBuffer.baseUrl : ToneAudioBuffer.baseUrl + "/";
375
-
390
+ const baseUrl =
391
+ ToneAudioBuffer.baseUrl === "" ||
392
+ ToneAudioBuffer.baseUrl.endsWith("/")
393
+ ? ToneAudioBuffer.baseUrl
394
+ : ToneAudioBuffer.baseUrl + "/";
395
+
376
396
  // encode special characters in file path
377
397
  const location = document.createElement("a");
378
- location.href = (baseUrl + url);
379
- location.pathname = (location.pathname + location.hash).split("/").map(encodeURIComponent).join("/");
398
+ location.href = baseUrl + url;
399
+ location.pathname = (location.pathname + location.hash)
400
+ .split("/")
401
+ .map(encodeURIComponent)
402
+ .join("/");
380
403
 
381
404
  const response = await fetch(location.href);
382
405
  if (!response.ok) {
@@ -401,7 +424,9 @@ export class ToneAudioBuffer extends Tone {
401
424
  static supportsType(url: string): boolean {
402
425
  const extensions = url.split(".");
403
426
  const extension = extensions[extensions.length - 1];
404
- const response = document.createElement("audio").canPlayType("audio/" + extension);
427
+ const response = document
428
+ .createElement("audio")
429
+ .canPlayType("audio/" + extension);
405
430
  return response !== "";
406
431
  }
407
432
 
@@ -89,19 +89,21 @@ describe("Pattern", () => {
89
89
  it("is invoked after it's started", () => {
90
90
  let invoked = false;
91
91
  return Offline(({ transport }) => {
92
+ const values = ["a", "b", "c"];
92
93
  let index = 0;
93
94
  const pattern = new Pattern((() => {
94
95
  invoked = true;
95
- expect(pattern.value).to.equal(index);
96
+ expect(pattern.value).to.equal(values[index]);
97
+ expect(pattern.index).to.equal(index);
96
98
  index++;
97
- }), [0, 1, 2]).start(0);
99
+ }), values).start(0);
98
100
  transport.start();
99
101
  }, 0.2).then(() => {
100
102
  expect(invoked).to.be.true;
101
103
  });
102
104
  });
103
105
 
104
- it("passes in the scheduled time and pattern index to the callback", () => {
106
+ it("passes in the scheduled time and pattern note to the callback", () => {
105
107
  let invoked = false;
106
108
  return Offline(({ transport }) => {
107
109
  const startTime = 0.05;
@@ -109,6 +111,8 @@ describe("Pattern", () => {
109
111
  expect(time).to.be.a("number");
110
112
  expect(time - startTime).to.be.closeTo(0.3, 0.01);
111
113
  expect(note).to.be.equal("a");
114
+ expect(pattern.value).to.equal("a");
115
+ expect(pattern.index).to.be.equal(0);
112
116
  invoked = true;
113
117
  }), ["a"], "up");
114
118
  transport.start(startTime);
@@ -121,16 +125,38 @@ describe("Pattern", () => {
121
125
  it("passes in the next note of the pattern", () => {
122
126
  let counter = 0;
123
127
  return Offline(({ transport }) => {
128
+ const values = ["a", "b", "c"];
124
129
  const pattern = new Pattern(((time, note) => {
125
- expect(note).to.equal(counter % 3);
130
+ expect(note).to.equal(values[counter % 3]);
131
+ expect(pattern.value).to.equal(values[counter % 3]);
132
+ expect(pattern.index).to.be.equal(counter % 3);
126
133
  counter++;
127
- }), [0, 1, 2], "up").start(0);
134
+ }), values, "up").start(0);
128
135
  pattern.interval = "16n";
129
136
  transport.start(0);
130
137
  }, 0.7).then(() => {
131
138
  expect(counter).to.equal(6);
132
139
  });
133
140
  });
141
+
142
+ it("can modify the pattern type and values", () => {
143
+ let counter = 0;
144
+ return Offline(({ transport }) => {
145
+ const values = ["a", "b", "c"];
146
+ const pattern = new Pattern(((time, note) => {
147
+ expect(note).to.equal(values[counter % 3]);
148
+ expect(pattern.value).to.equal(values[counter % 3]);
149
+ expect(pattern.index).to.be.equal(counter % 3);
150
+ counter++;
151
+ }), ["a"], "down").start(0);
152
+ pattern.interval = "16n";
153
+ pattern.pattern = "up";
154
+ pattern.values = values;
155
+ transport.start(0);
156
+ }, 0.7).then(() => {
157
+ expect(counter).to.equal(6);
158
+ });
159
+ });
134
160
  });
135
161
 
136
162
  });
@@ -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
  }