diff --git a/package.json b/package.json
index 4d437ed..1af25d4 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
"tone": "^14.8.49",
"unique-names-generator": "^4.7.1",
"vite-plugin-markdown": "^2.1.0",
- "zifferjs": "^0.0.28",
+ "zifferjs": "^0.0.30",
"zzfx": "^1.2.0"
}
}
diff --git a/src/classes/MidiEvent.ts b/src/classes/MidiEvent.ts
index 2321df0..1294d57 100644
--- a/src/classes/MidiEvent.ts
+++ b/src/classes/MidiEvent.ts
@@ -3,6 +3,14 @@ import { type Editor } from "../main";
import { MidiConnection } from "../IO/MidiConnection";
import { midiToFreq, noteFromPc } from "zifferjs";
+export type MidiParams = {
+ note: number;
+ bend?: number;
+ channel?: number;
+ port?: number;
+ sustain?: number;
+}
+
export class MidiEvent extends AudibleEvent {
midiConnection: MidiConnection;
@@ -13,7 +21,7 @@ export class MidiEvent extends AudibleEvent {
this.midiConnection = app.api.MidiConnection;
}
- chord = (value: number[]): this => {
+ chord = (value: MidiParams[]): this => {
this.values["chord"] = value;
return this;
};
@@ -79,9 +87,12 @@ export class MidiEvent extends AudibleEvent {
};
out = (): void => {
- function play(note: number, event: MidiEvent): void {
- const channel = event.values.channel ? event.values.channel : 0;
+ function play(event: MidiEvent, params?: MidiParams): void {
+ 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 paramNote = params && params.note ? params.note : 60;
+ const note = event.values.note ? event.values.note : paramNote;
const sustain = event.values.sustain
? event.values.sustain *
@@ -106,12 +117,11 @@ export class MidiEvent extends AudibleEvent {
}
if(this.values.chord) {
- this.values.chord.forEach((note: number) => {
- play(note, this);
+ this.values.chord.forEach((p: MidiParams) => {
+ play(this, p);
});
} else {
- const note = this.values.note ? this.values.note : 60;
- play(note, this);
+ play(this);
}
};
diff --git a/src/classes/SoundEvent.ts b/src/classes/SoundEvent.ts
index 60ed0d9..91c1dcf 100644
--- a/src/classes/SoundEvent.ts
+++ b/src/classes/SoundEvent.ts
@@ -7,6 +7,11 @@ import {
// @ts-ignore
} from "superdough";
+export type SoundParams = {
+ dur: number;
+ s?: string;
+}
+
export class SoundEvent extends AudibleEvent {
constructor(sound: string | object, public app: Editor) {
super(app);
diff --git a/src/classes/ZPlayer.ts b/src/classes/ZPlayer.ts
index 4c3a862..28f345b 100644
--- a/src/classes/ZPlayer.ts
+++ b/src/classes/ZPlayer.ts
@@ -2,8 +2,8 @@ import { Chord, Pitch, Rest as ZRest, Ziffers } from "zifferjs";
import { Editor } from "../main";
import { Event } from "./AbstractEvents";
import { SkipEvent } from "./SkipEvent";
-import { SoundEvent } from "./SoundEvent";
-import { MidiEvent } from "./MidiEvent";
+import { SoundEvent, SoundParams } from "./SoundEvent";
+import { MidiEvent, MidiParams } from "./MidiEvent";
import { RestEvent } from "./RestEvent";
export type InputOptions = { [key: string]: string | number };
@@ -131,7 +131,7 @@ export class Player extends Event {
return areWeThereYet;
};
- sound(name: string) {
+ sound(name?: string) {
if (this.areWeThereYet()) {
const event = this.next() as Pitch | Chord | ZRest;
@@ -145,8 +145,10 @@ export class Player extends Event {
"octave",
"parsedScale"
);
+ if(event.sound) name = event.sound as string;
+ if(event.soundIndex) obj.n = event.soundIndex;
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) {
const pitches = event.pitches.map((p) => {
return p.getExisting(
@@ -158,7 +160,9 @@ export class Player extends Event {
"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) {
return RestEvent.createRestProxy(event.duration, this.app);
}
@@ -177,15 +181,16 @@ export class Player extends Event {
"key",
"scale",
"octave",
- "parsedScale"
+ "parsedScale",
);
if (event instanceof Pitch) {
+ if(event.soundIndex) obj.channel = event.soundIndex;
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();
+ const pitches = event.midiChord() as MidiParams[];
return new MidiEvent(obj, this.app).chord(pitches);
}
} else {
diff --git a/src/documentation/midi.ts b/src/documentation/midi.ts
index 26eef0d..49a74ed 100644
--- a/src/documentation/midi.ts
+++ b/src/documentation/midi.ts
@@ -32,7 +32,7 @@ midi_outputs()
- midi_output(output_name: string): enter your desired output to connect to it.
${makeExample(
- "Listing MIDI outputs",
+ "Changing MIDI output",
`
midi_output("MIDI Rocket-Trumpet")
`,
@@ -137,5 +137,26 @@ beat(.25) && midi_clock() // Sending clock to MIDI device from the global buffer
`,
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
+)}
+
`;
};
diff --git a/src/documentation/ziffers.ts b/src/documentation/ziffers.ts
index 460d71b..c8e720d 100644
--- a/src/documentation/ziffers.ts
+++ b/src/documentation/ziffers.ts
@@ -29,8 +29,10 @@ The basic Ziffer notation is entirely written in JavaScript strings (_e.g_ "
| **Rest** | r | Rest / silences |
| **Repeat** | :1-9 | Repeat the item 1-9 times |
| **Chords** | [1-9]+ / [iv]+ / [AG]+name | Multiple pitches grouped together, roman numerals or named chords |
+| **Samples** | [a-z0-9_]+ | Samples can be used pitched or unpitched |
+| **Index/Channel** | [a-z0-9]+:[0-9]* | Samples or midi channel can be changed using a colon |
-**Note:** Some features are still unsupported. For full syntax see article about Ziffers.
+**Note:** Some features are experimental and some are still unsupported. For full / prior syntax see article about Ziffers.
${makeExample(
"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
)}
+## Samples
+
+Samples can be patterned using the sample names or using @-operator for assigning sample to a pitch. Sample index can be changed using the : operator.
+
+${makeExample(
+ "Sampled drums",
+ `
+ z1('bd [hh hh]').octave(-2).sound('sine').out()
+ `,
+ true
+)}
+
+${makeExample(
+ "More complex pattern",
+ `
+ z1('bd [hh >]').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
You can also use string prototypes as an alternative syntax for creating Ziffers patterns
diff --git a/yarn.lock b/yarn.lock
index a18dbf2..cbb9307 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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.28:
- version "0.0.28"
- resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.28.tgz#4f4c4578ff6b01c6860320532967a2e0d8394a32"
- integrity sha512-0wCi2+eIeqO6IIXDzjLh6Aes4uEAxm1CoXnKYayfJuUkLIpkq5NOGSNl/toubxmoqi+0HgJsjfdd4johhATMnQ==
+zifferjs@^0.0.30:
+ version "0.0.30"
+ resolved "https://registry.yarnpkg.com/zifferjs/-/zifferjs-0.0.30.tgz#92f5ee8e207e1316a8ffb0ca8ede0e88bca4cf47"
+ integrity sha512-CPRswWxl3hxvmBJDXdWyV1QNFWcWVSyVMMUZw1obZo2UaHQzw2cqINcRoZ5S4BMSYPgZUsJt0Efw1U8/dR9dOg==
zzfx@^1.2.0:
version "1.2.0"