mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-15 08:17:07 +00:00
converting Pattern to typescript
This commit is contained in:
parent
e24f12c4a9
commit
93eab5e6a3
6 changed files with 263 additions and 131 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
137
Tone/event/Pattern.test.ts
Normal 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
121
Tone/event/Pattern.ts
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
Loading…
Reference in a new issue