Skip to content

Commit

Permalink
Adds the Delay example
Browse files Browse the repository at this point in the history
  • Loading branch information
modlfo committed Mar 1, 2016
1 parent b14a185 commit 47e6dce
Show file tree
Hide file tree
Showing 9 changed files with 1,444 additions and 2 deletions.
210 changes: 210 additions & 0 deletions examples/web/delay.vult
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
= A simple synthesizer with one LFO and a Delay effect
CC30 - Volume
CC31 - Detune/Resonance
CC32 - LFO rate
CC33 - LFO amount (bipolar)
CC34 - Delay time
CC35 - Delay feedback
*/

// Used to soften the transitions of controls
fun smooth(input:real) : real {
mem x;
x = x+(input-x)*0.005;
return x;
}

// Returns true every time the input value changes
fun change(x:real):bool {
mem pre_x;
val v:bool = pre_x<>x;
pre_x = x;
return v;
}

// Returns true if the value changes from 0 to anything
fun edge(x:bool):bool {
mem pre_x;
val v:bool = (pre_x<>x) && (pre_x==false);
pre_x = x;
return v;
}

// Returns true every 'n' calls
fun each(n:int) : bool {
mem count;
val ret = (count == 0);
count = (count + 1) % n;
return ret;
}

// Converts the MIDI note to increment rate at a 44100 sample rate
fun pitchToRate(d) return 8.1758*exp(0.0577623*d)/44100.0;

fun phasor(pitch:real,reset:bool){
mem rate,phase;
if(change(pitch))
rate = pitchToRate(pitch);
phase = if reset then 0.0 else (phase + rate) % 1.0;
return phase;
}

// A simple LFO with reset signal
fun lfo(f:real,gate:bool) : real {
mem phase;
val rate = f * 10.0/44100.0;
if(edge(gate)) phase = 0.0;
phase = phase + rate;
if(phase>1.0) phase = phase-1.0;
return sin(phase*2.0*3.141592653589793)-0.5;
}

// Implements the resonant filter simulation as shown in
// http://en.wikipedia.org/wiki/Phase_distortion_synthesis
fun phd_osc(pitch:real,detune:real) : real {
mem pre_phase1; // used to detect when the phase wrapps from 1 to 0
val phase1 = phasor(pitch,false);
val comp = 1.0 - phase1;
val reset = (pre_phase1 - phase1) > 0.5;
pre_phase1 = phase1;
val phase2 = phasor(pitch+smooth(detune)*32.0,reset);
val sine = sin(2.0*3.14159265359*phase2);
return sine*comp;
}

// Simple delay.
fun delay(x:real, time:real, feedback:real) : real {
mem buffer : array(real,44100);
mem write_pos;
// Constraints the parameter values
time = clip(time,0.0,1.0);
feedback = clip(feedback,0.0,1.0);
// Gets the position in the buffer to read
val index_r = real(size(buffer)) * time;
val index_i = int(floor(index_r));
val delta = write_pos - index_i;
val read_pos = if delta < 0 then size(buffer)+delta else delta;
// Gets the decimal part of the position
val decimal = index_r - real(index_i);
// Reads the values in the buffer
val x1 = get(buffer,read_pos);
val x2 = get(buffer,(read_pos+1) % size(buffer));
// Interpolates the value
val ret = (x2-x1)*decimal + x1;
// Write the data to the buffer
write_pos = (write_pos+1) % size(buffer);
_ = set(buffer,write_pos,clip(x+feedback*ret,-1.0,1.0));
return ret;
}

/* These three functions handle midi on/off events in order to behave
* like a monophonic sinthesizer that can hold 4 notes */

// Activates a note and returns the current note value
fun mono_noteOn(n:int){
mem count,pre;
mem notes : array(int,4);
// Do not add more that the size of array
if(count < size(notes)) {
_ = set(notes,count,n);
pre = n;
if(count < size(notes)) count = count + 1;
}
return pre;
}

// Deactivates a note and returns the following note value;
and mono_noteOff(n:int){
mem count,pre;
mem notes : array(int,4);
val found = false;
val pos;
val i = 0;
// if there are no notes, no dot do anything
if(count == 0)
return pre;
// Finds the location of the note
while(i < size(notes) && not(found)){
if(get(notes,i) == n) {
pos = i;
found = true;
}
i = i + 1;
}
// if the note was found moves all the notes one location
if(found) {
val k = pos + 1;
while(k < size(notes)) {
_ = set(notes,k-1,get(notes,k));
k = k + 1;
}
// If found, decrease the number of active notes
if(found && count>0) {
count = count - 1;
pre = get(notes,count - 1);
}
}
return pre;
}

// Returns 1 if any note is active
and mono_isGateOn() {
mem count;
return count > 0;
}

// Main processing function
fun process(input:real){
mem volume,detune; // values set in 'controlChange'
mem pitch;
mem lfo_rate,lfo_amt;
mem time, feedback;

val gate = notes:mono_isGateOn();
// creates one LFO
val lfo_val = lfo(lfo_rate,gate)*lfo_amt;
// creates one oscillator
val o1 = phd_osc(pitch,detune+lfo_val);
// gets the amplification by using a low-pass on the gate
val amp = smooth(if gate then 1.0 else 0.0);
val osc_out = o1 * amp;
val delay_out = delay(osc_out,smooth(time),smooth(feedback));
return volume * (osc_out+delay_out) /2.0;
}

// Called when a note On is received
and noteOn(note:int,velocity:int){
mem pitch = real(notes:mono_noteOn(note));
}

// Called when a note Off is received
and noteOff(note){
mem pitch = real(notes:mono_noteOff(note));
}

// Called when a control changes
and controlChange(control,value){
mem volume;
mem detune;
mem lfo_rate, lfo_amt;
mem time, feedback;
// Control 30 defines the volume
if(control==30) volume = value/127.0;
if(control==31) detune = value/127.0;
if(control==32) lfo_rate = value/127.0;
if(control==33) lfo_amt = 2.0*((real(value)/127.)-0.5);
if(control==34) time = value/127.0;
if(control==35) feedback = value/127.0;
}

// Called on initialization to define initial values
and default(){
mem volume = 0.0;
mem pitch = 45.0;
mem detune = 0.8;
mem lfo_rate = 0.07;
mem lfo_amt = -0.8;
mem time = 0.5;
mem feedback = 0.5;
}
2 changes: 1 addition & 1 deletion examples/web/synth2.vult
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
= A synthesizer with a bandwidth-limited multi-oscillator
and a state variable filter
and a state variable filter
CC30 - Volume
CC31 - Wave selector (pulse,saw,triangle)
CC32 - Pulse width
Expand Down
2 changes: 1 addition & 1 deletion src/generators/vultCh.ml
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ let getFunctionSetType (elem_typs:VType.t list) : VType.t =

let getInitArrayFunction params (typ:VType.t) : string =
match () with
| _ when (params.real = Float) && (isReal typ) -> "float_init_rray"
| _ when (params.real = Float) && (isReal typ) -> "float_init_array"
| _ when (params.real = Fixed) && (isReal typ) -> "fix_init_array"
| _ when (isInt typ) -> "int_init_array"
| _ when (isBool typ) -> "bool_init_array"
Expand Down
Loading

0 comments on commit 47e6dce

Please sign in to comment.