Renamed root level note() to midi(). midi() & sound() now accepts object as parameter

This commit is contained in:
2023-08-24 10:50:17 +03:00
parent 8da1aa7ac2
commit a421a28844
8 changed files with 66 additions and 69 deletions

View File

@ -4,8 +4,8 @@ import { tryEvaluate } from "./Evaluator";
import { DrunkWalk } from "./Utils/Drunk"; import { DrunkWalk } from "./Utils/Drunk";
import { scale } from "./Scales"; import { scale } from "./Scales";
import { Editor } from "./main"; import { Editor } from "./main";
import { Sound } from "./classes/Sound"; import { SoundEvent } from "./classes/SoundEvent";
import { Note } from "./classes/Note"; import { NoteEvent } from "./classes/MidiEvent";
import { LRUCache } from "lru-cache"; import { LRUCache } from "lru-cache";
import { Player } from "./classes/ZPlayer"; import { Player } from "./classes/ZPlayer";
import { import {
@ -236,7 +236,7 @@ export class UserAPI {
} }
}; };
public note = (value: number = 60): Note => { public midi = (value: number|object = 60): NoteEvent => {
/** /**
* Sends a MIDI note to the current MIDI output. * Sends a MIDI note to the current MIDI output.
* *
@ -244,7 +244,7 @@ export class UserAPI {
* @param options - an object containing options for that note * @param options - an object containing options for that note
* { channel: 0, velocity: 100, duration: 0.5 } * { channel: 0, velocity: 100, duration: 0.5 }
*/ */
return new Note(value, this.app); return new NoteEvent(value, this.app);
}; };
public sysex = (data: Array<number>): void => { public sysex = (data: Array<number>): void => {
@ -1234,9 +1234,10 @@ export class UserAPI {
// Trivial functions // Trivial functions
// ============================================================= // =============================================================
sound = (sound: string) => { sound = (sound: string|object) => {
return new Sound(sound, this.app); return new SoundEvent(sound, this.app);
}; };
snd = this.sound; snd = this.sound;
samples = samples; samples = samples;
soundMap = soundMap; soundMap = soundMap;

View File

@ -35,11 +35,11 @@ Press ${key_shortcut(
<pre><code class="language-javascript"> <pre><code class="language-javascript">
bpm(80) bpm(80)
mod(0.25) :: sound('sawtooth') mod(0.25) :: sound('sawtooth')
.note(seqbar( .midi(seqbar(
pick(60, 67, 63) - 12, pick(60, 67, 63) - 12, pick(60, 67, 63) - 12, pick(60, 67, 63) - 12,
pick(60, 67, 63) - 12 + 5, pick(60, 67, 63) - 12 + 5, pick(60, 67, 63) - 12 + 5, pick(60, 67, 63) - 12 + 5,
pick(60, 67, 63) - 12 + 7, pick(60, 67, 63) - 12 + 7) + (sometimes() ? 24 : 12)) pick(60, 67, 63) - 12 + 7, pick(60, 67, 63) - 12 + 7) + (sometimes() ? 24 : 12))
.dur(0.1).fmi(8).fmh(4).room(0.9) .sustain(0.1).fmi(8).fmh(4).room(0.9)
.gain(0.75).cutoff(500 + usine(8) * 10000) .gain(0.75).cutoff(500 + usine(8) * 10000)
.delay(0.5).delaytime(bpm() / 60 / 4 / 3) .delay(0.5).delaytime(bpm() / 60 / 4 / 3)
.delayfeedback(0.25) .delayfeedback(0.25)
@ -192,27 +192,25 @@ const midi: string = `
You can use Topos to play MIDI thanks to the [WebMIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API). You can currently send notes, control change, program change and so on. You can also send a MIDI Clock to your MIDI devices or favorite DAW. Note that Topos is also capable of playing MIDI using **Ziffers** which provides a better syntax for melodic expression. You can use Topos to play MIDI thanks to the [WebMIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API). You can currently send notes, control change, program change and so on. You can also send a MIDI Clock to your MIDI devices or favorite DAW. Note that Topos is also capable of playing MIDI using **Ziffers** which provides a better syntax for melodic expression.
## Notes ## Notes
- <icode>note(note: number, options: {})</icode>: send a MIDI Note. This function can take an object as a second argument to specify the MIDI channel, velocity, etc... (_e.g._ <icode>note(60, {channel: 1, velocity: 127})</icode>). - <icode>midi(note: number|object)</icode>: send a MIDI Note. Object can take parameters {note: number, channel: number, port: number|string, velocity: number}.
\`\`\`javascript \`\`\`javascript
bpm(80) // Setting a default BPM bpm(80) // Setting a default BPM
mod(.5) && note(36 + seqbeat(0,12)).duration(0.02).out() mod(.5) && midi(36 + seqbeat(0,12)).sustain(0.02).out()
mod(.25) && note(pick(64, 76)).duration(0.05).out() mod(.25) && midi(pick(64, 76)).sustain(0.05).out()
mod(.75) && note(seqbeat(64, 67, 69)).duration(0.05).out() mod(.75) && midi(seqbeat(64, 67, 69)).sustain(0.05).out()
sometimes() && mod(.25) && note(seqbeat(64, 67, 69) + 24).duration(0.05).out() sometimes() && mod(.25) && midi(seqbeat(64, 67, 69) + 24).sustain(0.05).out()
\`\`\` \`\`\`
## Note chaining ## Note chaining
The <icode>note(number)</icode> function can be chained to _specify_ a midi note more. For instance, you can add a duration, a velocity, a channel, etc...: The <icode>midi(number|object)</icode> function can be chained to _specify_ a midi note more. For instance, you can add a duration, a velocity, a channel, etc... by chaining:
\`\`\`javascript \`\`\`javascript
mod(0.25) && note(60) mod(0.25) && midi(60)
.sometimes(n=>n.note(irand(40,60))) .sometimes(n=>n.note(irand(40,60)))
.duration(0.05) .sustain(0.05)
.channel(2) .channel(2)
.port("bespoke") .port("bespoke")
.out() .out()

View File

@ -75,7 +75,7 @@ export abstract class Event {
} }
export abstract class SoundEvent extends Event { export abstract class AudibleEvent extends Event {
constructor(app: Editor) { constructor(app: Editor) {
super(app); super(app);
} }
@ -120,32 +120,3 @@ export abstract class SoundEvent extends Event {
} }
} }
export class Skip {
_fallbackMethod = (): Skip => {
return this;
}
public static createRestProxy = () => {
const instance = new Skip();
return new Proxy(instance, {
// @ts-ignore
get(target, propKey, receiver) {
// @ts-ignore
if (typeof target[propKey] === 'undefined') {
return target._fallbackMethod;
}
// @ts-ignore
return target[propKey];
},
// @ts-ignore
set(target, propKey, value, receiver) {
return false;
}
});
}
out = (): void => {}
}

View File

@ -1,9 +1,9 @@
import { SoundEvent } from './Event'; import { AudibleEvent } from './AbstractEvents';
import { type Editor } from '../main'; 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 class Note extends SoundEvent { export class NoteEvent extends AudibleEvent {
midiConnection: MidiConnection; midiConnection: MidiConnection;
constructor(input: number|object, public app: Editor) { constructor(input: number|object, public app: Editor) {

View File

@ -1,7 +1,7 @@
import { type Editor } from '../main'; import { type Editor } from '../main';
import { Event } from "./Event"; import { Event } from "./AbstractEvents";
export class Rest extends Event { export class RestEvent extends Event {
constructor(duration: number, app: Editor) { constructor(duration: number, app: Editor) {
super(app); super(app);
this.values["duration"] = duration; this.values["duration"] = duration;
@ -12,7 +12,7 @@ export class Rest extends Event {
} }
public static createRestProxy = (duration: number, app: Editor) => { public static createRestProxy = (duration: number, app: Editor) => {
const instance = new Rest(duration, app); const instance = new RestEvent(duration, app);
return new Proxy(instance, { return new Proxy(instance, {
// @ts-ignore // @ts-ignore
get(target, propKey, receiver) { get(target, propKey, receiver) {

28
src/classes/SkipEvent.ts Normal file
View File

@ -0,0 +1,28 @@
export class SkipEvent {
_fallbackMethod = (): SkipEvent => {
return this;
}
public static createSkipProxy = () => {
const instance = new SkipEvent();
return new Proxy(instance, {
// @ts-ignore
get(target, propKey, receiver) {
// @ts-ignore
if (typeof target[propKey] === 'undefined') {
return target._fallbackMethod;
}
// @ts-ignore
return target[propKey];
},
// @ts-ignore
set(target, propKey, value, receiver) {
return false;
}
});
}
out = (): void => {}
}

View File

@ -1,5 +1,5 @@
import { type Editor } from '../main'; import { type Editor } from '../main';
import { SoundEvent } from './Event'; import { AudibleEvent } from './AbstractEvents';
import { midiToFreq, noteFromPc } from 'zifferjs'; import { midiToFreq, noteFromPc } from 'zifferjs';
import { import {
@ -7,7 +7,7 @@ import {
// @ts-ignore // @ts-ignore
} from "superdough"; } from "superdough";
export class Sound extends SoundEvent { 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

@ -1,9 +1,10 @@
import { Chord, Pitch, Rest as ZRest, Ziffers } from "zifferjs"; import { Chord, Pitch, Rest as ZRest, Ziffers } from "zifferjs";
import { Editor } from "../main"; import { Editor } from "../main";
import { Event, Skip } from "./Event"; import { Event } from "./AbstractEvents";
import { Sound } from "./Sound"; import { SkipEvent } from "./SkipEvent";
import { Note } from "./Note"; import { SoundEvent } from "./SoundEvent";
import { Rest } from "./Rest"; import { NoteEvent } from "./MidiEvent";
import { RestEvent } from "./RestEvent";
export class Player extends Event { export class Player extends Event {
input: string; input: string;
@ -40,30 +41,28 @@ export class Player extends Event {
if(this.areWeThereYet()) { if(this.areWeThereYet()) {
const event = this.next() as Pitch|Chord|ZRest; const event = this.next() as Pitch|Chord|ZRest;
if(event instanceof Pitch) { if(event instanceof Pitch) {
// TODO: Quick hack. Select which attributes to use, but some ziffers stuff is needed for chaining key change etc.
const obj = event.getExisting("freq","pitch","key","scale","octave"); const obj = event.getExisting("freq","pitch","key","scale","octave");
return new Sound(obj, this.app).sound(name); return new SoundEvent(obj, this.app).sound(name);
} else if(event instanceof ZRest) { } else if(event instanceof ZRest) {
return Rest.createRestProxy(event.duration, this.app); return RestEvent.createRestProxy(event.duration, this.app);
} }
} else { } else {
return Skip.createRestProxy(); return SkipEvent.createSkipProxy();
} }
} }
note(value: number|undefined = undefined) { midi(value: number|undefined = undefined) {
if(this.areWeThereYet()) { if(this.areWeThereYet()) {
const event = this.next() as Pitch|Chord|ZRest; const event = this.next() as Pitch|Chord|ZRest;
if(event instanceof Pitch) { if(event instanceof Pitch) {
// TODO: Quick hack. Select which attributes to use, but some ziffers stuff is needed for chaining key change etc.
const obj = event.getExisting("note","pitch","bend","key","scale","octave"); const obj = event.getExisting("note","pitch","bend","key","scale","octave");
const note = new Note(obj, this.app); const note = new NoteEvent(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 Rest.createRestProxy(event.duration, this.app); return RestEvent.createRestProxy(event.duration, this.app);
} }
} else { } else {
return Skip.createRestProxy(); return SkipEvent.createSkipProxy();
} }
} }