converting Pattern to typescript

This commit is contained in:
Yotam Mann 2019-11-07 14:39:28 -05:00
parent e24f12c4a9
commit 93eab5e6a3
6 changed files with 263 additions and 131 deletions

View file

@ -29,7 +29,7 @@ export interface LoopOptions extends ToneWithContextOptions {
* Transport.start();
* @category Event
*/
export class Loop extends ToneWithContext<LoopOptions> {
export class Loop<Options extends LoopOptions = LoopOptions> extends ToneWithContext<Options> {
readonly name: string = "Loop";
@ -110,7 +110,7 @@ export class Loop extends ToneWithContext<LoopOptions> {
* Internal function called when the notes should be called
* @param time The time the event occurs
*/
private _tick(time: Seconds) {
protected _tick(time: Seconds): void {
this.callback(time);
}

View file

@ -1,126 +0,0 @@
import Tone from "../core/Tone";
import "../event/Loop";
import "../control/CtrlPattern";
/**
* @class Tone.Pattern arpeggiates between the given notes
* in a number of patterns. See Tone.CtrlPattern for
* a full list of patterns.
* @example
* var pattern = new Tone.Pattern(function(time, note){
* //the order of the notes passed in depends on the pattern
* }, ["C2", "D4", "E5", "A6"], "upDown");
* @extends {Tone.Loop}
* @param {Function} callback The callback to invoke with the event.
* @param {Array} values The values to arpeggiate over.
*/
Tone.Pattern = function(){
var options = Tone.defaults(arguments, ["callback", "values", "pattern"], Tone.Pattern);
Tone.Loop.call(this, options);
/**
* The pattern manager
* @type {Tone.CtrlPattern}
* @private
*/
this._pattern = new Tone.CtrlPattern({
"values" : options.values,
"type" : options.pattern,
"index" : options.index
});
};
Tone.extend(Tone.Pattern, Tone.Loop);
/**
* The defaults
* @const
* @type {Object}
*/
Tone.Pattern.defaults = {
"pattern" : Tone.CtrlPattern.Type.Up,
"callback" : Tone.noOp,
"values" : [],
};
/**
* Internal function called when the notes should be called
* @param {Number} time The time the event occurs
* @private
*/
Tone.Pattern.prototype._tick = function(time){
this.callback(time, this._pattern.value);
this._pattern.next();
};
/**
* The current index in the values array.
* @memberOf Tone.Pattern#
* @type {Positive}
* @name index
*/
Object.defineProperty(Tone.Pattern.prototype, "index", {
get : function(){
return this._pattern.index;
},
set : function(i){
this._pattern.index = i;
}
});
/**
* The array of events.
* @memberOf Tone.Pattern#
* @type {Array}
* @name values
*/
Object.defineProperty(Tone.Pattern.prototype, "values", {
get : function(){
return this._pattern.values;
},
set : function(vals){
this._pattern.values = vals;
}
});
/**
* The current value of the pattern.
* @memberOf Tone.Pattern#
* @type {*}
* @name value
* @readOnly
*/
Object.defineProperty(Tone.Pattern.prototype, "value", {
get : function(){
return this._pattern.value;
}
});
/**
* The pattern type. See Tone.CtrlPattern for the full list of patterns.
* @memberOf Tone.Pattern#
* @type {String}
* @name pattern
*/
Object.defineProperty(Tone.Pattern.prototype, "pattern", {
get : function(){
return this._pattern.type;
},
set : function(pattern){
this._pattern.type = pattern;
}
});
/**
* Clean up
* @return {Tone.Pattern} this
*/
Tone.Pattern.prototype.dispose = function(){
Tone.Loop.prototype.dispose.call(this);
this._pattern.dispose();
this._pattern = null;
};
export default Tone.Pattern;

137
Tone/event/Pattern.test.ts Normal file
View file

@ -0,0 +1,137 @@
import { BasicTests } from "test/helper/Basic";
import { Pattern } from "./Pattern";
import { Offline } from "test/helper/Offline";
import { Time } from "Tone/core/type/Time";
import { expect } from "chai";
describe("Pattern", () => {
BasicTests(Pattern);
context("Constructor", () => {
it("takes a callback, an array of values and a pattern name", () => {
return Offline(() => {
const callback = function() {};
const pattern = new Pattern(callback, [0, 1, 2, 3], "down");
expect(pattern.callback).to.equal(callback);
expect(pattern.values).to.deep.equal([0, 1, 2, 3]);
expect(pattern.pattern).to.equal("down");
pattern.dispose();
});
});
it("can be constructed with no arguments", () => {
return Offline(() => {
const pattern = new Pattern();
pattern.dispose();
});
});
it("can pass in arguments in options object", () => {
return Offline(() => {
const callback = function() {};
const pattern = new Pattern({
callback: callback,
iterations: 4,
probability: 0.3,
interval: "8t",
values: [1, 2, 3],
pattern: "upDown"
});
expect(pattern.callback).to.equal(callback);
expect(pattern.interval.valueOf()).to.equal(Time("8t").valueOf());
expect(pattern.iterations).to.equal(4);
expect(pattern.values).to.deep.equal([1, 2, 3]);
expect(pattern.probability).to.equal(0.3);
expect(pattern.pattern).to.equal("upDown");
pattern.dispose();
});
});
});
context("Get/Set", () => {
it("can set values with object", () => {
return Offline(() => {
const callback = function() {};
const pattern = new Pattern();
pattern.set({
callback: callback,
values: ["a", "b", "c"],
});
expect(pattern.callback).to.equal(callback);
expect(pattern.values).to.deep.equal(["a", "b", "c"]);
pattern.dispose();
});
});
it("can set get a the values as an object", () => {
return Offline(() => {
const callback = function() {};
const pattern = new Pattern({
callback: callback,
pattern: "random",
probability: 0.3,
});
const values = pattern.get();
expect(values.pattern).to.equal("random");
values.pattern = "upDown";
expect(values.pattern).to.equal("upDown");
expect(values.probability).to.equal(0.3);
pattern.dispose();
});
});
});
context("Callback", () => {
it("is invoked after it's started", () => {
let invoked = false;
return Offline(({ transport }) => {
let index = 0;
const pattern = new Pattern((() => {
invoked = true;
expect(pattern.value).to.equal(index);
index++;
}), [0, 1, 2]).start(0);
transport.start();
}, 0.2).then(() => {
expect(invoked).to.be.true;
});
});
it("passes in the scheduled time and pattern index to the callback", () => {
let invoked = false;
return Offline(({ transport }) => {
const startTime = 0.05;
const pattern = new Pattern(((time, note) => {
expect(time).to.be.a("number");
expect(time - startTime).to.be.closeTo(0.3, 0.01);
expect(note).to.be.equal("a");
invoked = true;
}), ["a"], "up");
transport.start(startTime);
pattern.start(0.3);
}, 0.4).then(() => {
expect(invoked).to.be.true;
});
});
it("passes in the next note of the pattern", () => {
let counter = 0;
return Offline(({ transport }) => {
const pattern = new Pattern(((time, note) => {
expect(note).to.equal(counter % 3);
counter++;
}), [0, 1, 2], "up").start(0);
pattern.interval = "16n";
transport.start(0);
}, 0.7).then(() => {
expect(counter).to.equal(6);
});
});
});
});

121
Tone/event/Pattern.ts Normal file
View file

@ -0,0 +1,121 @@
import { Loop, LoopOptions } from "./Loop";
import { PatternGenerator, PatternName } from "./PatternGenerator";
import { ToneEventCallback } from "./ToneEvent";
import { optionsFromArguments } from "../core/util/Defaults";
import { Seconds } from "../core/type/Units";
import { noOp } from "../core/util/Interface";
export interface PatternOptions<ValueType> extends LoopOptions {
pattern: PatternName;
values: ValueType[];
callback: (time: Seconds, value?: ValueType) => void;
}
/**
* Pattern arpeggiates between the given notes
* in a number of patterns.
* @example
* import { Pattern } from "tone";
* const pattern = new Pattern((time, note) => {
* // the order of the notes passed in depends on the pattern
* }, ["C2", "D4", "E5", "A6"], "upDown");
*/
export class Pattern<ValueType> extends Loop<PatternOptions<ValueType>> {
readonly name: string = "Pattern";
/**
* The pattern generator function
*/
private _pattern: Iterator<ValueType>;
/**
* The current value
*/
private _value?: ValueType;
/**
* Hold the pattern type
*/
private _type: PatternName;
/**
* Hold the values
*/
private _values: ValueType[];
/**
* The callback to be invoked at a regular interval
*/
callback: (time: Seconds, value?: ValueType) => void;
/**
* @param callback The callback to invoke with the event.
* @param values The values to arpeggiate over.
* @param pattern The name of the pattern
*/
constructor(
callback?: ToneEventCallback<ValueType>,
values?: ValueType[],
pattern?: PatternName,
);
constructor(options?: Partial<PatternOptions<ValueType>>);
constructor() {
super(optionsFromArguments(Pattern.getDefaults(), arguments, ["callback", "values", "pattern"]));
const options = optionsFromArguments(Pattern.getDefaults(), arguments, ["callback", "values", "pattern"]);
this.callback = options.callback;
this._values = options.values;
this._pattern = PatternGenerator(options.values, options.pattern);
this._type = options.pattern;
}
static getDefaults(): PatternOptions<any> {
return Object.assign(Loop.getDefaults(), {
pattern: "up" as "up",
values: [],
callback: noOp,
});
}
/**
* Internal function called when the notes should be called
*/
protected _tick(time: Seconds): void {
const value = this._pattern.next() as IteratorResult<ValueType>;
this._value = value.value;
this.callback(time, this._value);
}
/**
* The array of events.
*/
get values(): ValueType[] {
return this._values;
}
set values(val) {
this._values = val;
// reset the pattern
this.pattern = this._type;
}
/**
* The current value of the pattern.
*/
get value(): ValueType | undefined {
return this._value;
}
/**
* The pattern type. See Tone.CtrlPattern for the full list of patterns.
*/
get pattern(): PatternName {
return this._type;
}
set pattern(pattern) {
this._type = pattern;
this._pattern = PatternGenerator(this._values, this._type);
}
}

View file

@ -3,8 +3,7 @@ import { assert } from "../core/util/Debug";
/**
* The name of the patterns
*/
export type PatternName = "up" | "down" | "upDown" | "downUp" |
"alternateUp" | "alternateDown" | "random" | "randomOnce";
export type PatternName = "up" | "down" | "upDown" | "downUp" | "alternateUp" | "alternateDown" | "random" | "randomOnce";
/**
* Start at the first value and go up to the last
@ -128,8 +127,9 @@ function* randomOnce<T>(values: T[]): IterableIterator<T> {
* of values and yield the items according to the passed in pattern
* @param values An array of values to iterate over
* @param pattern The name of the pattern use when iterating over
* @param index Where to start in the offset of the values array
*/
export function* PatternGenerator<T>(values: T[], pattern: PatternName = "up"): Iterator<T> {
export function* PatternGenerator<T>(values: T[], pattern: PatternName = "up", index: number = 0): Iterator<T> {
// safeguards
assert(values.length > 0, "The array must have more than one value in it");
switch (pattern) {