2019-08-16 19:15:14 +00:00
|
|
|
import { FrequencyClass } from "../core/type/Frequency";
|
|
|
|
import { Frequency, Positive, Time } from "../core/type/Units";
|
2019-08-16 17:57:38 +00:00
|
|
|
import { deepMerge, optionsFromArguments } from "../core/util/Defaults";
|
|
|
|
import { readOnly, RecursivePartial } from "../core/util/Interface";
|
2019-08-16 19:15:14 +00:00
|
|
|
import { Monophonic } from "./Monophonic";
|
2019-08-16 17:57:38 +00:00
|
|
|
import { Synth, SynthOptions } from "./Synth";
|
|
|
|
|
2019-08-16 19:21:09 +00:00
|
|
|
export interface MembraneSynthOptions extends SynthOptions {
|
2019-08-16 17:57:38 +00:00
|
|
|
pitchDecay: Time;
|
|
|
|
octaves: Positive;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* MembraneSynth makes kick and tom sounds using a single oscillator
|
|
|
|
* with an amplitude envelope and frequency ramp. A Tone.OmniOscillator
|
|
|
|
* is routed through a Tone.AmplitudeEnvelope to the output. The drum
|
|
|
|
* quality of the sound comes from the frequency envelope applied
|
|
|
|
* during MembraneSynth.triggerAttack(note). The frequency envelope
|
|
|
|
* starts at <code>note * .octaves</code> and ramps to <code>note</code>
|
|
|
|
* over the duration of <code>.pitchDecay</code>.
|
|
|
|
* @example
|
|
|
|
* var synth = new MembraneSynth().toMaster();
|
|
|
|
* synth.triggerAttackRelease("C2", "8n");
|
|
|
|
*/
|
2019-08-16 19:15:14 +00:00
|
|
|
export class MembraneSynth extends Synth<MembraneSynthOptions> {
|
2019-08-16 17:57:38 +00:00
|
|
|
|
2019-09-04 23:18:44 +00:00
|
|
|
readonly name: string = "MembraneSynth";
|
2019-08-16 17:57:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of octaves the pitch envelope ramps.
|
|
|
|
*/
|
|
|
|
octaves: Positive;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The amount of time the frequency envelope takes.
|
|
|
|
*/
|
|
|
|
pitchDecay: Time;
|
|
|
|
|
2019-08-16 19:15:14 +00:00
|
|
|
/**
|
|
|
|
* Portamento is ignored in this synth. use pitch decay instead.
|
|
|
|
*/
|
|
|
|
readonly portamento = 0;
|
|
|
|
|
2019-08-27 15:57:00 +00:00
|
|
|
/**
|
|
|
|
* @param options the options available for the synth see defaults
|
|
|
|
*/
|
2019-08-16 17:57:38 +00:00
|
|
|
constructor(options?: RecursivePartial<MembraneSynthOptions>)
|
|
|
|
constructor() {
|
|
|
|
|
|
|
|
super(optionsFromArguments(MembraneSynth.getDefaults(), arguments));
|
|
|
|
const options = optionsFromArguments(MembraneSynth.getDefaults(), arguments);
|
|
|
|
|
|
|
|
this.pitchDecay = options.pitchDecay;
|
2019-08-16 19:15:14 +00:00
|
|
|
this.octaves = options.octaves;
|
2019-08-16 17:57:38 +00:00
|
|
|
readOnly(this, ["oscillator", "envelope"]);
|
|
|
|
}
|
|
|
|
|
|
|
|
static getDefaults(): MembraneSynthOptions {
|
2019-08-16 19:15:14 +00:00
|
|
|
return deepMerge(Monophonic.getDefaults(), Synth.getDefaults(), {
|
2019-08-16 17:57:38 +00:00
|
|
|
envelope : {
|
|
|
|
attack : 0.001,
|
|
|
|
attackCurve : "exponential",
|
|
|
|
decay : 0.4,
|
|
|
|
release : 1.4,
|
|
|
|
sustain : 0.01,
|
|
|
|
},
|
|
|
|
octaves: 10,
|
|
|
|
oscillator: {
|
|
|
|
type: "sine",
|
|
|
|
},
|
|
|
|
pitchDecay: 0.05,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-08-16 19:15:14 +00:00
|
|
|
setNote(note: Frequency | FrequencyClass, time?: Time): this {
|
2019-08-16 17:57:38 +00:00
|
|
|
const seconds = this.toSeconds(time);
|
2019-08-16 19:15:14 +00:00
|
|
|
const hertz = this.toFrequency(note instanceof FrequencyClass ? note.toFrequency() : note);
|
2019-08-16 17:57:38 +00:00
|
|
|
const maxNote = hertz * this.octaves;
|
|
|
|
this.oscillator.frequency.setValueAtTime(maxNote, seconds);
|
|
|
|
this.oscillator.frequency.exponentialRampToValueAtTime(hertz, seconds + this.toSeconds(this.pitchDecay));
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
dispose(): this {
|
|
|
|
super.dispose();
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
}
|