Documenting ziffers

This commit is contained in:
2023-08-28 17:19:27 +03:00
parent f770bbb007
commit cb12c4d8cc
6 changed files with 142 additions and 21 deletions

View File

@ -120,6 +120,7 @@
<p rel="noopener noreferrer" id="docs_samples" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Samples</p>
<p rel="noopener noreferrer" id="docs_synths" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Synths</p>
<p rel="noopener noreferrer" id="docs_patterns" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Patterns</p>
<p rel="noopener noreferrer" id="docs_ziffers" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Ziffers</p>
<p rel="noopener noreferrer" id="docs_midi" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">MIDI</p>
<p rel="noopener noreferrer" id="docs_functions" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Functions</p>
<p rel="noopener noreferrer" id="docs_shortcuts" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Shortcuts</p>

View File

@ -1057,6 +1057,127 @@ mod(1)::snd('sine').sustain(0.1).freq([100,100,100,100,200].unique().beat()).out
`;
const ziffers: string = `
# Ziffers
Ziffers is a musical number based notation developed especially for live coding. It is a very powerful and flexibly notation for describing musical patterns in a short string.
## Notation
Basic notation consists of numbers and letters using spaces as separators.
**Pitches:** Single digits 0-9 or multiple digits escaped {10 11 21}
**Durations:** a-z or decimals: 0.25 = q (quarter), 0.5 = h (half), 1 = w (whole), etc.
**Octave:** ^ _ (up and down)
**Accidentals:** # b
**Rest:** r
NOTE! Some of the features are still unsupported. For full syntax see article on <a href="https://zenodo.org/record/7841945" target="_blank">Ziffers</a>.
## Keys and scales
Ziffers supports all the keys and scales. Keys can be defined by using <a href="https://en.wikipedia.org/wiki/Scientific_pitch_notation" target="_blank">scientific pitch notation</a>, for example 'F3'. Western style (1490 scales) can be used with the scale named named after greek modes and extended by William Zeitler (see full <a href="https://ianring.com/musictheory/scales/traditions/zeitler" target="_blank">list</a>):
* Lydian
* Mixolydian
* Aeolian
* Locrian
* Ionian
* Dorian
* Phrygian
* Soryllic
* Modimic
* Ionalian
* ...
or by most traditional <a href="https://ianring.com/musictheory/scales/traditions/western" target="_blank">western names</a>:
* Major
* Minor
* Minor pentatonic
* Major pentatonic
* Harmonic minor
* Harmonic major
* Melodic minor
* Melodic major
* Whole
* Blues minor
* Blues major
* ...
Microtonal scales can be defined using <a href="https://www.huygens-fokker.org/scala/scl_format.html" target="_blank">Scala format</a> or by extended notation defined by Sevish <a href="https://sevish.com/scaleworkshop/" target="_blank">Scale workshop</a>, for example:
**Young:** 106. 198. 306.2 400.1 502. 604. 697.9 806.1 898.1 1004.1 1102. 1200.
**Wendy carlos:** 17/16 9/8 6/5 5/4 4/3 11/8 3/2 13/8 5/3 7/4 15/8 2/1
## Methods
Ziffers numbered methods **(z0-z16)** can be used to parse and play patterns. Each method is individually cached and can be used to play patterns simultaniously.
## Chaining and options
Ziffers patterns can be chained to <icode>sound()</icode> and <icode>midi()</icode> to produce different outputs. Chaining is often alternative for passing in options, which can be more efficient. Methods available for chaining are:
* key() - for changing key
* scale() - for chaning scale
* octave() - for changing octave
* sound() - for outputting pattern as sounds (See Sound)
* midi() - for outputting pattern as midi (See Midi)
## Examples
- Basic notation
${makeExample(
"Simple method chaining",
`
z1('0 1 2 3').key('G3').scale('minor').sound('sine').out()
`,
true
)}
${makeExample(
"More complex chaining",
`
z1('0 1 2 3 4').key('G3').scale('minor').sound('sine').often(n => n.pitch+=3).rarely(s => s.delay(0.5)).out()
`,
true
)}
${makeExample(
"Simple options",
`
z1('0 3 2 4',{key: 'D3', scale: 'minor pentatonic'}).sound('sine').out()
`,
true
)}
${makeExample(
"Duration chars",
`
z1('s 0 e 1 q 2 h 3 w 4').sound('sine').scale("locrian").out()
`,
true
)}
${makeExample(
"Decimal durations",
`
z1('0.25 5 1 2 6 0.125 3 8 0.5 4 1.0 0').sound('sine').scale("ionian").out()
`,
true
)}
${makeExample(
"Rest and octaves",
`
z1('q 0 ^ e0 r _ 0 _ r 4 ^4 4').sound('sine').scale("ionian").out()
`,
true
)}
`;
const synths: string = `
# Synthesizers
@ -1418,6 +1539,7 @@ Topos is made to be controlled entirely with a keyboard. It is recommanded to st
samples: samples,
synths: synths,
patterns: patterns,
ziffers: ziffers,
midi: midi,
functions: functions,
reference: reference,

View File

@ -42,10 +42,10 @@ export class NoteEvent extends AudibleEvent {
const funcResult = func(this);
if(funcResult instanceof Object) {
return funcResult;
}
else {
func(this.values);
this.update();
return this;
}
}
@ -63,12 +63,15 @@ export class NoteEvent extends AudibleEvent {
}
update = (): void => {
if(this.values.key && this.values.pitch && this.values.parsedScale && this.values.octave) {
const [note,bend] = noteFromPc(this.values.key, this.values.pitch, this.values.parsedScale, this.values.octave);
this.values.note = note;
this.values.freq = midiToFreq(note);
if(bend) this.values.bend = bend;
}
const [note, bend] = noteFromPc(
this.values.key || "C4",
this.values.pitch || 0,
this.values.parsedScale || "MAJOR",
this.values.octave || 0
);
this.values.note = note;
this.values.freq = midiToFreq(note);
if(bend) this.values.bend = bend;
}
out = (): void => {

View File

@ -225,6 +225,7 @@ export class SoundEvent extends AudibleEvent {
if (funcResult instanceof Object) return funcResult;
else {
func(this.values);
this.update();
return this;
}
};
@ -237,20 +238,13 @@ export class SoundEvent extends AudibleEvent {
sus = this.sustain;
update = (): void => {
if (
this.values.key &&
this.values.pitch &&
this.values.parsedScale &&
this.values.octave
) {
const [note, _] = noteFromPc(
this.values.key,
this.values.pitch,
this.values.parsedScale,
this.values.octave
this.values.key || "C4",
this.values.pitch || 0,
this.values.parsedScale || "MAJOR",
this.values.octave || 0
);
this.values.freq = midiToFreq(note);
}
};
out = (): object => {

View File

@ -124,7 +124,7 @@ export class Player extends Event {
if(this.areWeThereYet()) {
const event = this.next() as Pitch|Chord|ZRest;
if(event instanceof Pitch) {
const obj = event.getExisting("freq","pitch","key","scale","octave");
const obj = event.getExisting("freq","pitch","key","scale","octave","parsedScale");
return new SoundEvent(obj, this.app).sound(name);
} else if(event instanceof ZRest) {
return RestEvent.createRestProxy(event.duration, this.app);
@ -138,7 +138,7 @@ export class Player extends Event {
if(this.areWeThereYet()) {
const event = this.next() as Pitch|Chord|ZRest;
if(event instanceof Pitch) {
const obj = event.getExisting("note","pitch","bend","key","scale","octave");
const obj = event.getExisting("note","pitch","bend","key","scale","octave","parsedScale");
const note = new NoteEvent(obj, this.app);
return value ? note.note(value) : note;
} else if(event instanceof ZRest) {

View File

@ -565,6 +565,7 @@ export class Editor {
"samples",
"synths",
"patterns",
"ziffers",
"midi",
"functions",
"reference",
@ -989,5 +990,5 @@ window.addEventListener("beforeunload", () => {
// app._mouseY = event.clientY;
// }
onmousemove = function(e){console.log("mouse location:", e.clientX, e.clientY)}
// onmousemove = function(e){console.log("mouse location:", e.clientX, e.clientY)}