Added chords to ziffers
This commit is contained in:
@ -39,7 +39,7 @@
|
||||
"tone": "^14.8.49",
|
||||
"unique-names-generator": "^4.7.1",
|
||||
"vite-plugin-markdown": "^2.1.0",
|
||||
"zifferjs": "^0.0.19",
|
||||
"zifferjs": "^0.0.21",
|
||||
"zzfx": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import { DrunkWalk } from "./Utils/Drunk";
|
||||
import { scale } from "./Scales";
|
||||
import { Editor } from "./main";
|
||||
import { SoundEvent } from "./classes/SoundEvent";
|
||||
import { NoteEvent } from "./classes/MidiEvent";
|
||||
import { MidiEvent } from "./classes/MidiEvent";
|
||||
import { LRUCache } from "lru-cache";
|
||||
import { InputOptions, Player } from "./classes/ZPlayer";
|
||||
import {
|
||||
@ -316,7 +316,7 @@ export class UserAPI {
|
||||
value: number | object = 60,
|
||||
velocity?: number,
|
||||
channel?: number
|
||||
): NoteEvent => {
|
||||
): MidiEvent => {
|
||||
/**
|
||||
* Sends a MIDI note to the current MIDI output.
|
||||
*
|
||||
@ -342,7 +342,7 @@ export class UserAPI {
|
||||
value["channel"] = channel;
|
||||
}
|
||||
|
||||
return new NoteEvent(value, this.app);
|
||||
return new MidiEvent(value, this.app);
|
||||
};
|
||||
|
||||
public sysex = (data: Array<number>): void => {
|
||||
|
||||
@ -3,7 +3,7 @@ import { type Editor } from "../main";
|
||||
import { MidiConnection } from "../IO/MidiConnection";
|
||||
import { midiToFreq, noteFromPc } from "zifferjs";
|
||||
|
||||
export class NoteEvent extends AudibleEvent {
|
||||
export class MidiEvent extends AudibleEvent {
|
||||
midiConnection: MidiConnection;
|
||||
|
||||
constructor(input: number | object, public app: Editor) {
|
||||
@ -13,6 +13,11 @@ export class NoteEvent extends AudibleEvent {
|
||||
this.midiConnection = app.api.MidiConnection;
|
||||
}
|
||||
|
||||
chord = (value: number[]): this => {
|
||||
this.values["chord"] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
note = (value: number): this => {
|
||||
this.values["note"] = value;
|
||||
return this;
|
||||
@ -74,23 +79,23 @@ export class NoteEvent extends AudibleEvent {
|
||||
};
|
||||
|
||||
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;
|
||||
function play(note: number, event: MidiEvent): void {
|
||||
const channel = event.values.channel ? event.values.channel : 0;
|
||||
const velocity = event.values.velocity ? event.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 sustain = event.values.sustain
|
||||
? event.values.sustain *
|
||||
event.app.clock.pulse_duration *
|
||||
event.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
|
||||
? this.midiConnection.getMidiOutputIndex(this.values.port)
|
||||
: this.midiConnection.getCurrentMidiPortIndex();
|
||||
const port = event.values.port
|
||||
? event.midiConnection.getMidiOutputIndex(event.values.port)
|
||||
: event.midiConnection.getCurrentMidiPortIndex();
|
||||
|
||||
this.midiConnection.sendMidiNote(
|
||||
event.midiConnection.sendMidiNote(
|
||||
note,
|
||||
channel,
|
||||
velocity,
|
||||
@ -98,5 +103,16 @@ export class NoteEvent extends AudibleEvent {
|
||||
port,
|
||||
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;
|
||||
};
|
||||
public sound = (value: string) => this.updateValue("s", value);
|
||||
public chord = (value: number[]) => this.updateValue("chord", value);
|
||||
public snd = this.sound;
|
||||
public nudge = (value: number) => this.updateValue("nudge", value);
|
||||
public cut = (value: number) => this.updateValue("cut", value);
|
||||
@ -162,7 +163,15 @@ export class SoundEvent extends AudibleEvent {
|
||||
this.values.freq = midiToFreq(note);
|
||||
};
|
||||
|
||||
out = (): object => {
|
||||
return superdough(this.values, 1 / 4, this.values.dur || 0.5);
|
||||
out = (): void => {
|
||||
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 { SkipEvent } from "./SkipEvent";
|
||||
import { SoundEvent } from "./SoundEvent";
|
||||
import { NoteEvent } from "./MidiEvent";
|
||||
import { MidiEvent } from "./MidiEvent";
|
||||
import { RestEvent } from "./RestEvent";
|
||||
|
||||
export type InputOptions = { [key: string]: string | number };
|
||||
@ -144,6 +144,9 @@ export class Player extends Event {
|
||||
"parsedScale"
|
||||
);
|
||||
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) {
|
||||
return RestEvent.createRestProxy(event.duration, this.app);
|
||||
}
|
||||
@ -155,7 +158,6 @@ export class Player extends Event {
|
||||
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",
|
||||
@ -165,10 +167,14 @@ export class Player extends Event {
|
||||
"octave",
|
||||
"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;
|
||||
} else if (event instanceof ZRest) {
|
||||
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 {
|
||||
return SkipEvent.createSkipProxy();
|
||||
|
||||
@ -101,6 +101,41 @@ z1('w [0 [5 [3 7]]] h [0 4]')
|
||||
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
|
||||
|
||||
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": {
|
||||
// backgroundColor: highlightBackground
|
||||
backgroundColor: "rgb(76,86,106, 0.4)",
|
||||
backgroundColor: "rgb(76,76,106, 0.1)",
|
||||
},
|
||||
".cm-selectionMatch": {
|
||||
backgroundColor: base04,
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
@ -1451,10 +1451,10 @@ yaml@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b"
|
||||
integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==
|
||||
|
||||
zifferjs@^0.0.19:
|
||||
version "0.0.19"
|
||||
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.19.tgz#a3e1fc04b12cef1f59a78f3d23229b072d9f0a60"
|
||||
integrity sha512-mdrfac/ryDF1MEC1iMCV9KTj36AXNVsiDoYiDEA5ZCYFuHqfzS5s2aCh2iK6teyRKTIagE2IvZCrsTacB5GBBg==
|
||||
zifferjs@^0.0.21:
|
||||
version "0.0.21"
|
||||
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.21.tgz#5df0c6541ae189b2515b4eb4856eb9d2c0610778"
|
||||
integrity sha512-wJat1nbeCJ1j7+5YpeB5MnZdbyFfAwVRB8Ei+cgViEtjidwe4oCLtNq1p8Gq1PjjpSzr32YR57egCdME2Lb5qg==
|
||||
|
||||
zzfx@^1.2.0:
|
||||
version "1.2.0"
|
||||
|
||||
Reference in New Issue
Block a user