diff --git a/index.html b/index.html index a01e8d3..805f571 100644 --- a/index.html +++ b/index.html @@ -99,6 +99,8 @@

Time

Sound

Samples

+

Synths

+

MIDI

Functions

Shortcuts

diff --git a/src/API.ts b/src/API.ts index 1f57475..cc27754 100644 --- a/src/API.ts +++ b/src/API.ts @@ -255,6 +255,7 @@ export class UserAPI { */ this.MidiConnection.sendSysExMessage(data); }; + sy = this.sysex; public pitch_bend = (value: number, channel: number): void => { /** @@ -277,6 +278,7 @@ export class UserAPI { */ this.MidiConnection.sendProgramChange(program, channel); }; + pc = this.program_change; public midi_clock = (): void => { /** @@ -298,6 +300,7 @@ export class UserAPI { */ this.MidiConnection.sendMidiControlChange(control, value, channel); }; + cc = this.control_change; public midi_panic = (): void => { /** @@ -1034,7 +1037,7 @@ export class UserAPI { */ return this._euclidean_cycle(pulses, length, rotate)[iterator % length]; }; - ec = this.euclid; + eu = this.euclid; _euclidean_cycle( pulses: number, diff --git a/src/Documentation.ts b/src/Documentation.ts index f3f8a29..bf1d596 100644 --- a/src/Documentation.ts +++ b/src/Documentation.ts @@ -81,6 +81,13 @@ A set of files is called a _universe_. Topos can store several universes and swi Switching between universes will not stop the transport nor reset the clock. You are switching the context but time keeps flowing. This can be useful to prepare immediate transitions between songs and parts. Think of universes as an algorithmic set of music. All scripts in a given universe are aware about how many times they have been runned already. You can reset that value programatically. You can clear the current universe by pressing the flame button on the top right corner of the interface. This will clear all the scripts and the note file. **Note:** there is no shortcut for clearing a universe. We do not want to loose your work by mistake! + +# Sharing your work + +Click on the Topos logo in the top bar. Your URL will change to something much longer and complex. The same URL will be copied to your clipboard. Send this link to your friends to share the universe you are currently working on with them. + +- The imported universe will always get a randomly generated name such as: random_silly_llama. +- Topos will automatically fetch and switch to the universe that was sent to you. Your previous universe is still there, don't worry. `; const time: string = ` @@ -346,6 +353,51 @@ ${injectAvailableSamples()} `; +const synths: string = ` +# Synthesizers + +Topos comes with a small number of basic synthesizers. These synths are based on a basic [WebAudio](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) design. For heavy synthesis duties, please use MIDI and speak to more complex instruments. + +# Substractive Synthesis + +The sound function can take the name of a synthesizer as first argument. +- sine, sawtooth,triangle, square for the waveform selection. +- cutoff and resonance for adding a low-pass filter with cutoff frequency and filter resonance. + - hcutoff or bandf to switch to a high-pass or bandpass filter. + - hresonance and bandq for the resonance parameter of these filters. + +Here is a simple example of a substractive synth: + +\`\`\`javascript +mod(.5) && snd('sawtooth') + .cutoff(pick(2000,500)) + usine(.5) * 4000) + .resonance(0.9).freq(pick(100,150)) + .out() +\`\`\` + + +# Frequency Modulation Synthesis (FM) + +The same basic waveforms can take additional methods to switch to a basic two operators FM synth design (with _carrier_ and _modulator_). FM Synthesis is a complex topic but take this advice: simple ratios will yield stable and harmonic sounds, complex ratios will generate noises, percussions and gritty sounds. + +- fmi (_frequency modulation index_): a floating point value between 1 and n. +- fmh (_frequency modulation harmonic ratio_): a floating point value between 1 and n. + +And here is a simple example: + +\`\`\`javascript +mod(.25) && snd('sine') + .fmi(pick(1,2,4,8)) + .fmh(divseq(2, 1,2,4,8)) + .freq(pick(100,150)) + .sustain(0.1) + .out() +\`\`\` + +**Note::** you can also set the _modulation index_ and the _harmonic ratio_ with the fm argument. You will have to feed both as a string: fm('2:4'). If you only feed one number, only the _modulation index_ will be updated. + +`; + const about: string = ` # About Topos @@ -561,6 +613,7 @@ export const documentation = { time: time, sound: sound, samples: samples, + synths: synths, midi: midi, functions: functions, reference: reference, diff --git a/src/classes/SoundEvent.ts b/src/classes/SoundEvent.ts index ba9346d..40d9e5a 100644 --- a/src/classes/SoundEvent.ts +++ b/src/classes/SoundEvent.ts @@ -9,234 +9,253 @@ import { export class SoundEvent extends AudibleEvent { - constructor(sound: string|object, public app: Editor) { - super(app); - if (typeof sound === 'string') this.values = { 's': sound, 'dur': 0.5 }; - else this.values = sound; + constructor(sound: string | object, public app: Editor) { + super(app); + if (typeof sound === "string") this.values = { s: sound, dur: 0.5 }; + else this.values = sound; + } + + attack = (value: number): this => { + this.values["attack"] = value; + return this; + }; + atk = this.attack; + + decay = (value: number): this => { + this.values["decay"] = value; + return this; + }; + dec = this.decay; + + release = (value: number): this => { + this.values["release"] = value; + return this; + }; + rel = this.release; + + unit = (value: number): this => { + this.values["unit"] = value; + return this; + }; + + freq = (value: number): this => { + this.values["freq"] = value; + return this; + }; + + fm = (value: number | string): this => { + if (typeof value === "number") { + this.values["fmi"] = value; + } else { + let values = value.split(":"); + this.values["fmi"] = parseFloat(values[0]); + if (values.length > 1) this.values["fmh"] = parseFloat(values[1]); } + return this; + }; - attack = (value: number): this => { - this.values["attack"] = value; - return this; - }; - atk = this.attack; + sound = (value: string): this => { + this.values["s"] = value; + return this; + }; - decay = (value: number): this => { - this.values["decay"] = value; - return this; - }; - dec = this.decay; + fmi = (value: number): this => { + this.values["fmi"] = value; + return this; + }; - release = (value: number): this => { - this.values["release"] = value; - return this; - }; - rel = this.release; + fmh = (value: number): this => { + this.values["fmh"] = value; + return this; + }; - unit = (value: number): this => { - this.values["unit"] = value; - return this; - }; + nudge = (value: number): this => { + this.values["nudge"] = value; + return this; + }; - freq = (value: number): this => { - this.values["freq"] = value; - return this; - }; + cut = (value: number): this => { + this.values["cut"] = value; + return this; + }; - fm = (value: number | string): this => { - if (typeof value === "number") { - this.values["fmi"] = value; - } else { - let values = value.split(":"); - this.values["fmi"] = parseFloat(values[0]); - if (values.length > 1) this.values["fmh"] = parseFloat(values[1]); - } - return this; - }; + loop = (value: number): this => { + this.values["loop"] = value; + return this; + }; - sound = (value: string): this => { - this.values['s'] = value - return this; - }; + clip = (value: number): this => { + this.values["clip"] = value; + return this; + }; - fmi = (value: number): this => { - this.values["fmi"] = value; - return this; - }; + n = (value: number): this => { + this.values["n"] = value; + return this; + }; - fmh = (value: number): this => { - this.values["fmh"] = value; - return this; - }; + note = (value: number): this => { + this.values["note"] = value; + return this; + }; - nudge = (value: number): this => { - this.values["nudge"] = value; - return this; - }; + speed = (value: number): this => { + this.values["speed"] = value; + return this; + }; - cut = (value: number): this => { - this.values["cut"] = value; - return this; - }; + begin = (value: number): this => { + this.values["begin"] = value; + return this; + }; - loop = (value: number): this => { - this.values["loop"] = value; - return this; - }; + end = (value: number): this => { + this.values["end"] = value; + return this; + }; - clip = (value: number): this => { - this.values["clip"] = value; - return this; - }; + gain = (value: number): this => { + this.values["gain"] = value; + return this; + }; - n = (value: number): this => { - this.values["n"] = value; - return this; - }; + cutoff = (value: number): this => { + this.values["cutoff"] = value; + return this; + }; + lpf = this.cutoff; - note = (value: number): this => { - this.values["note"] = value; - return this; - }; + resonance = (value: number): this => { + this.values["resonance"] = value; + return this; + }; + lpq = this.resonance; - speed = (value: number): this => { - this.values["speed"] = value; - return this; - }; + hcutoff = (value: number): this => { + this.values["hcutoff"] = value; + return this; + }; + hpf = this.hcutoff; - begin = (value: number): this => { - this.values["begin"] = value; - return this; - }; + hresonance = (value: number): this => { + this.values["hresonance"] = value; + return this; + }; + hpq = this.hresonance; - end = (value: number): this => { - this.values["end"] = value; - return this; - }; + bandf = (value: number): this => { + this.values["bandf"] = value; + return this; + }; + bpf = this.bandf; - gain = (value: number): this => { - this.values["gain"] = value; - return this; - }; + bandq = (value: number): this => { + this.values["bandq"] = value; + return this; + }; + bpq = this.bandq; - cutoff = (value: number): this => { - this.values["cutoff"] = value; - return this; - }; + coarse = (value: number): this => { + this.values["coarse"] = value; + return this; + }; - resonance = (value: number): this => { - this.values["resonance"] = value; - return this; - }; + crush = (value: number): this => { + this.values["crush"] = value; + return this; + }; - hcutoff = (value: number): this => { - this.values["hcutoff"] = value; - return this; - }; + shape = (value: number): this => { + this.values["shape"] = value; + return this; + }; - hresonance = (value: number): this => { - this.values["hresonance"] = value; - return this; - }; + pan = (value: number): this => { + this.values["pan"] = value; + return this; + }; - bandf = (value: number): this => { - this.values["bandf"] = value; - return this; - }; + vowel = (value: number): this => { + this.values["vowel"] = value; + return this; + }; - bandq = (value: number): this => { - this.values["bandq"] = value; - return this; - }; + delay = (value: number): this => { + this.values["delay"] = value; + return this; + }; - coarse = (value: number): this => { - this.values["coarse"] = value; - return this; - }; + delayfeedback = (value: number): this => { + this.values["delayfeedback"] = value; + return this; + }; + delayfb = this.delayfeedback; - crush = (value: number): this => { - this.values["crush"] = value; - return this; - }; + delaytime = (value: number): this => { + this.values["delaytime"] = value; + return this; + }; + delayt = this.delaytime; - shape = (value: number): this => { - this.values["shape"] = value; - return this; - }; + orbit = (value: number): this => { + this.values["orbit"] = value; + return this; + }; - pan = (value: number): this => { - this.values["pan"] = value; - return this; - }; + room = (value: number): this => { + this.values["room"] = value; + return this; + }; - vowel = (value: number): this => { - this.values["vowel"] = value; - return this; - }; + size = (value: number): this => { + this.values["size"] = value; + return this; + }; - delay = (value: number): this => { - this.values["delay"] = value; - return this; - }; + velocity = (value: number): this => { + this.values["velocity"] = value; + return this; + }; + vel = this.velocity; - delayfeedback = (value: number): this => { - this.values["delayfeedback"] = value; - return this; - }; - - delaytime = (value: number): this => { - this.values["delaytime"] = value; - return this; - }; - - orbit = (value: number): this => { - this.values["orbit"] = value; - return this; - }; - - room = (value: number): this => { - this.values["room"] = value; - return this; - }; - - size = (value: number): this => { - this.values["size"] = value; - return this; - }; - - velocity = (value: number): this => { - this.values["velocity"] = value; - return this; - }; - - modify = (func: Function): this => { - const funcResult = func(this); - if(funcResult instanceof Object) return funcResult; - else { - func(this.values); - return this; - } - }; - - // NOTE: Sustain of the sound. duration() from the superclass Event is used to change the note length. - sustain = (value: number): this => { - this.values["dur"] = value; - return this; - }; - - 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.freq = midiToFreq(note); - } + modify = (func: Function): this => { + const funcResult = func(this); + if (funcResult instanceof Object) return funcResult; + else { + func(this.values); + return this; } + }; - out = (): object => { - return superdough( - this.values, - this.app.clock.pulse_duration, - this.values.dur - ); - }; + // NOTE: Sustain of the sound. duration() from the superclass Event is used to change the note length. + sustain = (value: number): this => { + this.values["dur"] = value; + return this; + }; + sus = this.sustain; -} \ No newline at end of file + 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.freq = midiToFreq(note); + } + }; + + out = (): object => { + return superdough( + this.values, + this.app.clock.pulse_duration, + this.values.dur + ); + }; +} diff --git a/src/main.ts b/src/main.ts index 9321c89..ede47bc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -559,6 +559,7 @@ export class Editor { "time", "sound", "samples", + "synths", "midi", "functions", "reference",