Adding sample patterning to ziffers

This commit is contained in:
2023-09-26 20:02:00 +03:00
parent 2843b65122
commit bf360247c8
7 changed files with 134 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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