Adding documentation about synthesizers
This commit is contained in:
@ -99,6 +99,8 @@
|
|||||||
<p rel="noopener noreferrer" id="docs_time" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Time</p>
|
<p rel="noopener noreferrer" id="docs_time" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Time</p>
|
||||||
<p rel="noopener noreferrer" id="docs_sound" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Sound</p>
|
<p rel="noopener noreferrer" id="docs_sound" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Sound</p>
|
||||||
<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_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_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_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_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>
|
<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>
|
||||||
|
|||||||
@ -355,6 +355,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 <icode>sound</icode> function can take the name of a synthesizer as first argument.
|
||||||
|
- <icode>sine</icode>, <icode>sawtooth</icode>,<icode>triangle</icode>, <icode>square</icode> for the waveform selection.
|
||||||
|
- <icode>cutoff</icode> and <icode>resonance</icode> for adding a low-pass filter with cutoff frequency and filter resonance.
|
||||||
|
- <icode>hcutoff</icode> or <icode>bandf</icode> to switch to a high-pass or bandpass filter.
|
||||||
|
- <icode>hresonance</icode> and <icode>bandq</icode> 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.
|
||||||
|
|
||||||
|
- <icode>fmi</icode> (_frequency modulation index_): a floating point value between <icode>1</icode> and <icode>n</icode>.
|
||||||
|
- <icode>fmh</icode> (_frequency modulation harmonic ratio_): a floating point value between <icode>1</icode> and <icode>n</icode>.
|
||||||
|
|
||||||
|
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 <icode>fm</icode> argument. You will have to feed both as a string: <icode>fm('2:4')</icode>. If you only feed one number, only the _modulation index_ will be updated.
|
||||||
|
|
||||||
|
`;
|
||||||
|
|
||||||
const about: string = `
|
const about: string = `
|
||||||
# About Topos
|
# About Topos
|
||||||
|
|
||||||
@ -570,6 +615,7 @@ export const documentation = {
|
|||||||
time: time,
|
time: time,
|
||||||
sound: sound,
|
sound: sound,
|
||||||
samples: samples,
|
samples: samples,
|
||||||
|
synths: synths,
|
||||||
midi: midi,
|
midi: midi,
|
||||||
functions: functions,
|
functions: functions,
|
||||||
reference: reference,
|
reference: reference,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { type Editor } from '../main';
|
import { type Editor } from "../main";
|
||||||
import { SoundEvent } from './Event';
|
import { SoundEvent } from "./Event";
|
||||||
import { midiToFreq, noteFromPc } from 'zifferjs';
|
import { midiToFreq, noteFromPc } from "zifferjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
superdough,
|
superdough,
|
||||||
@ -8,235 +8,244 @@ import {
|
|||||||
} from "superdough";
|
} from "superdough";
|
||||||
|
|
||||||
export class Sound extends SoundEvent {
|
export class Sound extends SoundEvent {
|
||||||
|
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) {
|
attack = (value: number): this => {
|
||||||
super(app);
|
this.values["attack"] = value;
|
||||||
if (typeof sound === 'string') this.values = { 's': sound, 'dur': 0.5 };
|
return this;
|
||||||
else this.values = sound;
|
};
|
||||||
|
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 => {
|
sound = (value: string): this => {
|
||||||
this.values["attack"] = value;
|
this.values["s"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
atk = this.attack;
|
|
||||||
|
|
||||||
decay = (value: number): this => {
|
fmi = (value: number): this => {
|
||||||
this.values["decay"] = value;
|
this.values["fmi"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
dec = this.decay;
|
|
||||||
|
|
||||||
release = (value: number): this => {
|
fmh = (value: number): this => {
|
||||||
this.values["release"] = value;
|
this.values["fmh"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
rel = this.release;
|
|
||||||
|
|
||||||
unit = (value: number): this => {
|
nudge = (value: number): this => {
|
||||||
this.values["unit"] = value;
|
this.values["nudge"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
freq = (value: number): this => {
|
cut = (value: number): this => {
|
||||||
this.values["freq"] = value;
|
this.values["cut"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
fm = (value: number | string): this => {
|
loop = (value: number): this => {
|
||||||
if (typeof value === "number") {
|
this.values["loop"] = value;
|
||||||
this.values["fmi"] = value;
|
return this;
|
||||||
} else {
|
};
|
||||||
let values = value.split(":");
|
|
||||||
this.values["fmi"] = parseFloat(values[0]);
|
|
||||||
if (values.length > 1) this.values["fmh"] = parseFloat(values[1]);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
sound = (value: string): this => {
|
clip = (value: number): this => {
|
||||||
this.values['s'] = value
|
this.values["clip"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
fmi = (value: number): this => {
|
n = (value: number): this => {
|
||||||
this.values["fmi"] = value;
|
this.values["n"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
fmh = (value: number): this => {
|
note = (value: number): this => {
|
||||||
this.values["fmh"] = value;
|
this.values["note"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
nudge = (value: number): this => {
|
speed = (value: number): this => {
|
||||||
this.values["nudge"] = value;
|
this.values["speed"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
cut = (value: number): this => {
|
begin = (value: number): this => {
|
||||||
this.values["cut"] = value;
|
this.values["begin"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
loop = (value: number): this => {
|
end = (value: number): this => {
|
||||||
this.values["loop"] = value;
|
this.values["end"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
clip = (value: number): this => {
|
gain = (value: number): this => {
|
||||||
this.values["clip"] = value;
|
this.values["gain"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
n = (value: number): this => {
|
cutoff = (value: number): this => {
|
||||||
this.values["n"] = value;
|
this.values["cutoff"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
note = (value: number): this => {
|
resonance = (value: number): this => {
|
||||||
this.values["note"] = value;
|
this.values["resonance"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
speed = (value: number): this => {
|
hcutoff = (value: number): this => {
|
||||||
this.values["speed"] = value;
|
this.values["hcutoff"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
begin = (value: number): this => {
|
hresonance = (value: number): this => {
|
||||||
this.values["begin"] = value;
|
this.values["hresonance"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
end = (value: number): this => {
|
bandf = (value: number): this => {
|
||||||
this.values["end"] = value;
|
this.values["bandf"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
gain = (value: number): this => {
|
bandq = (value: number): this => {
|
||||||
this.values["gain"] = value;
|
this.values["bandq"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
cutoff = (value: number): this => {
|
coarse = (value: number): this => {
|
||||||
this.values["cutoff"] = value;
|
this.values["coarse"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
resonance = (value: number): this => {
|
crush = (value: number): this => {
|
||||||
this.values["resonance"] = value;
|
this.values["crush"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
hcutoff = (value: number): this => {
|
shape = (value: number): this => {
|
||||||
this.values["hcutoff"] = value;
|
this.values["shape"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
hresonance = (value: number): this => {
|
pan = (value: number): this => {
|
||||||
this.values["hresonance"] = value;
|
this.values["pan"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
bandf = (value: number): this => {
|
vowel = (value: number): this => {
|
||||||
this.values["bandf"] = value;
|
this.values["vowel"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
bandq = (value: number): this => {
|
delay = (value: number): this => {
|
||||||
this.values["bandq"] = value;
|
this.values["delay"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
coarse = (value: number): this => {
|
delayfeedback = (value: number): this => {
|
||||||
this.values["coarse"] = value;
|
this.values["delayfeedback"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
crush = (value: number): this => {
|
delaytime = (value: number): this => {
|
||||||
this.values["crush"] = value;
|
this.values["delaytime"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
shape = (value: number): this => {
|
orbit = (value: number): this => {
|
||||||
this.values["shape"] = value;
|
this.values["orbit"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
pan = (value: number): this => {
|
room = (value: number): this => {
|
||||||
this.values["pan"] = value;
|
this.values["room"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
vowel = (value: number): this => {
|
size = (value: number): this => {
|
||||||
this.values["vowel"] = value;
|
this.values["size"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
delay = (value: number): this => {
|
velocity = (value: number): this => {
|
||||||
this.values["delay"] = value;
|
this.values["velocity"] = value;
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
delayfeedback = (value: number): this => {
|
modify = (func: Function): this => {
|
||||||
this.values["delayfeedback"] = value;
|
const funcResult = func(this);
|
||||||
return this;
|
if (funcResult instanceof Object) return funcResult;
|
||||||
};
|
else {
|
||||||
|
func(this.values);
|
||||||
delaytime = (value: number): this => {
|
return 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
out = (): object => {
|
// NOTE: Sustain of the sound. duration() from the superclass Event is used to change the note length.
|
||||||
return superdough(
|
sustain = (value: number): this => {
|
||||||
this.values,
|
this.values["dur"] = value;
|
||||||
this.app.clock.pulse_duration,
|
return this;
|
||||||
this.values.dur
|
};
|
||||||
);
|
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.freq = midiToFreq(note);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
out = (): object => {
|
||||||
|
return superdough(
|
||||||
|
this.values,
|
||||||
|
this.app.clock.pulse_duration,
|
||||||
|
this.values.dur
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -559,6 +559,7 @@ export class Editor {
|
|||||||
"time",
|
"time",
|
||||||
"sound",
|
"sound",
|
||||||
"samples",
|
"samples",
|
||||||
|
"synths",
|
||||||
"midi",
|
"midi",
|
||||||
"functions",
|
"functions",
|
||||||
"reference",
|
"reference",
|
||||||
|
|||||||
Reference in New Issue
Block a user