Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Updated synthesizer and dsp.js

  • Loading branch information...
commit a3fd0cacc6900b9e6293778dc79d48653b66d547 1 parent feb5faf
@corbanbrook corbanbrook authored committed
View
40 public/synth/dsp.js
@@ -142,7 +142,7 @@ FFT.prototype.forward = function(buffer) {
return this.spectrum;
};
-
+
Oscillator = function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
this.frequency = frequency;
this.amplitude = amplitude;
@@ -150,6 +150,8 @@ Oscillator = function Oscillator(type, frequency, amplitude, bufferSize, sampleR
this.sampleRate = sampleRate;
this.frameCount = 0;
+ this.waveTableLength = 2048;
+
this.cyclesPerSample = frequency / sampleRate;
this.signal = new Array(bufferSize);
@@ -175,9 +177,30 @@ Oscillator = function Oscillator(type, frequency, amplitude, bufferSize, sampleR
break;
}
+ this.generateWaveTable = function() {
+ Oscillator.waveTable[this.func] = Array(2048);
+ var waveTableTime = this.waveTableLength / this.sampleRate;
+ var waveTableHz = 1 / waveTableTime;
+
+ for (var i = 0; i < this.waveTableLength; i++) {
+ Oscillator.waveTable[this.func][i] = this.func(i * waveTableHz/this.sampleRate);
+ }
+ };
+
+ if ( typeof Oscillator.waveTable === 'undefined' ) {
+ Oscillator.waveTable = {};
+ }
+
+ if ( typeof Oscillator.waveTable[this.func] === 'undefined' ) {
+ this.generateWaveTable();
+ }
+
+ this.waveTable = Oscillator.waveTable[this.func];
+
//this.generate();
};
+
Oscillator.prototype.setAmp = function(amplitude) {
if (amplitude >= 0 && amplitude <= 1) {
this.amplitude = amplitude;
@@ -227,16 +250,21 @@ Oscillator.prototype.addEnvelope = function(envelope) {
};
Oscillator.prototype.valueAt = function(offset) {
- return this.waveLength[offset % this.waveLength.length];
+ //return this.waveLength[offset % this.waveLength.length];
+ return this.waveTable[offset % this.waveTableLength];
};
Oscillator.prototype.generate = function() {
- var frameOffset = this.frameCount * this.bufferSize, step;
+ var frameOffset = this.frameCount * this.bufferSize;
+ var step = this.waveTableLength * this.frequency / this.sampleRate;
+ var offset;
for ( var i = 0; i < this.bufferSize; i++ ) {
- step = (frameOffset + i) * this.cyclesPerSample % 1;
-
- this.signal[i] = this.func(step) * this.amplitude;
+ //var step = (frameOffset + i) * this.cyclesPerSample % 1;
+ //this.signal[i] = this.func(step) * this.amplitude;
+ //this.signal[i] = this.valueAt(Math.round((frameOffset + i) * step)) * this.amplitude;
+ offset = Math.round((frameOffset + i) * step);
+ this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude;
}
this.frameCount++;
View
263 public/synth/examples/synthesizer2.html → public/synth/examples/synthesizer.html
@@ -1,7 +1,6 @@
<!DOCTYPE html>
<html>
<head>
- <title>Synthesizer 2</title>
<!-- Load JQuery and JQuery-UI -->
<link type="text/css" href="css/hot-sneaks/jquery-ui-1.8.custom.css" rel="stylesheet" />
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
@@ -35,7 +34,7 @@
$('#FR' ).slider({ orientation: 'vertical', range: 'min', value: 0.8, min: 0, max: 5.0, step: 0.01, slide: changeFilterEnvelope });
// Osc Mix
- $('#osc_mix').slider({ orientation: 'vertical', range: 'min', value: 0.5, min: 0, max: 1.0, step: 0.01, slide: changeAmplitude });
+ $('#osc_mix').slider({ orientation: 'vertical', range: 'min', value: 0.5, min: -0.1, max: 1.1, step: 0.01, slide: changeAmplitude });
changeAmplitude(); // reset amp and mix
@@ -44,6 +43,20 @@
$("#debug").html("");
});
+ // curve automation toggles
+ $("#curve_cutoff_toggle").click(function() {
+ if ( $("#curve_cutoff_toggle").attr("checked") ) {
+ SHOW_SEQUENCER = true;
+ CURVE_EDITOR = true;
+ }
+ });
+ $("#curve_res_toggle").click(function() {
+ if ( $("#curve_res_toggle").attr("checked") ) {
+ SHOW_SEQUENCER = true;
+ CURVE_EDITOR = true;
+ }
+ });
+
// Filter type radio selection
$("input[name='filter_type']").change(changeFilter);
changeFilter(); // reset filter to slider defaults
@@ -55,6 +68,10 @@
// Oscillator osctave select
$("#osc1_octave").change(changeOsc1);
$("#osc2_octave").change(changeOsc2);
+
+ // Oscillator osctave select
+ $("#osc1_semi").change(changeOsc1);
+ $("#osc2_semi").change(changeOsc2);
changeOsc1(); // reset oscillator to select defaults
changeOsc2();
@@ -104,10 +121,11 @@
<body>
<script>
var SHOW_SEQUENCER = true;
+ var CURVE_EDITOR = false;
// Setup shared variables
var sampleRate = 11025;
- var bufferSize = 1024;
+ var bufferSize = 1025;
var bufferTime = Math.floor(1000 / (sampleRate / bufferSize));
var frequency = 440;
@@ -117,11 +135,16 @@
amplitude = $('#amp').slider('option', 'value');
$('#ampLevel').html(Math.round(amplitude * 100) + "%");
- osc1_amplitude = $('#osc_mix').slider('option', 'value');
- osc2_amplitude = 1 - osc1_amplitude;
+ var mix = $('#osc_mix').slider('option', 'value');
+
+ mix = mix > 1 ? 1 : mix;
+ mix = mix < 0 ? 0 : mix;
+ osc2_amplitude = 1 - (osc1_amplitude = mix);
+
+
osc1_amplitude *= amplitude;
- osc2_amplitude *= amplitude;
+ osc2_amplitude *= amplitude;
};
var filter = new IIRFilter2(0, 0, 0, sampleRate);
@@ -132,7 +155,6 @@
};
var changeFilterEnvelope = function() {
- filter.addEnvelope(new ADSR($('#FA').slider('option', 'value'), $('#FD').slider('option', 'value'), $('#FS').slider('option', 'value'), bufferTime / 1000, $('#FR').slider('option', 'value'), sampleRate));
};
var osc;
@@ -161,11 +183,13 @@
var changeOsc1 = function() {
osc1_waveform = parseInt($("#osc1_waveform").val());
osc1_octave = parseInt($("#osc1_octave").val());
+ osc1_semi = parseInt($("#osc1_semi").val());
};
var changeOsc2 = function() {
osc2_waveform = parseInt($("#osc2_waveform").val());
osc2_octave = parseInt($("#osc2_octave").val());
+ osc2_semi = parseInt($("#osc2_semi").val());
};
var bpm = 60; // Beats per minute
@@ -192,6 +216,14 @@
sequencerNoteOn[i][j] = false;
}
}
+
+ var curveOver = 0;
+ var curveCutoff = new Array(17);
+ var curveRes = new Array(17);
+ for ( var i = 0; i < 17; i++ ) {
+ curveCutoff[i] = sampleRate / 4;
+ curveRes[i] = 0.1;
+ }
var oct = 3; // Root Octave
@@ -230,6 +262,7 @@
var startTime = (new Date()).getTime();
var currentTime = (new Date()).getTime();
var mergedOsc; // undefined
+ var filterOn = $('#filter_toggle').attr('checked');
if ( programStart === undefined ) {
programStart = new Date();
@@ -248,7 +281,8 @@
oscPool[i].osc1.addSignal(oscPool[i].osc2.signal);
// apply the filter
- if ( $('#filter_toggle').attr('checked') ) {
+ if ( filterOn ) {
+ filter.addEnvelope(oscPool[i].filterEnvelope);
filter.process(oscPool[i].osc1.signal);
}
@@ -256,7 +290,7 @@
oscPool[i].envelope.process(oscPool[i].osc1.signal);
// Merge down oscPool
- if ( typeof mergedOsc == 'undefined' ) {
+ if ( typeof mergedOsc === 'undefined' ) {
mergedOsc = oscPool[i].osc1;
} else {
mergedOsc.addSignal(oscPool[i].osc1.signal);
@@ -309,7 +343,7 @@
// setup oscillator to hold the merged signal of osc1 and osc2
osc = new Oscillator(osc1_waveform, 440, 0, bufferSize, sampleRate);
- setInterval(audioWriter, bufferTime); // start audioWriter timer
+ setInterval(audioWriter, bufferTime); // start audioWriter timer
}
void draw() {
@@ -317,7 +351,26 @@
int sequencerCol = Math.floor(sequencerStep) % 32;
background(0, 0, 30, 50);
-
+
+ var curCol = Math.floor(sequencerCol/2);
+ var nextCol = (curCol + 1) % 17;
+ var alpha = ((sequencerStep % 32) / 2) - curCol; // should be a value between 0 and 0.99
+
+ if ( $("#curve_cutoff_toggle").attr("checked") ) {
+ // interpolate value
+ var curveVal = curveCutoff[curCol] + (curveCutoff[nextCol] - curveCutoff[curCol]) * alpha;
+
+ $('#cutoff').slider('option', 'value', curveVal);
+ changeFilter();
+ }
+
+ if ( $("#curve_res_toggle").attr("checked") ) {
+ var curveVal = curveRes[curCol] + (curveRes[nextCol] - curveRes[curCol]) * alpha;
+
+ $('#res').slider('option', 'value', curveVal);
+ changeFilter();
+ }
+
// Check sequencer column for notes to trigger
for ( int i = 0; i < 24; i ++ ) {
if ( sequencer[sequencerCol][i] == true && !sequencerNoteOn[sequencerCol][i] ) {
@@ -334,11 +387,13 @@
var osc1 = new Oscillator(osc1_waveform, osc1_frequency, osc1_amplitude, bufferSize, sampleRate);
var osc2 = new Oscillator(osc2_waveform, osc2_frequency, osc2_amplitude, bufferSize, sampleRate);
var adsr = new ADSR($('#A').slider('option', 'value'), $('#D').slider('option', 'value'), $('#S').slider('option', 'value'), bufferTime / 1000, $('#R').slider('option', 'value'), sampleRate);
+ var fadsr = new ADSR($('#FA').slider('option', 'value'), $('#FD').slider('option', 'value'), $('#FS').slider('option', 'value'), bufferTime / 1000, $('#FR').slider('option', 'value'), sampleRate);
oscPool[index] = {};
oscPool[index].osc1 = osc1;
oscPool[index].osc2 = osc2;
oscPool[index].envelope = adsr;
+ oscPool[index].filterEnvelope = fadsr;
}
if ( sequencer[sequencerCol][i] == false && sequencerCol > 0 ) {
@@ -365,7 +420,7 @@
for (int i = 0; i < 32; i++ ) {
if ( i % 8 == 0 ) {
strokeWeight(2);
- stroke(200, 255, 255);
+ stroke(200, 255, 255, 60);
} else {
stroke(200, 255, 255, 20);
strokeWeight(1);
@@ -375,26 +430,107 @@
// Render sequencer step position
if ( i == Math.floor(sequencerStep % 32) ) {
noStroke();
- fill(200, 255, 255, 60);
- rect(i * (width/32), 0, width/32, height);
+ stroke(200, 255, 255, 60);
+ //rect(i * (width/32), 0, width/32, height);
+ //stroke(255, 0, 0);
+ line((sequencerStep % 32) * (width/32), 0, (sequencerStep % 32) * (width/32), height);
}
}
+
noStroke();
for (int c = 0; c < sequencer.length; c++ ) {
for (int r = 0; r < sequencer[c].length; r++ ) {
if ( sequencer[c][r] == true ) {
- fill(200, 255, 255);
-
+ if ( CURVE_EDITOR ) {
+ fill(200, 255, 255, 40);
+ } else {
+ fill(200, 255, 255, 100);
+ }
if ( c == Math.floor(sequencerStep % 32) ) {
- fill(100 * r, 255 - (r * 10), r * 5, 200);
rect( (c - 1) * (width/32), (r -1) * (height/24), width/32 * 3, height/24 * 3);
- fill(255);
}
rect(c * (width/32), r * (height/24), width/32, height/24);
}
}
}
+
+ if ( CURVE_EDITOR ) {
+ strokeWeight(2);
+
+ var cutoffScale = 50;
+ var resScale = 100;
+
+ for ( int i = 0; i < 17; i++ ) {
+ fill(0, 0, 30);
+
+
+ // Cut off curve
+ if ( $("#curve_cutoff_toggle").attr("checked") ) {
+ stroke(200, 255, 255, 100);
+ } else {
+ stroke(200, 255, 255, 40);
+ }
+
+ var xPos = i * (width/16);
+ var yPos = height/2 - curveCutoff[i] / cutoffScale;
+
+ if (i < 16 ) {
+ var xPosNext = (i+1) * (width/16);
+ var yPosNext = height/2 - curveCutoff[i+1] / cutoffScale;
+
+ line(xPos, yPos, xPosNext, yPosNext);
+ }
+
+ if ( mouseX > xPos - 7 && mouseX < xPos + 7 && mouseY > yPos -7 && mouseY < yPos + 7 ) {
+ ellipse(xPos, yPos, 7, 7);
+ curveOver = i;
+ } else {
+ ellipse(xPos, yPos, 5, 5);
+ }
+
+
+ // Resonance Curve
+ if ( $("#curve_res_toggle").attr("checked") ) {
+ stroke(200, 255, 255, 100);
+ } else {
+ stroke(200, 255, 255, 40);
+ }
+
+ var xPos = i * (width/16);
+ var yPos = height - curveRes[i] * resScale;
+
+ if (i < 16 ) {
+ var xPosNext = (i+1) * (width/16);
+ var yPosNext = height - curveRes[i+1] * resScale;
+
+ line(xPos, yPos, xPosNext, yPosNext);
+ }
+
+ if ( mouseX > xPos - 7 && mouseX < xPos + 7 && mouseY > yPos -7 && mouseY < yPos + 7 ) {
+ ellipse(xPos, yPos, 7, 7);
+ curveOver = i;
+ } else {
+ ellipse(xPos, yPos, 5, 5);
+ }
+
+ // draw center line
+ stroke(200, 255, 255, 60);
+ line(0, height/2, width, height/2);
+
+
+ }
+ strokeWeight(1);
+
+ if ( DRAGGING ) {
+ if ( mouseY < height / 2 ) {
+ curveCutoff[curveOver] = (height/2 - mouseY) * cutoffScale;
+ } else if ( mouseY > height / 2 ) {
+ curveRes[curveOver] = (height - mouseY) / resScale;
+ }
+ }
+ }
+
} else {
/*
* Signal View
@@ -425,17 +561,29 @@
);
}
}
+
+ boolean DRAGGING = false;
+
+ void mousePressed() {
+ if ( SHOW_SEQUENCER && CURVE_EDITOR ) {
+ DRAGGING = true;
+ }
+ }
void mouseReleased() {
- if ( SHOW_SEQUENCER ) {
+ if ( SHOW_SEQUENCER && !CURVE_EDITOR ) {
// Plot notes in sequencer
var col = parseInt(mouseX / (width/32));
var row = parseInt(mouseY / (height/24));
+
if(window.synth != null){
window.synth.setNote(col,row,!sequencer[col][row]);
} else {
sequencer[col][row] = !sequencer[col][row];
}
+
+ } else if ( SHOW_SEQUENCER && CURVE_EDITOR && DRAGGING ) {
+ DRAGGING = false;
}
}
@@ -443,6 +591,9 @@
if ( key == 's' ) {
SHOW_SEQUENCER = !SHOW_SEQUENCER;
}
+ if ( key == 'c' ) {
+ CURVE_EDITOR = !CURVE_EDITOR;
+ }
}
</script>
@@ -454,48 +605,66 @@
<ul>
<li><b>S</b> - toggle between sequencer/signal view</li>
+ <li><b>C</b> - toggle curve editor</li>
<li><b>W</b> - change waveform</li>
</ul>
<div style="float:left;margin-right: 5px;"><canvas id="signal" width="200px" height="200px"></canvas></div>
<div class="control">
- <h3><input type="checkbox" id="debug_toggle" checked/> Debug</h3>
+ <h3><input type="checkbox" id="debug_toggle" /> Debug</h3>
<div id="debug"></div>
</div>
<div style="clear:both;"></div>
- <div class="control" style="width: 512px;">
+ <div class="control">
<h3>Oscillators</h3>
- <table width="100%">
+ <table>
<tr>
- <td width="20%"><h3>Osc 1</h3></td>
- <td>
- Waveform <select id="osc1_waveform">
+ <td style="width: 40px;"><h3>Osc 1</h3></td>
+ <td style="width: 100px;">
+ <select id="osc1_waveform">
<option value="1">Sine</option>
<option value="2">Triangle</option>
<option value="3">Saw</option>
<option value="4" selected="selected">Square</option>
</select>
</td>
- <td>
- Octave <select id="osc1_octave">
+ <td style="width: 80px;">
+ Oct <select id="osc1_octave">
<option value="8">8</option>
<option value="7">7</option>
<option value="6">6</option>
<option value="5">5</option>
- <option value="4">4</option>
- <option value="3" selected="selected">3</option>
+ <option value="4" selected="selected">4</option>
+ <option value="3">3</option>
<option value="2">2</option>
<option value="1">1</option>
<option value="0">0</option>
</select>
</td>
+ <td style="width: 80px;">
+ Semi <select id="osc1_semi">
+ <option value="12">12</option>
+ <option value="11">11</option>
+ <option value="10">10</option>
+ <option value="9">9</option>
+ <option value="8">8</option>
+ <option value="7">7</option>
+ <option value="6">6</option>
+ <option value="5">5</option>
+ <option value="4">4</option>
+ <option value="3">3</option>
+ <option value="2">2</option>
+ <option value="1">1</option>
+ <option value="0" selected="selected">0</option>
+ </select>
+ </td>
</tr>
<tr>
- <td width="20%"><h3>Osc 2</h3></td>
+ <td><h3>Osc 2</h3></td>
<td>
- Waveform <select id="osc2_waveform">
+ <select id="osc2_waveform">
<option value="1">Sine</option>
<option value="2">Triangle</option>
<option value="3">Saw</option>
@@ -503,18 +672,35 @@
</select>
</td>
<td>
- Octave <select id="osc2_octave">
+ Oct <select id="osc2_octave">
<option value="8">8</option>
<option value="7">7</option>
<option value="6">6</option>
<option value="5">5</option>
- <option value="4">4</option>
- <option value="3" selected="selected">3</option>
+ <option value="4" selected="selected">4</option>
+ <option value="3">3</option>
<option value="2">2</option>
<option value="1">1</option>
<option value="0">0</option>
</select>
</td>
+ <td style="width: 100px;">
+ Semi <select id="osc2_semi">
+ <option value="12">12</option>
+ <option value="11">11</option>
+ <option value="10">10</option>
+ <option value="9">9</option>
+ <option value="8">8</option>
+ <option value="7">7</option>
+ <option value="6">6</option>
+ <option value="5">5</option>
+ <option value="4">4</option>
+ <option value="3">3</option>
+ <option value="2">2</option>
+ <option value="1">1</option>
+ <option value="0" selected="selected">0</option>
+ </select>
+ </td>
</tr>
</table>
</div>
@@ -522,7 +708,7 @@
<h3>Mix</h3>
<table>
<tr>
- <td><div id="osc_mix" class="slider" style="height: 60px;"></div></td>
+ <td><div id="osc_mix" class="slider" style="height: 70px;"></div></td>
</tr>
</table>
</div>
@@ -560,8 +746,15 @@
<input type="radio" id="bp12" name="filter_type" value="2"/> <label for="bp12">BP12</label><br />
<input type="radio" id="lp12" name="filter_type" value="0" checked="checked"/> <label for="lp12">LP12</label><br />
</div>
+ <div style="margin-top: 10px;">
+ <h3>Automate</h3>
+ <input type="checkbox" id="curve_cutoff_toggle" /> F<br/>
+ <input type="checkbox" id="curve_res_toggle" /> Q
+ </div>
</td>
</tr>
+ <tr>
+ </tr>
</table>
</div>
<div class="control">
Please sign in to comment.
Something went wrong with that request. Please try again.