Adding sample patterning 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.28",
|
"zifferjs": "^0.0.30",
|
||||||
"zzfx": "^1.2.0"
|
"zzfx": "^1.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,14 @@ 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 type MidiParams = {
|
||||||
|
note: number;
|
||||||
|
bend?: number;
|
||||||
|
channel?: number;
|
||||||
|
port?: number;
|
||||||
|
sustain?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export class MidiEvent extends AudibleEvent {
|
export class MidiEvent extends AudibleEvent {
|
||||||
midiConnection: MidiConnection;
|
midiConnection: MidiConnection;
|
||||||
|
|
||||||
@ -13,7 +21,7 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
this.midiConnection = app.api.MidiConnection;
|
this.midiConnection = app.api.MidiConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
chord = (value: number[]): this => {
|
chord = (value: MidiParams[]): this => {
|
||||||
this.values["chord"] = value;
|
this.values["chord"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -79,9 +87,12 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
out = (): void => {
|
out = (): void => {
|
||||||
function play(note: number, event: MidiEvent): void {
|
function play(event: MidiEvent, params?: MidiParams): void {
|
||||||
const channel = event.values.channel ? event.values.channel : 0;
|
const paramChannel = params && params.channel ? params.channel : 0;
|
||||||
|
const channel = event.values.channel ? event.values.channel : paramChannel;
|
||||||
const velocity = event.values.velocity ? event.values.velocity : 100;
|
const velocity = event.values.velocity ? event.values.velocity : 100;
|
||||||
|
const paramNote = params && params.note ? params.note : 60;
|
||||||
|
const note = event.values.note ? event.values.note : paramNote;
|
||||||
|
|
||||||
const sustain = event.values.sustain
|
const sustain = event.values.sustain
|
||||||
? event.values.sustain *
|
? event.values.sustain *
|
||||||
@ -106,12 +117,11 @@ export class MidiEvent extends AudibleEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(this.values.chord) {
|
if(this.values.chord) {
|
||||||
this.values.chord.forEach((note: number) => {
|
this.values.chord.forEach((p: MidiParams) => {
|
||||||
play(note, this);
|
play(this, p);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const note = this.values.note ? this.values.note : 60;
|
play(this);
|
||||||
play(note, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,6 +7,11 @@ import {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} from "superdough";
|
} from "superdough";
|
||||||
|
|
||||||
|
export type SoundParams = {
|
||||||
|
dur: number;
|
||||||
|
s?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class SoundEvent extends AudibleEvent {
|
export class SoundEvent extends AudibleEvent {
|
||||||
constructor(sound: string | object, public app: Editor) {
|
constructor(sound: string | object, public app: Editor) {
|
||||||
super(app);
|
super(app);
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import { Chord, Pitch, Rest as ZRest, Ziffers } from "zifferjs";
|
|||||||
import { Editor } from "../main";
|
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, SoundParams } from "./SoundEvent";
|
||||||
import { MidiEvent } from "./MidiEvent";
|
import { MidiEvent, MidiParams } from "./MidiEvent";
|
||||||
import { RestEvent } from "./RestEvent";
|
import { RestEvent } from "./RestEvent";
|
||||||
|
|
||||||
export type InputOptions = { [key: string]: string | number };
|
export type InputOptions = { [key: string]: string | number };
|
||||||
@ -131,7 +131,7 @@ export class Player extends Event {
|
|||||||
return areWeThereYet;
|
return areWeThereYet;
|
||||||
};
|
};
|
||||||
|
|
||||||
sound(name: string) {
|
sound(name?: string) {
|
||||||
|
|
||||||
if (this.areWeThereYet()) {
|
if (this.areWeThereYet()) {
|
||||||
const event = this.next() as Pitch | Chord | ZRest;
|
const event = this.next() as Pitch | Chord | ZRest;
|
||||||
@ -145,8 +145,10 @@ export class Player extends Event {
|
|||||||
"octave",
|
"octave",
|
||||||
"parsedScale"
|
"parsedScale"
|
||||||
);
|
);
|
||||||
|
if(event.sound) name = event.sound as string;
|
||||||
|
if(event.soundIndex) obj.n = event.soundIndex;
|
||||||
obj.dur = noteLengthInSeconds;
|
obj.dur = noteLengthInSeconds;
|
||||||
return new SoundEvent(obj, this.app).sound(name);
|
return new SoundEvent(obj, this.app).sound(name || "sine");
|
||||||
} else if (event instanceof Chord) {
|
} else if (event instanceof Chord) {
|
||||||
const pitches = event.pitches.map((p) => {
|
const pitches = event.pitches.map((p) => {
|
||||||
return p.getExisting(
|
return p.getExisting(
|
||||||
@ -158,7 +160,9 @@ export class Player extends Event {
|
|||||||
"parsedScale"
|
"parsedScale"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
return new SoundEvent({dur: noteLengthInSeconds}, this.app).chord(pitches).sound(name);
|
const sound: SoundParams = {dur: noteLengthInSeconds};
|
||||||
|
if(name) sound.s = name;
|
||||||
|
return new SoundEvent(sound, this.app).chord(pitches);
|
||||||
} else if (event instanceof ZRest) {
|
} else if (event instanceof ZRest) {
|
||||||
return RestEvent.createRestProxy(event.duration, this.app);
|
return RestEvent.createRestProxy(event.duration, this.app);
|
||||||
}
|
}
|
||||||
@ -177,15 +181,16 @@ export class Player extends Event {
|
|||||||
"key",
|
"key",
|
||||||
"scale",
|
"scale",
|
||||||
"octave",
|
"octave",
|
||||||
"parsedScale"
|
"parsedScale",
|
||||||
);
|
);
|
||||||
if (event instanceof Pitch) {
|
if (event instanceof Pitch) {
|
||||||
|
if(event.soundIndex) obj.channel = event.soundIndex;
|
||||||
const note = new MidiEvent(obj, this.app);
|
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) {
|
} else if (event instanceof Chord) {
|
||||||
const pitches = event.notes();
|
const pitches = event.midiChord() as MidiParams[];
|
||||||
return new MidiEvent(obj, this.app).chord(pitches);
|
return new MidiEvent(obj, this.app).chord(pitches);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -32,7 +32,7 @@ midi_outputs()
|
|||||||
- <ic>midi_output(output_name: string)</ic>: enter your desired output to connect to it.
|
- <ic>midi_output(output_name: string)</ic>: enter your desired output to connect to it.
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Listing MIDI outputs",
|
"Changing MIDI output",
|
||||||
`
|
`
|
||||||
midi_output("MIDI Rocket-Trumpet")
|
midi_output("MIDI Rocket-Trumpet")
|
||||||
`,
|
`,
|
||||||
@ -137,5 +137,26 @@ beat(.25) && midi_clock() // Sending clock to MIDI device from the global buffer
|
|||||||
`,
|
`,
|
||||||
true
|
true
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
## Using midi with ziffers
|
||||||
|
|
||||||
|
Ziffers offers some shorthands for defining channels within the patterns. See Ziffers for more information.
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Using midi with ziffers",
|
||||||
|
`
|
||||||
|
z1('0 2 e 5 2 q 4 2').midi().port(2).channel(4).out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Setting the channel within the pattern",
|
||||||
|
`
|
||||||
|
z1('(0 2 e 5 2):0 (4 2):1').midi().out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -29,8 +29,10 @@ The basic Ziffer notation is entirely written in JavaScript strings (_e.g_ <ic>"
|
|||||||
| **Rest** | <ic>r</ic> | Rest / silences |
|
| **Rest** | <ic>r</ic> | Rest / silences |
|
||||||
| **Repeat** | <ic>:1-9</ic> | Repeat the item 1-9 times |
|
| **Repeat** | <ic>:1-9</ic> | Repeat the item 1-9 times |
|
||||||
| **Chords** | <ic>[1-9]+ / [iv]+ / [AG]+name</ic> | Multiple pitches grouped together, roman numerals or named chords |
|
| **Chords** | <ic>[1-9]+ / [iv]+ / [AG]+name</ic> | Multiple pitches grouped together, roman numerals or named chords |
|
||||||
|
| **Samples** | <ic>[a-z0-9_]+</ic> | Samples can be used pitched or unpitched |
|
||||||
|
| **Index/Channel** | <ic>[a-z0-9]+:[0-9]*</ic> | Samples or midi channel can be changed using a colon |
|
||||||
|
|
||||||
**Note:** Some features are still unsupported. For full syntax see article about <a href="https://zenodo.org/record/7841945" target="_blank">Ziffers</a>.
|
**Note:** Some features are experimental and some are still unsupported. For full / prior syntax see article about <a href="https://zenodo.org/record/7841945" target="_blank">Ziffers</a>.
|
||||||
|
|
||||||
${makeExample(
|
${makeExample(
|
||||||
"Pitches from 0 to 9",
|
"Pitches from 0 to 9",
|
||||||
@ -409,6 +411,76 @@ z1('q (0 3 1 5)+(2 5) e (0 5 2)*(2 3) (0 5 2)>>(2 3) (0 5 2)%(2 3)').sound('sine
|
|||||||
true
|
true
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
## Samples
|
||||||
|
|
||||||
|
Samples can be patterned using the sample names or using <c>@</c>-operator for assigning sample to a pitch. Sample index can be changed using the <c>:</c> operator.
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Sampled drums",
|
||||||
|
`
|
||||||
|
z1('bd [hh hh]').octave(-2).sound('sine').out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"More complex pattern",
|
||||||
|
`
|
||||||
|
z1('bd [hh <hh <cp cp:2>>]').octave(-2).sound('sine').out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Pitched samples",
|
||||||
|
`
|
||||||
|
z1('0@sax 3@sax 2@sax 6@sax')
|
||||||
|
.octave(-1).sound()
|
||||||
|
.adsr(0.25,0.125,0.125,0.25).out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Pitched samples from list operation",
|
||||||
|
`
|
||||||
|
z1('e (0 3 -1 4)+(-1 0 2 1)@sine')
|
||||||
|
.key('G4')
|
||||||
|
.scale('110 220 320 450')
|
||||||
|
.sound().out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Pitched samples with list notation",
|
||||||
|
`
|
||||||
|
z1('e (0 2 6 3 5 -2)@sax (0 2 6 3 5 -2)@arp')
|
||||||
|
.octave(-1).sound()
|
||||||
|
.adsr(0.25,0.125,0.125,0.25).out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Sample indices",
|
||||||
|
`
|
||||||
|
z1('e 1:2 4:3 6:2')
|
||||||
|
.octave(-1).sound("east").out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
${makeExample(
|
||||||
|
"Pitched samples with sample indices",
|
||||||
|
`
|
||||||
|
z1('_e 1@east:2 4@bd:3 6@arp:2 9@baa').sound().out()
|
||||||
|
`,
|
||||||
|
true
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## String prototypes
|
## String prototypes
|
||||||
|
|
||||||
You can also use string prototypes as an alternative syntax for creating Ziffers patterns
|
You can also use string prototypes as an alternative syntax for creating Ziffers patterns
|
||||||
|
|||||||
@ -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.28:
|
zifferjs@^0.0.30:
|
||||||
version "0.0.28"
|
version "0.0.30"
|
||||||
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.28.tgz#4f4c4578ff6b01c6860320532967a2e0d8394a32"
|
resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.30.tgz#92f5ee8e207e1316a8ffb0ca8ede0e88bca4cf47"
|
||||||
integrity sha512-0wCi2+eIeqO6IIXDzjLh6Aes4uEAxm1CoXnKYayfJuUkLIpkq5NOGSNl/toubxmoqi+0HgJsjfdd4johhATMnQ==
|
integrity sha512-CPRswWxl3hxvmBJDXdWyV1QNFWcWVSyVMMUZw1obZo2UaHQzw2cqINcRoZ5S4BMSYPgZUsJt0Efw1U8/dR9dOg==
|
||||||
|
|
||||||
zzfx@^1.2.0:
|
zzfx@^1.2.0:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user