Merge branch 'main' of github.com:Bubobubobubobubo/Topos into dev
This commit is contained in:
@ -1,122 +1,126 @@
|
||||
import { type Editor } from '../main';
|
||||
import { freqToMidi, resolvePitchBend, getScale, isScale, parseScala } from 'zifferjs';
|
||||
import { type Editor } from "../main";
|
||||
import {
|
||||
freqToMidi,
|
||||
resolvePitchBend,
|
||||
getScale,
|
||||
isScale,
|
||||
parseScala,
|
||||
} from "zifferjs";
|
||||
|
||||
export abstract class Event {
|
||||
seedValue: string|undefined = undefined;
|
||||
randomGen: Function = Math.random;
|
||||
app: Editor;
|
||||
values: { [key: string]: any } = {};
|
||||
seedValue: string | undefined = undefined;
|
||||
randomGen: Function = Math.random;
|
||||
app: Editor;
|
||||
values: { [key: string]: any } = {};
|
||||
|
||||
constructor(app: Editor) {
|
||||
this.app = app;
|
||||
if(this.app.api.currentSeed) {
|
||||
this.randomGen = this.app.api.randomGen;
|
||||
}
|
||||
constructor(app: Editor) {
|
||||
this.app = app;
|
||||
if (this.app.api.currentSeed) {
|
||||
this.randomGen = this.app.api.randomGen;
|
||||
}
|
||||
}
|
||||
|
||||
odds = (probability: number, func: Function): Event => {
|
||||
if(this.randomGen() < probability) {
|
||||
return this.modify(func);
|
||||
}
|
||||
return this;
|
||||
odds = (probability: number, func: Function): Event => {
|
||||
if (this.randomGen() < probability) {
|
||||
return this.modify(func);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
almostNever = (func: Function): Event => {
|
||||
return this.odds(0.025, func);
|
||||
}
|
||||
almostNever = (func: Function): Event => {
|
||||
return this.odds(0.025, func);
|
||||
};
|
||||
|
||||
rarely = (func: Function): Event => {
|
||||
return this.odds(0.1, func);
|
||||
}
|
||||
rarely = (func: Function): Event => {
|
||||
return this.odds(0.1, func);
|
||||
};
|
||||
|
||||
scarcely = (func: Function): Event => {
|
||||
return this.odds(0.25, func);
|
||||
}
|
||||
scarcely = (func: Function): Event => {
|
||||
return this.odds(0.25, func);
|
||||
};
|
||||
|
||||
sometimes = (func: Function): Event => {
|
||||
return this.odds(0.5, func);
|
||||
}
|
||||
sometimes = (func: Function): Event => {
|
||||
return this.odds(0.5, func);
|
||||
};
|
||||
|
||||
often = (func: Function): Event => {
|
||||
return this.odds(0.75, func);
|
||||
}
|
||||
often = (func: Function): Event => {
|
||||
return this.odds(0.75, func);
|
||||
};
|
||||
|
||||
frequently = (func: Function): Event => {
|
||||
return this.odds(0.9, func);
|
||||
}
|
||||
frequently = (func: Function): Event => {
|
||||
return this.odds(0.9, func);
|
||||
};
|
||||
|
||||
almostAlways = (func: Function): Event => {
|
||||
return this.odds(0.985, func);
|
||||
}
|
||||
almostAlways = (func: Function): Event => {
|
||||
return this.odds(0.985, func);
|
||||
};
|
||||
|
||||
modify = (func: Function): Event => {
|
||||
return func(this);
|
||||
}
|
||||
modify = (func: Function): Event => {
|
||||
return func(this);
|
||||
};
|
||||
|
||||
seed = (value: string|number): Event => {
|
||||
this.seedValue = value.toString();
|
||||
this.randomGen = this.app.api.localSeededRandom(this.seedValue);
|
||||
return this;
|
||||
}
|
||||
seed = (value: string | number): Event => {
|
||||
this.seedValue = value.toString();
|
||||
this.randomGen = this.app.api.localSeededRandom(this.seedValue);
|
||||
return this;
|
||||
};
|
||||
|
||||
clear = (): Event => {
|
||||
this.app.api.clearLocalSeed(this.seedValue);
|
||||
return this;
|
||||
}
|
||||
clear = (): Event => {
|
||||
this.app.api.clearLocalSeed(this.seedValue);
|
||||
return this;
|
||||
};
|
||||
|
||||
apply = (func: Function): Event => {
|
||||
return this.modify(func);
|
||||
}
|
||||
|
||||
duration = (value: number): Event => {
|
||||
this.values['duration'] = value;
|
||||
return this;
|
||||
}
|
||||
apply = (func: Function): Event => {
|
||||
return this.modify(func);
|
||||
};
|
||||
|
||||
duration = (value: number): Event => {
|
||||
this.values["duration"] = value;
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
export abstract class AudibleEvent extends Event {
|
||||
constructor(app: Editor) {
|
||||
super(app);
|
||||
}
|
||||
constructor(app: Editor) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
octave = (value: number): this => {
|
||||
this.values['octave'] = value;
|
||||
this.update();
|
||||
return this;
|
||||
}
|
||||
octave = (value: number): this => {
|
||||
this.values["octave"] = value;
|
||||
this.update();
|
||||
return this;
|
||||
};
|
||||
|
||||
key = (value: string): this => {
|
||||
this.values['key'] = value;
|
||||
this.update();
|
||||
return this;
|
||||
}
|
||||
key = (value: string): this => {
|
||||
this.values["key"] = value;
|
||||
this.update();
|
||||
return this;
|
||||
};
|
||||
|
||||
scale = (value: string): this => {
|
||||
if(!isScale(value)) {
|
||||
this.values.parsedScale = parseScala(value) as number[];
|
||||
} else {
|
||||
this.values.scaleName = value;
|
||||
this.values.parsedScale = getScale(value) as number[];
|
||||
}
|
||||
this.update();
|
||||
return this;
|
||||
scale = (value: string): this => {
|
||||
if (!isScale(value)) {
|
||||
this.values.parsedScale = parseScala(value) as number[];
|
||||
} else {
|
||||
this.values.scaleName = value;
|
||||
this.values.parsedScale = getScale(value) as number[];
|
||||
}
|
||||
this.update();
|
||||
return this;
|
||||
};
|
||||
|
||||
freq = (value: number): this => {
|
||||
this.values['freq'] = value;
|
||||
const midiNote = freqToMidi(value);
|
||||
if(midiNote % 1 !== 0) {
|
||||
this.values['note'] = Math.floor(midiNote);
|
||||
this.values['bend'] = resolvePitchBend(midiNote)[1];
|
||||
} else {
|
||||
this.values['note'] = midiNote;
|
||||
}
|
||||
return this;
|
||||
freq = (value: number): this => {
|
||||
this.values["freq"] = value;
|
||||
const midiNote = freqToMidi(value);
|
||||
if (midiNote % 1 !== 0) {
|
||||
this.values["note"] = Math.floor(midiNote);
|
||||
this.values["bend"] = resolvePitchBend(midiNote)[1];
|
||||
} else {
|
||||
this.values["note"] = midiNote;
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
update = (): void => {
|
||||
// Overwrite in subclasses
|
||||
}
|
||||
|
||||
}
|
||||
update = (): void => {
|
||||
// Overwrite in subclasses
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,95 +1,102 @@
|
||||
import { AudibleEvent } from './AbstractEvents';
|
||||
import { type Editor } from '../main';
|
||||
import { AudibleEvent } from "./AbstractEvents";
|
||||
import { type Editor } from "../main";
|
||||
import { MidiConnection } from "../IO/MidiConnection";
|
||||
import { midiToFreq, noteFromPc } from 'zifferjs';
|
||||
import { midiToFreq, noteFromPc } from "zifferjs";
|
||||
|
||||
export class NoteEvent extends AudibleEvent {
|
||||
midiConnection: MidiConnection;
|
||||
midiConnection: MidiConnection;
|
||||
|
||||
constructor(input: number|object, public app: Editor) {
|
||||
super(app);
|
||||
if(typeof input === 'number') this.values['note'] = input;
|
||||
else this.values = input;
|
||||
this.midiConnection = app.api.MidiConnection
|
||||
}
|
||||
constructor(input: number | object, public app: Editor) {
|
||||
super(app);
|
||||
if (typeof input === "number") this.values["note"] = input;
|
||||
else this.values = input;
|
||||
this.midiConnection = app.api.MidiConnection;
|
||||
}
|
||||
|
||||
note = (value: number): this => {
|
||||
this.values['note'] = value;
|
||||
return this;
|
||||
}
|
||||
note = (value: number): this => {
|
||||
this.values["note"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
sustain = (value: number): this => {
|
||||
this.values['sustain'] = value;
|
||||
return this;
|
||||
}
|
||||
sustain = (value: number): this => {
|
||||
this.values["sustain"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
channel = (value: number): this => {
|
||||
this.values['channel'] = value;
|
||||
return this;
|
||||
}
|
||||
channel = (value: number): this => {
|
||||
this.values["channel"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
port = (value: number|string): this => {
|
||||
this.values['port'] = this.midiConnection.getMidiOutputIndex(value);
|
||||
return this;
|
||||
}
|
||||
port = (value: number | string): this => {
|
||||
this.values["port"] = this.midiConnection.getMidiOutputIndex(value);
|
||||
return this;
|
||||
};
|
||||
|
||||
add = (value: number): this => {
|
||||
this.values.note += value;
|
||||
return this;
|
||||
}
|
||||
add = (value: number): this => {
|
||||
this.values.note += value;
|
||||
return this;
|
||||
};
|
||||
|
||||
modify = (func: Function): this => {
|
||||
const funcResult = func(this);
|
||||
if(funcResult instanceof Object) {
|
||||
return funcResult;
|
||||
}
|
||||
else {
|
||||
func(this.values);
|
||||
this.update();
|
||||
return this;
|
||||
}
|
||||
modify = (func: Function): this => {
|
||||
const funcResult = func(this);
|
||||
if (funcResult instanceof Object) {
|
||||
return funcResult;
|
||||
} else {
|
||||
func(this.values);
|
||||
this.update();
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
bend = (value: number): this => {
|
||||
this.values['bend'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
random = (min: number = 0, max: number = 127): this => {
|
||||
min = Math.min(Math.max(min, 0), 127);
|
||||
max = Math.min(Math.max(max, 0), 127);
|
||||
this.values['note'] = Math.floor(this.randomGen() * (max - min + 1)) + min;
|
||||
return this;
|
||||
}
|
||||
bend = (value: number): this => {
|
||||
this.values["bend"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
update = (): void => {
|
||||
const [note, bend] = noteFromPc(
|
||||
this.values.key || "C4",
|
||||
this.values.pitch || 0,
|
||||
this.values.parsedScale || "MAJOR",
|
||||
this.values.octave || 0
|
||||
);
|
||||
this.values.note = note;
|
||||
this.values.freq = midiToFreq(note);
|
||||
if(bend) this.values.bend = bend;
|
||||
}
|
||||
random = (min: number = 0, max: number = 127): this => {
|
||||
min = Math.min(Math.max(min, 0), 127);
|
||||
max = Math.min(Math.max(max, 0), 127);
|
||||
this.values["note"] = Math.floor(this.randomGen() * (max - min + 1)) + min;
|
||||
return this;
|
||||
};
|
||||
|
||||
out = (): void => {
|
||||
const note = this.values.note ? this.values.note : 60;
|
||||
const channel = this.values.channel ? this.values.channel : 0;
|
||||
const velocity = this.values.velocity ? this.values.velocity : 100;
|
||||
|
||||
const sustain = this.values.sustain ?
|
||||
this.values.sustain * this.app.clock.pulse_duration * this.app.api.ppqn() :
|
||||
this.app.clock.pulse_duration * this.app.api.ppqn();
|
||||
|
||||
const bend = this.values.bend ? this.values.bend : undefined;
|
||||
|
||||
const port = this.values.port ?
|
||||
this.midiConnection.getMidiOutputIndex(this.values.port) :
|
||||
this.midiConnection.getCurrentMidiPortIndex();
|
||||
update = (): void => {
|
||||
const [note, bend] = noteFromPc(
|
||||
this.values.key || "C4",
|
||||
this.values.pitch || 0,
|
||||
this.values.parsedScale || "MAJOR",
|
||||
this.values.octave || 0
|
||||
);
|
||||
this.values.note = note;
|
||||
this.values.freq = midiToFreq(note);
|
||||
if (bend) this.values.bend = bend;
|
||||
};
|
||||
|
||||
this.midiConnection.sendMidiNote(note, channel, velocity, sustain, port, bend);
|
||||
}
|
||||
|
||||
}
|
||||
out = (): void => {
|
||||
const note = this.values.note ? this.values.note : 60;
|
||||
const channel = this.values.channel ? this.values.channel : 0;
|
||||
const velocity = this.values.velocity ? this.values.velocity : 100;
|
||||
|
||||
const sustain = this.values.sustain
|
||||
? this.values.sustain *
|
||||
this.app.clock.pulse_duration *
|
||||
this.app.api.ppqn()
|
||||
: this.app.clock.pulse_duration * this.app.api.ppqn();
|
||||
|
||||
const bend = this.values.bend ? this.values.bend : undefined;
|
||||
|
||||
const port = this.values.port
|
||||
? this.midiConnection.getMidiOutputIndex(this.values.port)
|
||||
: this.midiConnection.getCurrentMidiPortIndex();
|
||||
|
||||
this.midiConnection.sendMidiNote(
|
||||
note,
|
||||
channel,
|
||||
velocity,
|
||||
sustain,
|
||||
port,
|
||||
bend
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,37 +1,39 @@
|
||||
import { type Editor } from '../main';
|
||||
import { type Editor } from "../main";
|
||||
import { Event } from "./AbstractEvents";
|
||||
|
||||
export class RestEvent extends Event {
|
||||
constructor(duration: number, app: Editor) {
|
||||
super(app);
|
||||
this.values["duration"] = duration;
|
||||
}
|
||||
constructor(duration: number, app: Editor) {
|
||||
super(app);
|
||||
this.values["duration"] = duration;
|
||||
}
|
||||
|
||||
_fallbackMethod = (): Event => {
|
||||
return RestEvent.createRestProxy(this.values["duration"], this.app);
|
||||
}
|
||||
_fallbackMethod = (): Event => {
|
||||
return RestEvent.createRestProxy(this.values["duration"], this.app);
|
||||
};
|
||||
|
||||
public static createRestProxy = (duration: number, app: Editor): RestEvent => {
|
||||
const instance = new RestEvent(duration, app);
|
||||
return new Proxy(instance, {
|
||||
// @ts-ignore
|
||||
get(target, propKey, receiver) {
|
||||
// @ts-ignore
|
||||
if (typeof target[propKey] === 'undefined') {
|
||||
return target._fallbackMethod;
|
||||
}
|
||||
// @ts-ignore
|
||||
return target[propKey];
|
||||
},
|
||||
// @ts-ignore
|
||||
set(target, propKey, value, receiver) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
public static createRestProxy = (
|
||||
duration: number,
|
||||
app: Editor
|
||||
): RestEvent => {
|
||||
const instance = new RestEvent(duration, app);
|
||||
return new Proxy(instance, {
|
||||
// @ts-ignore
|
||||
get(target, propKey, receiver) {
|
||||
// @ts-ignore
|
||||
if (typeof target[propKey] === "undefined") {
|
||||
return target._fallbackMethod;
|
||||
}
|
||||
// @ts-ignore
|
||||
return target[propKey];
|
||||
},
|
||||
// @ts-ignore
|
||||
set(target, propKey, value, receiver) {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
out = (): void => {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
}
|
||||
out = (): void => {
|
||||
// TODO?
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,28 +1,26 @@
|
||||
export class SkipEvent {
|
||||
_fallbackMethod = (): SkipEvent => {
|
||||
return SkipEvent.createSkipProxy();
|
||||
};
|
||||
|
||||
_fallbackMethod = (): SkipEvent => {
|
||||
return SkipEvent.createSkipProxy();
|
||||
}
|
||||
|
||||
public static createSkipProxy = (): SkipEvent => {
|
||||
const instance = new SkipEvent();
|
||||
return new Proxy(instance, {
|
||||
// @ts-ignore
|
||||
get(target, propKey, receiver) {
|
||||
// @ts-ignore
|
||||
if (typeof target[propKey] === 'undefined') {
|
||||
return target._fallbackMethod;
|
||||
}
|
||||
// @ts-ignore
|
||||
return target[propKey];
|
||||
},
|
||||
// @ts-ignore
|
||||
set(target, propKey, value, receiver) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
public static createSkipProxy = (): SkipEvent => {
|
||||
const instance = new SkipEvent();
|
||||
return new Proxy(instance, {
|
||||
// @ts-ignore
|
||||
get(target, propKey, receiver) {
|
||||
// @ts-ignore
|
||||
if (typeof target[propKey] === "undefined") {
|
||||
return target._fallbackMethod;
|
||||
}
|
||||
// @ts-ignore
|
||||
return target[propKey];
|
||||
},
|
||||
// @ts-ignore
|
||||
set(target, propKey, value, receiver) {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
out = (): void => {}
|
||||
|
||||
}
|
||||
out = (): void => {};
|
||||
}
|
||||
|
||||
@ -238,13 +238,13 @@ export class SoundEvent extends AudibleEvent {
|
||||
sus = this.sustain;
|
||||
|
||||
update = (): void => {
|
||||
const [note, _] = noteFromPc(
|
||||
this.values.key || "C4",
|
||||
this.values.pitch || 0,
|
||||
this.values.parsedScale || "MAJOR",
|
||||
this.values.octave || 0
|
||||
);
|
||||
this.values.freq = midiToFreq(note);
|
||||
const [note, _] = noteFromPc(
|
||||
this.values.key || "C4",
|
||||
this.values.pitch || 0,
|
||||
this.values.parsedScale || "MAJOR",
|
||||
this.values.octave || 0
|
||||
);
|
||||
this.values.freq = midiToFreq(note);
|
||||
};
|
||||
|
||||
out = (): object => {
|
||||
|
||||
@ -9,192 +9,204 @@ import { RestEvent } from "./RestEvent";
|
||||
export type InputOptions = { [key: string]: string | number };
|
||||
|
||||
export class Player extends Event {
|
||||
input: string;
|
||||
ziffers: Ziffers;
|
||||
initCallTime: number = 0;
|
||||
startCallTime: number = 0;
|
||||
lastCallTime: number = 0;
|
||||
waitTime = 0;
|
||||
startBeat: number = 0;
|
||||
played: boolean = false;
|
||||
current!: Pitch|Chord|ZRest;
|
||||
retro: boolean = false;
|
||||
index: number = -1;
|
||||
zid: string|undefined = undefined;
|
||||
options: InputOptions = {};
|
||||
input: string;
|
||||
ziffers: Ziffers;
|
||||
initCallTime: number = 1;
|
||||
startCallTime: number = 1;
|
||||
lastCallTime: number = 1;
|
||||
waitTime = 0;
|
||||
startBeat: number = 0;
|
||||
played: boolean = false;
|
||||
current!: Pitch | Chord | ZRest;
|
||||
retro: boolean = false;
|
||||
index: number = -1;
|
||||
zid: string = "";
|
||||
options: InputOptions = {};
|
||||
skipIndex = 0;
|
||||
endTime = 0;
|
||||
|
||||
constructor(input: string, options: InputOptions, public app: Editor) {
|
||||
super(app);
|
||||
this.input = input;
|
||||
this.options = options;
|
||||
this.ziffers = new Ziffers(input, options);
|
||||
constructor(input: string, options: InputOptions, public app: Editor) {
|
||||
super(app);
|
||||
this.input = input;
|
||||
this.options = options;
|
||||
this.ziffers = new Ziffers(input, options);
|
||||
}
|
||||
|
||||
get ticks(): number {
|
||||
const dur = this.ziffers.duration;
|
||||
return dur * 4 * this.app.clock.ppqn;
|
||||
}
|
||||
|
||||
nextEndTime(): number {
|
||||
return this.startCallTime + this.ticks;
|
||||
}
|
||||
|
||||
updateLastCallTime(): void {
|
||||
if (this.notStarted() || this.played) {
|
||||
this.lastCallTime = this.app.clock.pulses_since_origin;
|
||||
this.played = false;
|
||||
}
|
||||
}
|
||||
|
||||
notStarted(): boolean {
|
||||
return this.ziffers.notStarted();
|
||||
}
|
||||
|
||||
next = (): Pitch | Chord | ZRest => {
|
||||
this.current = this.ziffers.next() as Pitch | Chord | ZRest;
|
||||
this.played = true;
|
||||
return this.current;
|
||||
};
|
||||
|
||||
pulseToSecond = (pulse: number): number => {
|
||||
return this.app.clock.convertPulseToSecond(pulse);
|
||||
};
|
||||
|
||||
firstRun = (): boolean => {
|
||||
return this.notStarted();
|
||||
};
|
||||
|
||||
atTheBeginning = (): boolean => {
|
||||
return this.skipIndex === 0 && this.ziffers.index <= 0;
|
||||
};
|
||||
|
||||
origin = (): number => {
|
||||
return this.app.clock.pulses_since_origin;
|
||||
};
|
||||
|
||||
pulse = (): number => {
|
||||
return this.app.clock.time_position.pulse;
|
||||
};
|
||||
|
||||
beat = (): number => {
|
||||
return this.app.clock.time_position.beat;
|
||||
};
|
||||
|
||||
nextBeat = (): number => {
|
||||
return this.app.clock.next_beat_in_ticks;
|
||||
};
|
||||
|
||||
// Check if it's time to play the event
|
||||
areWeThereYet = (): boolean => {
|
||||
// If clock has stopped
|
||||
if (this.app.clock.pulses_since_origin < this.lastCallTime) {
|
||||
this.lastCallTime = 1;
|
||||
this.startCallTime = 1;
|
||||
this.index = 0;
|
||||
this.waitTime = 0;
|
||||
}
|
||||
|
||||
get ticks(): number {
|
||||
const dur = this.ziffers.duration;
|
||||
return dur * 4 * this.app.clock.ppqn;
|
||||
// Main logic
|
||||
const howAboutNow =
|
||||
// If pattern is just starting
|
||||
(this.notStarted() &&
|
||||
(this.app.clock.time_position.pulse === 1 ||
|
||||
this.app.clock.pulses_since_origin >=
|
||||
this.app.clock.next_beat_in_ticks) &&
|
||||
this.app.clock.pulses_since_origin >= this.waitTime) || // If pattern is already playing
|
||||
(this.current &&
|
||||
this.pulseToSecond(this.app.clock.pulses_since_origin) >=
|
||||
this.pulseToSecond(this.lastCallTime) +
|
||||
this.current.duration *
|
||||
4 *
|
||||
this.pulseToSecond(this.app.api.ppqn()) &&
|
||||
this.app.clock.pulses_since_origin >= this.waitTime);
|
||||
|
||||
// Increment index of how many times call is skipped
|
||||
this.skipIndex = howAboutNow ? 0 : this.skipIndex + 1;
|
||||
|
||||
// Increment index of how many times sound/midi have been called
|
||||
this.index = howAboutNow ? this.index + 1 : this.index;
|
||||
|
||||
if (howAboutNow && this.notStarted()) {
|
||||
this.initCallTime = this.app.clock.pulses_since_origin;
|
||||
}
|
||||
|
||||
nextEndTime(): number {
|
||||
return this.startCallTime + this.ticks;
|
||||
if (this.atTheBeginning()) {
|
||||
this.startCallTime = this.app.clock.pulses_since_origin;
|
||||
}
|
||||
|
||||
updateLastCallTime(): void {
|
||||
if (this.notStarted() || this.played) {
|
||||
this.lastCallTime = this.app.clock.pulses_since_origin;
|
||||
this.played = false;
|
||||
}
|
||||
}
|
||||
return howAboutNow;
|
||||
};
|
||||
|
||||
notStarted(): boolean {
|
||||
return this.ziffers.notStarted();
|
||||
}
|
||||
|
||||
next = (): Pitch|Chord|ZRest => {
|
||||
this.current = this.ziffers.next() as Pitch|Chord|ZRest;
|
||||
this.played = true;
|
||||
return this.current;
|
||||
}
|
||||
|
||||
pulseToSecond = (pulse: number): number => {
|
||||
return this.app.clock.convertPulseToSecond(pulse);
|
||||
}
|
||||
|
||||
firstRun = (): boolean => {
|
||||
return this.origin()<=0 && this.notStarted();
|
||||
}
|
||||
|
||||
atTheBeginning = (): boolean => {
|
||||
return this.pulse()===0 && this.ziffers.index===0;
|
||||
}
|
||||
|
||||
origin = (): number => {
|
||||
return this.app.clock.pulses_since_origin+1;
|
||||
}
|
||||
|
||||
pulse = (): number => {
|
||||
return this.app.clock.time_position.pulse;
|
||||
}
|
||||
|
||||
nextBeat = (): number => {
|
||||
return this.app.clock.next_beat_in_ticks;
|
||||
}
|
||||
|
||||
// Check if it's time to play the event
|
||||
areWeThereYet = (): boolean => {
|
||||
// If clock has stopped
|
||||
if(this.app.clock.pulses_since_origin<this.lastCallTime) {
|
||||
this.lastCallTime = 0;
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
// Main logic
|
||||
const howAboutNow = (
|
||||
( // If pattern is just starting
|
||||
this.notStarted() &&
|
||||
(this.app.clock.time_position.pulse === 1 ||
|
||||
this.app.clock.pulses_since_origin+1 >= this.app.clock.next_beat_in_ticks) &&
|
||||
(this.app.clock.pulses_since_origin+1 >= this.waitTime)
|
||||
)
|
||||
||
|
||||
( // If pattern is already playing
|
||||
this.current &&
|
||||
(this.pulseToSecond(this.app.clock.pulses_since_origin+1) >=
|
||||
this.pulseToSecond(this.lastCallTime) +
|
||||
(this.current.duration*4) * this.pulseToSecond(this.app.api.ppqn())) &&
|
||||
(this.app.clock.pulses_since_origin+1 >= this.waitTime)
|
||||
)
|
||||
sound(name: string) {
|
||||
if (this.areWeThereYet()) {
|
||||
const event = this.next() as Pitch | Chord | ZRest;
|
||||
if (event instanceof Pitch) {
|
||||
const obj = event.getExisting(
|
||||
"freq",
|
||||
"pitch",
|
||||
"key",
|
||||
"scale",
|
||||
"octave",
|
||||
"parsedScale"
|
||||
);
|
||||
|
||||
// Increment index of how many times sound/midi have been called
|
||||
this.index = howAboutNow ? this.index+1 : this.index;
|
||||
|
||||
if(howAboutNow && this.notStarted()) {
|
||||
this.initCallTime = this.app.clock.pulses_since_origin+1;
|
||||
}
|
||||
|
||||
if(this.atTheBeginning()) {
|
||||
this.startCallTime = this.app.clock.pulses_since_origin;
|
||||
}
|
||||
|
||||
return howAboutNow;
|
||||
return new SoundEvent(obj, this.app).sound(name);
|
||||
} else if (event instanceof ZRest) {
|
||||
return RestEvent.createRestProxy(event.duration, this.app);
|
||||
}
|
||||
} else {
|
||||
return SkipEvent.createSkipProxy();
|
||||
}
|
||||
}
|
||||
|
||||
sound(name: string) {
|
||||
if(this.areWeThereYet()) {
|
||||
const event = this.next() as Pitch|Chord|ZRest;
|
||||
if(event instanceof Pitch) {
|
||||
const obj = event.getExisting("freq","pitch","key","scale","octave","parsedScale");
|
||||
return new SoundEvent(obj, this.app).sound(name);
|
||||
} else if(event instanceof ZRest) {
|
||||
return RestEvent.createRestProxy(event.duration, this.app);
|
||||
}
|
||||
} else {
|
||||
return SkipEvent.createSkipProxy();
|
||||
}
|
||||
midi(value: number | undefined = undefined) {
|
||||
if (this.areWeThereYet()) {
|
||||
const event = this.next() as Pitch | Chord | ZRest;
|
||||
if (event instanceof Pitch) {
|
||||
const obj = event.getExisting(
|
||||
"note",
|
||||
"pitch",
|
||||
"bend",
|
||||
"key",
|
||||
"scale",
|
||||
"octave",
|
||||
"parsedScale"
|
||||
);
|
||||
const note = new NoteEvent(obj, this.app);
|
||||
return value ? note.note(value) : note;
|
||||
} else if (event instanceof ZRest) {
|
||||
return RestEvent.createRestProxy(event.duration, this.app);
|
||||
}
|
||||
} else {
|
||||
return SkipEvent.createSkipProxy();
|
||||
}
|
||||
}
|
||||
|
||||
midi(value: number|undefined = undefined) {
|
||||
if(this.areWeThereYet()) {
|
||||
const event = this.next() as Pitch|Chord|ZRest;
|
||||
if(event instanceof Pitch) {
|
||||
const obj = event.getExisting("note","pitch","bend","key","scale","octave","parsedScale");
|
||||
const note = new NoteEvent(obj, this.app);
|
||||
return value ? note.note(value) : note;
|
||||
} else if(event instanceof ZRest) {
|
||||
return RestEvent.createRestProxy(event.duration, this.app);
|
||||
}
|
||||
} else {
|
||||
return SkipEvent.createSkipProxy();
|
||||
}
|
||||
}
|
||||
scale(name: string) {
|
||||
if (this.atTheBeginning()) this.ziffers.scale(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
scale(name: string) {
|
||||
this.ziffers.scale(name);
|
||||
key(name: string) {
|
||||
if (this.atTheBeginning()) this.ziffers.key(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
octave(value: number) {
|
||||
if (this.atTheBeginning()) this.ziffers.octave(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
retrograde() {
|
||||
if (this.atTheBeginning()) this.ziffers.retrograde();
|
||||
return this;
|
||||
}
|
||||
|
||||
wait(value: number | Function) {
|
||||
if (this.atTheBeginning()) {
|
||||
if (typeof value === "function") {
|
||||
const refPat = this.app.api.patternCache.get(value.name) as Player;
|
||||
if (refPat) this.waitTime = refPat.nextEndTime();
|
||||
return this;
|
||||
}
|
||||
this.waitTime =
|
||||
this.origin() + Math.ceil(value * 4 * this.app.clock.ppqn);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
key(name: string) {
|
||||
this.ziffers.key(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
octave(value: number) {
|
||||
this.ziffers.octave(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
retrograde() {
|
||||
if(this.index === -1 && this.ziffers.index === -1) {
|
||||
this.ziffers.retrograde();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
wait(value: number) {
|
||||
if(this.index === -1 && this.ziffers.index === -1) {
|
||||
|
||||
// TODO: THIS LATER!
|
||||
|
||||
/* if(typeof value === "string") {
|
||||
const cueKey = this.app.api.patternCues.get(value);
|
||||
if(cueKey) {
|
||||
const waitedPatter = this.app.api.patternCache.get(cueKey) as Player;
|
||||
if(waitedPatter) {
|
||||
this.waitTime = waitedPatter.nextEndTime();
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
||||
this.waitTime = this.origin() + Math.ceil(value*4*this.app.clock.ppqn);
|
||||
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
out = (): void => {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
out = (): void => {
|
||||
// TODO?
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user