Updating zifferjs and rearrange classes under folder
This commit is contained in:
122
src/classes/Event.ts
Normal file
122
src/classes/Event.ts
Normal file
@ -0,0 +1,122 @@
|
||||
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 } = {};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
almostNever = (func: Function): Event => {
|
||||
return this.odds(0.025, func);
|
||||
}
|
||||
|
||||
rarely = (func: Function): Event => {
|
||||
return this.odds(0.1, func);
|
||||
}
|
||||
|
||||
scarcely = (func: Function): Event => {
|
||||
return this.odds(0.25, func);
|
||||
}
|
||||
|
||||
sometimes = (func: Function): Event => {
|
||||
return this.odds(0.5, func);
|
||||
}
|
||||
|
||||
often = (func: Function): Event => {
|
||||
return this.odds(0.75, func);
|
||||
}
|
||||
|
||||
frequently = (func: Function): Event => {
|
||||
return this.odds(0.9, func);
|
||||
}
|
||||
|
||||
almostAlways = (func: Function): Event => {
|
||||
return this.odds(0.985, func);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export abstract class SoundEvent extends Event {
|
||||
constructor(app: Editor) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
octave = (value: number): this => {
|
||||
this.values['octave'] = 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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
92
src/classes/Note.ts
Normal file
92
src/classes/Note.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import { SoundEvent } from './Event';
|
||||
import { type Editor } from '../main';
|
||||
import { MidiConnection } from "../IO/MidiConnection";
|
||||
import { midiToFreq, noteFromPc } from 'zifferjs';
|
||||
|
||||
export class Note extends SoundEvent {
|
||||
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
|
||||
}
|
||||
|
||||
note = (value: number): this => {
|
||||
this.values['note'] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
sustain = (value: number): this => {
|
||||
this.values['sustain'] = 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;
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
update = (): void => {
|
||||
if(this.values.key && this.values.pitch && this.values.parsedScale && this.values.octave) {
|
||||
const [note,bend] = noteFromPc(this.values.key, this.values.pitch, this.values.parsedScale, this.values.octave);
|
||||
this.values.note = note;
|
||||
this.values.freq = midiToFreq(note);
|
||||
if(bend) this.values.bend = 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);
|
||||
}
|
||||
|
||||
}
|
||||
37
src/classes/Rest.ts
Normal file
37
src/classes/Rest.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { type Editor } from '../main';
|
||||
import { Event } from "./Event";
|
||||
|
||||
export class Rest extends Event {
|
||||
constructor(duration: number, app: Editor) {
|
||||
super(app);
|
||||
this.values["duration"] = duration;
|
||||
}
|
||||
|
||||
_fallbackMethod = (): Event => {
|
||||
return this;
|
||||
}
|
||||
|
||||
public static createRestProxy = (duration: number, app: Editor) => {
|
||||
const instance = new Rest(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?
|
||||
}
|
||||
|
||||
}
|
||||
242
src/classes/Sound.ts
Normal file
242
src/classes/Sound.ts
Normal file
@ -0,0 +1,242 @@
|
||||
import { type Editor } from '../main';
|
||||
import { SoundEvent } from './Event';
|
||||
import { midiToFreq, noteFromPc } from 'zifferjs';
|
||||
|
||||
import {
|
||||
superdough,
|
||||
// @ts-ignore
|
||||
} from "superdough";
|
||||
|
||||
export class Sound extends SoundEvent {
|
||||
|
||||
constructor(sound: string|object, public app: Editor) {
|
||||
super(app);
|
||||
if (typeof sound === 'string') this.values = { 's': sound, 'dur': 0.5 };
|
||||
else this.values = sound;
|
||||
}
|
||||
|
||||
attack = (value: number): this => {
|
||||
this.values["attack"] = value;
|
||||
return this;
|
||||
};
|
||||
atk = this.attack;
|
||||
|
||||
decay = (value: number): this => {
|
||||
this.values["decay"] = value;
|
||||
return this;
|
||||
};
|
||||
dec = this.decay;
|
||||
|
||||
release = (value: number): this => {
|
||||
this.values["release"] = value;
|
||||
return this;
|
||||
};
|
||||
rel = this.release;
|
||||
|
||||
unit = (value: number): this => {
|
||||
this.values["unit"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
freq = (value: number): this => {
|
||||
this.values["freq"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
fm = (value: number | string): this => {
|
||||
if (typeof value === "number") {
|
||||
this.values["fmi"] = value;
|
||||
} else {
|
||||
let values = value.split(":");
|
||||
this.values["fmi"] = parseFloat(values[0]);
|
||||
if (values.length > 1) this.values["fmh"] = parseFloat(values[1]);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
sound = (value: string): this => {
|
||||
this.values['s'] = value
|
||||
return this;
|
||||
};
|
||||
|
||||
fmi = (value: number): this => {
|
||||
this.values["fmi"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
fmh = (value: number): this => {
|
||||
this.values["fmh"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
nudge = (value: number): this => {
|
||||
this.values["nudge"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
cut = (value: number): this => {
|
||||
this.values["cut"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
loop = (value: number): this => {
|
||||
this.values["loop"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
clip = (value: number): this => {
|
||||
this.values["clip"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
n = (value: number): this => {
|
||||
this.values["n"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
note = (value: number): this => {
|
||||
this.values["note"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
speed = (value: number): this => {
|
||||
this.values["speed"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
begin = (value: number): this => {
|
||||
this.values["begin"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
end = (value: number): this => {
|
||||
this.values["end"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
gain = (value: number): this => {
|
||||
this.values["gain"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
cutoff = (value: number): this => {
|
||||
this.values["cutoff"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
resonance = (value: number): this => {
|
||||
this.values["resonance"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
hcutoff = (value: number): this => {
|
||||
this.values["hcutoff"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
hresonance = (value: number): this => {
|
||||
this.values["hresonance"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
bandf = (value: number): this => {
|
||||
this.values["bandf"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
bandq = (value: number): this => {
|
||||
this.values["bandq"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
coarse = (value: number): this => {
|
||||
this.values["coarse"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
crush = (value: number): this => {
|
||||
this.values["crush"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
shape = (value: number): this => {
|
||||
this.values["shape"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
pan = (value: number): this => {
|
||||
this.values["pan"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
vowel = (value: number): this => {
|
||||
this.values["vowel"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
delay = (value: number): this => {
|
||||
this.values["delay"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
delayfeedback = (value: number): this => {
|
||||
this.values["delayfeedback"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
delaytime = (value: number): this => {
|
||||
this.values["delaytime"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
orbit = (value: number): this => {
|
||||
this.values["orbit"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
room = (value: number): this => {
|
||||
this.values["room"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
size = (value: number): this => {
|
||||
this.values["size"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
velocity = (value: number): this => {
|
||||
this.values["velocity"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
modify = (func: Function): this => {
|
||||
const funcResult = func(this);
|
||||
if(funcResult instanceof Object) return funcResult;
|
||||
else {
|
||||
func(this.values);
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
// NOTE: Sustain of the sound. duration() from the superclass Event is used to change the note length.
|
||||
sustain = (value: number): this => {
|
||||
this.values["dur"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
update = (): void => {
|
||||
if(this.values.key && this.values.pitch && this.values.parsedScale && this.values.octave) {
|
||||
const [note,_] = noteFromPc(this.values.key, this.values.pitch, this.values.parsedScale, this.values.octave);
|
||||
this.values.freq = midiToFreq(note);
|
||||
}
|
||||
}
|
||||
|
||||
out = (): object => {
|
||||
return superdough(
|
||||
this.values,
|
||||
this.app.clock.pulse_duration,
|
||||
this.values.dur
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
99
src/classes/ZPlayer.ts
Normal file
99
src/classes/ZPlayer.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { Chord, Pitch, Rest as ZRest, Ziffers } from "zifferjs";
|
||||
import { Editor } from "../main";
|
||||
import { Event } from "./Event";
|
||||
import { Sound } from "./Sound";
|
||||
import { Note } from "./Note";
|
||||
import { Rest } from "./Rest";
|
||||
|
||||
export class Player extends Event {
|
||||
input: string;
|
||||
ziffers: Ziffers;
|
||||
callTime: number = 0;
|
||||
played: boolean = false;
|
||||
current!: Pitch|Chord|ZRest;
|
||||
retro: boolean = false;
|
||||
tick: number = 0;
|
||||
|
||||
constructor(input: string, options: object, public app: Editor) {
|
||||
super(app);
|
||||
this.input = input;
|
||||
this.ziffers = new Ziffers(input, options);
|
||||
}
|
||||
|
||||
next = (): Pitch|Chord|ZRest => {
|
||||
this.current = this.ziffers.next() as Pitch|Chord|ZRest;
|
||||
this.played = true;
|
||||
return this.current;
|
||||
}
|
||||
|
||||
areWeThereYet = (): boolean => {
|
||||
const howAboutNow = (this.ziffers.notStarted() || this.app.api.epulse() > this.callTime+(this.current.duration*this.app.api.ppqn()));
|
||||
if(howAboutNow) {
|
||||
this.tick = 0;
|
||||
} else {
|
||||
this.tick++;
|
||||
}
|
||||
return howAboutNow;
|
||||
}
|
||||
|
||||
sound(name: string) {
|
||||
if(this.areWeThereYet()) {
|
||||
const event = this.next() as Pitch|Chord|ZRest;
|
||||
if(event instanceof Pitch) {
|
||||
// TODO: Quick hack. Select which attributes to use, but some ziffers stuff is needed for chaining key change etc.
|
||||
const obj = event.getExisting("freq","pitch","key","scale","octave");
|
||||
return new Sound(obj, this.app).sound(name);
|
||||
} else if(event instanceof Rest) {
|
||||
return Rest.createRestProxy(event.duration, this.app);
|
||||
}
|
||||
} else {
|
||||
// Not really a rest, but calling for skipping undefined methods
|
||||
return Rest.createRestProxy(0, this.app);
|
||||
}
|
||||
}
|
||||
|
||||
note(value: number|undefined = undefined) {
|
||||
if(this.areWeThereYet()) {
|
||||
const event = this.next() as Pitch|Chord|ZRest;
|
||||
if(event instanceof Pitch) {
|
||||
// TODO: Quick hack. Select which attributes to use, but some ziffers stuff is needed for chaining key change etc.
|
||||
const obj = event.getExisting("note","pitch","bend","key","scale","octave");
|
||||
const note = new Note(obj, this.app);
|
||||
return value ? note.note(value) : note;
|
||||
} else if(event instanceof ZRest) {
|
||||
return Rest.createRestProxy(event.duration, this.app);
|
||||
}
|
||||
} else {
|
||||
// Not really a rest, but calling for skipping undefined methods
|
||||
return Rest.createRestProxy(0, this.app);
|
||||
}
|
||||
}
|
||||
|
||||
scale(name: string) {
|
||||
this.ziffers.scale(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
key(name: string) {
|
||||
this.ziffers.key(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
octave(value: number) {
|
||||
this.ziffers.octave(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
retrograde() {
|
||||
if(this.tick === 0 && this.ziffers.index === 0) {
|
||||
this.ziffers.retrograde();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
out = (): void => {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user