Added chords to ziffers

This commit is contained in:
2023-09-08 00:42:39 +03:00
parent 7993d87915
commit d2161eb5bc
10 changed files with 111 additions and 46 deletions

View File

@ -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"
}
}

View File

@ -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 => {

View File

@ -8,7 +8,7 @@ export class TransportNode extends AudioWorkletNode {
this.app = application
this.port.addEventListener("message", this.handleMessage);
this.port.start();
this.timeviewer = document.getElementById("timeviewer");
this.timeviewer = document.getElementById("timeviewer");
}
/** @type {(this: MessagePort, ev: MessageEvent<any>) => any} */
@ -18,7 +18,7 @@ export class TransportNode extends AudioWorkletNode {
this.app.clock.tick++
const futureTimeStamp = this.app.clock.convertTicksToTimeposition(this.app.clock.tick);
this.app.clock.time_position = futureTimeStamp;
this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${futureTimeStamp.beat+1}:${zeroPad(futureTimeStamp.pulse, 2)}`;
this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${futureTimeStamp.beat+1}:${zeroPad(futureTimeStamp.pulse, 2)}`;
if (this.app.exampleIsPlaying) {
tryEvaluate(this.app, this.app.example_buffer);

View File

@ -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,29 +79,40 @@ 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(
note,
channel,
velocity,
sustain,
port,
bend
);
event.midiConnection.sendMidiNote(
note,
channel,
velocity,
sustain,
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);
}
};
}

View File

@ -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);
}
};
}

View File

@ -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,20 +158,23 @@ export class Player extends Event {
midi(value: number | undefined = undefined) {
if (this.areWeThereYet()) {
const event = this.next() as Pitch | Chord | ZRest;
const obj = event.getExisting(
"note",
"pitch",
"bend",
"key",
"scale",
"octave",
"parsedScale"
);
if (event instanceof Pitch) {
const obj = event.getExisting(
"note",
"pitch",
"bend",
"key",
"scale",
"octave",
"parsedScale"
);
const note = new NoteEvent(obj, this.app);
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();

View File

@ -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:

View File

@ -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,

View File

@ -1,7 +1,6 @@
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,

View File

@ -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"