Added chords to ziffers
This commit is contained in:
@ -39,7 +39,7 @@
|
|||||||
"tone": "^14.8.49",
|
"tone": "^14.8.49",
|
||||||
"unique-names-generator": "^4.7.1",
|
"unique-names-generator": "^4.7.1",
|
||||||
"vite-plugin-markdown": "^2.1.0",
|
"vite-plugin-markdown": "^2.1.0",
|
||||||
"zifferjs": "^0.0.19",
|
"zifferjs": "^0.0.21",
|
||||||
"zzfx": "^1.2.0"
|
"zzfx": "^1.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { DrunkWalk } from "./Utils/Drunk";
|
|||||||
import { scale } from "./Scales";
|
import { scale } from "./Scales";
|
||||||
import { Editor } from "./main";
|
import { Editor } from "./main";
|
||||||
import { SoundEvent } from "./classes/SoundEvent";
|
import { SoundEvent } from "./classes/SoundEvent";
|
||||||
import { NoteEvent } from "./classes/MidiEvent";
|
import { MidiEvent } from "./classes/MidiEvent";
|
||||||
import { LRUCache } from "lru-cache";
|
import { LRUCache } from "lru-cache";
|
||||||
import { InputOptions, Player } from "./classes/ZPlayer";
|
import { InputOptions, Player } from "./classes/ZPlayer";
|
||||||
import {
|
import {
|
||||||
@ -316,7 +316,7 @@ export class UserAPI {
|
|||||||
value: number | object = 60,
|
value: number | object = 60,
|
||||||
velocity?: number,
|
velocity?: number,
|
||||||
channel?: number
|
channel?: number
|
||||||
): NoteEvent => {
|
): MidiEvent => {
|
||||||
/**
|
/**
|
||||||
* Sends a MIDI note to the current MIDI output.
|
* Sends a MIDI note to the current MIDI output.
|
||||||
*
|
*
|
||||||
@ -342,7 +342,7 @@ export class UserAPI {
|
|||||||
value["channel"] = channel;
|
value["channel"] = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NoteEvent(value, this.app);
|
return new MidiEvent(value, this.app);
|
||||||
};
|
};
|
||||||
|
|
||||||
public sysex = (data: Array<number>): void => {
|
public sysex = (data: Array<number>): void => {
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { type Editor } from "../main";
|
|||||||
import { MidiConnection } from "../IO/MidiConnection";
|
import { MidiConnection } from "../IO/MidiConnection";
|
||||||
import { midiToFreq, noteFromPc } from "zifferjs";
|
import { midiToFreq, noteFromPc } from "zifferjs";
|
||||||
|
|
||||||
export class NoteEvent extends AudibleEvent {
|
export class MidiEvent extends AudibleEvent {
|
||||||
midiConnection: MidiConnection;
|
midiConnection: MidiConnection;
|
||||||
|
|
||||||
constructor(input: number | object, public app: Editor) {
|
constructor(input: number | object, public app: Editor) {
|
||||||
@ -13,6 +13,11 @@ export class NoteEvent extends AudibleEvent {
|
|||||||
this.midiConnection = app.api.MidiConnection;
|
this.midiConnection = app.api.MidiConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chord = (value: number[]): this => {
|
||||||
|
this.values["chord"] = value;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
note = (value: number): this => {
|
note = (value: number): this => {
|
||||||
this.values["note"] = value;
|
this.values["note"] = value;
|
||||||
return this;
|
return this;
|
||||||
@ -74,23 +79,23 @@ export class NoteEvent extends AudibleEvent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
out = (): void => {
|
out = (): void => {
|
||||||
const note = this.values.note ? this.values.note : 60;
|
function play(note: number, event: MidiEvent): void {
|
||||||
const channel = this.values.channel ? this.values.channel : 0;
|
const channel = event.values.channel ? event.values.channel : 0;
|
||||||
const velocity = this.values.velocity ? this.values.velocity : 100;
|
const velocity = event.values.velocity ? event.values.velocity : 100;
|
||||||
|
|
||||||
const sustain = this.values.sustain
|
const sustain = event.values.sustain
|
||||||
? this.values.sustain *
|
? event.values.sustain *
|
||||||
this.app.clock.pulse_duration *
|
event.app.clock.pulse_duration *
|
||||||
this.app.api.ppqn()
|
event.app.api.ppqn()
|
||||||
: this.app.clock.pulse_duration * this.app.api.ppqn();
|
: event.app.clock.pulse_duration * event.app.api.ppqn();
|
||||||
|
|
||||||
const bend = this.values.bend ? this.values.bend : undefined;
|
const bend = event.values.bend ? event.values.bend : undefined;
|
||||||
|
|
||||||
const port = this.values.port
|
const port = event.values.port
|
||||||
? this.midiConnection.getMidiOutputIndex(this.values.port)
|
? event.midiConnection.getMidiOutputIndex(event.values.port)
|
||||||
: this.midiConnection.getCurrentMidiPortIndex();
|
: event.midiConnection.getCurrentMidiPortIndex();
|
||||||
|
|
||||||
this.midiConnection.sendMidiNote(
|
event.midiConnection.sendMidiNote(
|
||||||
note,
|
note,
|
||||||
channel,
|
channel,
|
||||||
velocity,
|
velocity,
|
||||||
@ -98,5 +103,16 @@ export class NoteEvent extends AudibleEvent {
|
|||||||
port,
|
port,
|
||||||
bend
|
bend
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.values.chord) {
|
||||||
|
this.values.chord.forEach((note: number) => {
|
||||||
|
play(note, this);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const note = this.values.note ? this.values.note : 60;
|
||||||
|
play(note, this);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,6 +90,7 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
public sound = (value: string) => this.updateValue("s", value);
|
public sound = (value: string) => this.updateValue("s", value);
|
||||||
|
public chord = (value: number[]) => this.updateValue("chord", value);
|
||||||
public snd = this.sound;
|
public snd = this.sound;
|
||||||
public nudge = (value: number) => this.updateValue("nudge", value);
|
public nudge = (value: number) => this.updateValue("nudge", value);
|
||||||
public cut = (value: number) => this.updateValue("cut", value);
|
public cut = (value: number) => this.updateValue("cut", value);
|
||||||
@ -162,7 +163,15 @@ export class SoundEvent extends AudibleEvent {
|
|||||||
this.values.freq = midiToFreq(note);
|
this.values.freq = midiToFreq(note);
|
||||||
};
|
};
|
||||||
|
|
||||||
out = (): object => {
|
out = (): void => {
|
||||||
return superdough(this.values, 1 / 4, this.values.dur || 0.5);
|
if(this.values.chord) {
|
||||||
|
this.values.chord.forEach((freq: number) => {
|
||||||
|
const copy = {...this.values};
|
||||||
|
copy.freq = freq;
|
||||||
|
superdough(copy, 1 / 4, this.values.dur || 0.5);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
superdough(this.values, 1 / 4, this.values.dur || 0.5);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { Editor } from "../main";
|
|||||||
import { Event } from "./AbstractEvents";
|
import { Event } from "./AbstractEvents";
|
||||||
import { SkipEvent } from "./SkipEvent";
|
import { SkipEvent } from "./SkipEvent";
|
||||||
import { SoundEvent } from "./SoundEvent";
|
import { SoundEvent } from "./SoundEvent";
|
||||||
import { NoteEvent } from "./MidiEvent";
|
import { MidiEvent } from "./MidiEvent";
|
||||||
import { RestEvent } from "./RestEvent";
|
import { RestEvent } from "./RestEvent";
|
||||||
|
|
||||||
export type InputOptions = { [key: string]: string | number };
|
export type InputOptions = { [key: string]: string | number };
|
||||||
@ -144,6 +144,9 @@ export class Player extends Event {
|
|||||||
"parsedScale"
|
"parsedScale"
|
||||||
);
|
);
|
||||||
return new SoundEvent(obj, this.app).sound(name);
|
return new SoundEvent(obj, this.app).sound(name);
|
||||||
|
} else if(event instanceof Chord) {
|
||||||
|
const pitches = event.freqs();
|
||||||
|
return new SoundEvent(event, this.app).chord(pitches).sound(name);
|
||||||
} else if (event instanceof ZRest) {
|
} else if (event instanceof ZRest) {
|
||||||
return RestEvent.createRestProxy(event.duration, this.app);
|
return RestEvent.createRestProxy(event.duration, this.app);
|
||||||
}
|
}
|
||||||
@ -155,7 +158,6 @@ export class Player extends Event {
|
|||||||
midi(value: number | undefined = undefined) {
|
midi(value: number | undefined = undefined) {
|
||||||
if (this.areWeThereYet()) {
|
if (this.areWeThereYet()) {
|
||||||
const event = this.next() as Pitch | Chord | ZRest;
|
const event = this.next() as Pitch | Chord | ZRest;
|
||||||
if (event instanceof Pitch) {
|
|
||||||
const obj = event.getExisting(
|
const obj = event.getExisting(
|
||||||
"note",
|
"note",
|
||||||
"pitch",
|
"pitch",
|
||||||
@ -165,10 +167,14 @@ export class Player extends Event {
|
|||||||
"octave",
|
"octave",
|
||||||
"parsedScale"
|
"parsedScale"
|
||||||
);
|
);
|
||||||
const note = new NoteEvent(obj, this.app);
|
if (event instanceof Pitch) {
|
||||||
|
const note = new MidiEvent(obj, this.app);
|
||||||
return value ? note.note(value) : note;
|
return value ? note.note(value) : note;
|
||||||
} else if (event instanceof ZRest) {
|
} else if (event instanceof ZRest) {
|
||||||
return RestEvent.createRestProxy(event.duration, this.app);
|
return RestEvent.createRestProxy(event.duration, this.app);
|
||||||
|
} else if (event instanceof Chord) {
|
||||||
|
const pitches = event.notes();
|
||||||
|
return new MidiEvent(obj, this.app).chord(pitches);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return SkipEvent.createSkipProxy();
|
return SkipEvent.createSkipProxy();
|
||||||
|
|||||||
@ -101,6 +101,41 @@ z1('w [0 [5 [3 7]]] h [0 4]')
|
|||||||
false
|
false
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
## Chords
|
||||||
|
|
||||||
|
Chords can be build by grouping pitches or using roman numeral notation, or by using named chords.
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Chords from pitches",
|
||||||
|
`
|
||||||
|
z1('q 024 468').sound('sine').scale("minor").out()
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Chords from roman numerals",
|
||||||
|
`
|
||||||
|
z1('i i v vii vi iv iv v').sound("pad").out();
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Named chords with repeats",
|
||||||
|
`
|
||||||
|
z1('e C9:4 Emin:4 F7:4 Emaj:4')
|
||||||
|
.sound("stab").sustain(2.0).out()
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Transposing chords",
|
||||||
|
`
|
||||||
|
z1('q Fmaj Amin Dmin Cmaj Cdim')
|
||||||
|
.key(["F3","E3","D3","E3"].div(3))
|
||||||
|
.sound('sawtooth').out()
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
|
||||||
## Algorithmic operations
|
## Algorithmic operations
|
||||||
|
|
||||||
Ziffers provides shorthands for **many** numeric and algorithimic operations such as evaluating random numbers and creating sequences using list operations:
|
Ziffers provides shorthands for **many** numeric and algorithimic operations such as evaluating random numbers and creating sequences using list operations:
|
||||||
|
|||||||
@ -63,7 +63,7 @@ export const toposDarkTheme = EditorView.theme(
|
|||||||
},
|
},
|
||||||
".cm-activeLine": {
|
".cm-activeLine": {
|
||||||
// backgroundColor: highlightBackground
|
// backgroundColor: highlightBackground
|
||||||
backgroundColor: "rgb(76,86,106, 0.4)",
|
backgroundColor: "rgb(76,76,106, 0.1)",
|
||||||
},
|
},
|
||||||
".cm-selectionMatch": {
|
".cm-selectionMatch": {
|
||||||
backgroundColor: base04,
|
backgroundColor: base04,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
"target": "ES2020",
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|||||||
@ -1451,10 +1451,10 @@ yaml@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
|
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
|
||||||
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
|
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
|
||||||
|
|
||||||
zifferjs@^0.0.19:
|
zifferjs@^0.0.21:
|
||||||
version "0.0.19"
|
version "0.0.21"
|
||||||
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.19.tgz#a3e1fc04b12cef1f59a78f3d23229b072d9f0a60"
|
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.21.tgz#5df0c6541ae189b2515b4eb4856eb9d2c0610778"
|
||||||
integrity sha512-mdrfac/ryDF1MEC1iMCV9KTj36AXNVsiDoYiDEA5ZCYFuHqfzS5s2aCh2iK6teyRKTIagE2IvZCrsTacB5GBBg==
|
integrity sha512-wJat1nbeCJ1j7+5YpeB5MnZdbyFfAwVRB8Ei+cgViEtjidwe4oCLtNq1p8Gq1PjjpSzr32YR57egCdME2Lb5qg==
|
||||||
|
|
||||||
zzfx@^1.2.0:
|
zzfx@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user