Skip to content

Commit f82d31d

Browse files
committed
Added inverse FFT and Vocoder demo.
1 parent 1f34e99 commit f82d31d

File tree

6 files changed

+437
-46
lines changed

6 files changed

+437
-46
lines changed

dsp.js

+78
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,83 @@ FFT.prototype.forward = function(buffer) {
301301
while(i--) {
302302
spectrum[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) / bufferSize;
303303
}
304+
305+
return spectrum;
306+
};
307+
308+
FFT.prototype.inverse = function(real, imag) {
309+
// Locally scope variables for speed up
310+
var bufferSize = this.bufferSize,
311+
cosTable = this.cosTable,
312+
sinTable = this.sinTable,
313+
reverseTable = this.reverseTable,
314+
spectrum = this.spectrum;
315+
316+
real = real || this.real;
317+
imag = imag || this.imag;
318+
319+
for (var i = 0; i < bufferSize; i++) {
320+
imag[i] *= -1;
321+
}
322+
323+
var revReal = new Float32Array(bufferSize);
324+
var revImag = new Float32Array(bufferSize);
325+
326+
for (var i = 0; i < real.length; i++) {
327+
revReal[i] = real[reverseTable[i]];
328+
revImag[i] = imag[reverseTable[i]];
329+
}
330+
331+
real = revReal;
332+
imag = revImag;
333+
334+
var halfSize = 1,
335+
phaseShiftStepReal,
336+
phaseShiftStepImag,
337+
currentPhaseShiftReal,
338+
currentPhaseShiftImag,
339+
off,
340+
tr,
341+
ti,
342+
tmpReal,
343+
i;
344+
345+
while ( halfSize < bufferSize ) {
346+
phaseShiftStepReal = cosTable[halfSize];
347+
phaseShiftStepImag = sinTable[halfSize];
348+
currentPhaseShiftReal = 1;
349+
currentPhaseShiftImag = 0;
350+
351+
for ( var fftStep = 0; fftStep < halfSize; fftStep++ ) {
352+
i = fftStep;
353+
354+
while ( i < bufferSize ) {
355+
off = i + halfSize;
356+
tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
357+
ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
358+
359+
real[off] = real[i] - tr;
360+
imag[off] = imag[i] - ti;
361+
real[i] += tr;
362+
imag[i] += ti;
363+
364+
i += halfSize << 1;
365+
}
366+
367+
tmpReal = currentPhaseShiftReal;
368+
currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
369+
currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
370+
}
371+
372+
halfSize = halfSize << 1;
373+
}
374+
375+
var buffer = new Float32Array(bufferSize);
376+
for (var i = 0; i < bufferSize; i++) {
377+
buffer[i] = real[i] / bufferSize;
378+
}
379+
380+
return buffer;
304381
};
305382

306383
Sampler = function Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode) {
@@ -884,6 +961,7 @@ WindowFunction.prototype.process = function(buffer) {
884961
for ( var i = 0; i < length; i++ ) {
885962
buffer[i] *= this.func(length, i, this.alpha);
886963
}
964+
return buffer;
887965
};
888966

889967
WindowFunction.Bartlett = function(length, index) {

examples/filter.html

+28-34
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,14 @@
22
<html>
33
<head>
44
<!-- Load JQuery and JQuery-UI -->
5-
<link type="text/css" href="css/hot-sneaks/jquery-ui-1.8.custom.css" rel="stylesheet" />
65
<script type="text/javascript" src="js/jquery-1.4.2.min.js"></script>
7-
<!--<script type="text/javascript" src="js/jquery-ui-1.8.custom.min.js"></script>-->
86

97
<!-- Load Processing.js -->
108
<script language="javascript" src="js/processing.js"></script>
119
<script language="javascript" src="js/init.js"></script>
1210

1311
<!-- Load DSP.js -->
1412
<script src="../dsp.js"></script>
15-
<script>
16-
/*
17-
$(function() {
18-
$('#cutoff').slider({ orientation: 'vertical', range: 'min', min: 60, max: 5000, step: 1, value: 880, slide: changeFilter });
19-
$('#res').slider({ orientation: 'vertical', range: 'min', min: 1.0, max: 20.0, value: 1.0, step: 0.01, slide: changeFilter });
20-
});
21-
*/
22-
</script>
2313

2414
<style type="text/css">
2515
body, * {
@@ -66,9 +56,11 @@
6656
<script>
6757
// Setup shared variables
6858
var sampleRate = 44100;
59+
var frameSize = 4096;
6960

70-
var signal = new Float32Array(1024);
61+
var signal = new Float32Array(frameSize/2);
7162
var lp12;
63+
var buffer = [];
7264

7365

7466
// Setup experimental audio out
@@ -84,17 +76,32 @@
8476

8577
function loadedMetaData(event) {
8678
var audio = document.getElementById('input');
87-
audio.mozFrameBufferLength = 4096;
79+
audio.mozFrameBufferLength = frameSize;
8880
}
8981

9082
function audioWritten(event) {
9183
signal = DSP.getChannel(DSP.MIX, event.mozFrameBuffer);
92-
93-
// Apply the filter to the signal
9484
lp12.process(signal);
9585

96-
output.mozWriteAudio([]); // flush
97-
output.mozWriteAudio(signal);
86+
var audio = [];
87+
for (var i = 0; i < signal.length; i++) {
88+
audio[i] = signal[i];
89+
}
90+
91+
// Apply the filter to the signal
92+
93+
buffer = (buffer.length === 0) ? audio : buffer.concat(audio);
94+
95+
var written = output.mozWriteAudio(audio);
96+
97+
if (written < buffer.length) {
98+
buffer.slice(written);
99+
} else {
100+
buffer.length = 0;
101+
}
102+
103+
//output.mozWriteAudio([]); // flush
104+
//output.mozWriteAudio(signal);
98105
}
99106
</script>
100107

@@ -105,11 +112,10 @@
105112
void setup() {
106113
size(512, 200);
107114

108-
cut = new Knob("", 60, 5000, 880, 70, 20, 50, 50);
109-
res = new Knob("", 1, 20, 1, 0.4, 20, 100, 50);
115+
cut = new Knob("", 60, 6000, 2500, 70, 20, 50, 50);
116+
res = new Knob("", 1, 20, 7, 0.3, 20, 100, 50);
110117

111118
lp12 = new IIRFilter(DSP.LOWPASS, 22050, 0, sampleRate);
112-
//lp12.set($('#cutoff').slider('option', 'value'), $('#res').slider('option', 'value'));
113119

114120
// mute the input
115121
document.getElementById('input').muted = true;
@@ -120,8 +126,8 @@
120126
void draw() {
121127
background(255);
122128
lp12.set(cut.value, res.value);
123-
for (int i = 0; i < width; i++) {
124-
line(i, height/2 - signal[2*i]/2 * 200, i, height/2 + signal[2*i]/2 * 200);
129+
for (int i = 0; i < width; i+=2) {
130+
line(i, height/2 - signal[4*i]/2 * 200, i, height/2 + signal[4*i]/2 * 200);
125131
}
126132
fill(255, 0, 0);
127133
text("F", 30, 30);
@@ -215,19 +221,7 @@ <h1>Audio Filter</h1>
215221
<p>The <b>F</b> knob controls the cut off frequency, between 0 and 22050 Hz.</p>
216222
<p>The <b>Q</b> knob controls the resonance drive.</p>
217223
<p>A low frequency cutoff and strong resonance can give an acidy or dampened underwater effect.</p>
218-
<p>(Be careful! Adjust your volume! Certain slider combinations can give off loud undesired schreeching noises!)</p>
224+
<p>(Be careful! Adjust your volume! Certain knob combinations can give off loud undesired schreeching noises!)</p>
219225
<p>Music: Delv-X: Megaman theme (cover)</p>
220-
221-
222-
<!-- <div class="control" style="float: right; height: 188px">
223-
<h3>Low Pass Filter</h3>
224-
<table>
225-
<tr>
226-
<td><div id="cutoff" class="slider"></div>F</td>
227-
<td><div id="res" class="slider"></div>Q</td>
228-
</tr>
229-
</table>
230-
</div>
231-
</div>-->
232226
</body>
233227
</html>

examples/index.html

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ <h1>DSP.JS Examples</h1>
1818

1919
<dt><a href="sampler.html">Sampler</a></dt>
2020
<dd>A simple audio sampler with keyboard input<dd>
21+
22+
<dt><a href="vocoder.html">Vocoder</a></dt>
23+
<dd>Vocodes a formant with a carrier wave.<dd>
2124
</dl>
2225

2326
<h2>html5 + audio related examples</h2>

examples/js/processing.js

+3-11
Original file line numberDiff line numberDiff line change
@@ -7200,12 +7200,8 @@
72007200

72017201
this.toImageData = function() {
72027202
// changed for 0.9
7203-
if (this.imageData instanceof Image) {
7204-
return this.imageData;
7205-
} else {
7206-
var canvasData = getCanvasData(this.imageData);
7207-
return canvasData.context.getImageData(0, 0, this.width, this.height);
7208-
}
7203+
var canvasData = getCanvasData(this.imageData);
7204+
return canvasData.context.getImageData(0, 0, this.width, this.height);
72097205
};
72107206

72117207
this.toDataURL = function() {
@@ -7224,11 +7220,7 @@
72247220
this.fromHTMLImageData = function(htmlImg) {
72257221
// convert an <img> to a PImage
72267222
var canvasData = getCanvasData(htmlImg);
7227-
try {
7228-
var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
7229-
} catch(e) {
7230-
var imageData = htmlImg;
7231-
}
7223+
var imageData = canvasData.context.getImageData(0, 0, htmlImg.width, htmlImg.height);
72327224
this.fromImageData(imageData);
72337225
};
72347226

examples/sampler.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
var timePerWrite = 0;
5656
var programStart;
5757

58-
var s = new Sampler('audio/c.ogg', bufferSize, sampleRate);
58+
var s = new Sampler('audio/carrier.ogg', bufferSize, sampleRate);
5959
s.envelope = new ADSR(0, 0, 1, Infinity, 0, sampleRate);
6060
s.envelope.disable(); // turn off so it does not auto trigger
6161

0 commit comments

Comments
 (0)