dspjs
Version:
DSP.js is a comprehensive digital signal processing library for javascript
325 lines (267 loc) • 10.3 kB
HTML
<html>
<head>
<!-- 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>
<script type="text/javascript" src="js/jquery-ui-1.8.custom.min.js"></script>
<!-- Load Processing.js -->
<script language="javascript" src="js/processing.js"></script>
<script language="javascript" src="js/init.js"></script>
<!-- Load DSP.js -->
<script src="../dsp.js"></script>
<script>
</script>
<style type="text/css">
</style>
</head>
<body>
<script>
// Setup shared variables
var sampleRate = 44100;
var bufferSize = 2048;
var bufferTime = Math.floor(1000 / (sampleRate / bufferSize));
var amplitude = 0.1; // Default amplitude at 70%
var oct = 2; // Root Octave
var oscs = [];
var noteOn = [];
var playSample = false;
// Borrowed from F1LTER's code
var midiNoteFreq = [ 16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87,
32.7, 34.65, 36.71, 38.89, 41.2, 43.65, 46.25, 49, 51.91, 55, 58.27, 61.74,
65.41, 69.3, 73.42, 77.78, 82.41, 87.31, 92.5, 98, 103.83, 110, 116.54, 123.47,
130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185, 196, 207.65, 220, 233.08, 246.94,
261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392, 415.3, 440, 466.16, 493.88,
523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61, 880, 932.33, 987.77,
1046.5, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760, 1864.66, 1975.53,
2093, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520, 3729.31, 3951.07,
4186.01, 4434.92, 4698.64, 4978 ];
var silence = new Float32Array(bufferSize);
// Setup audio channel
var output = new Audio();
if ( typeof output.mozSetup === 'function' ) {
output.mozSetup(1, sampleRate, 1);
}
var timePerWrite = 0;
var programStart;
// load the voice formant
var v = new Sampler('audio/formant.ogg', bufferSize, sampleRate);
v.envelope = new ADSR(0, 0, 1, Infinity, 0, sampleRate);
v.envelope.disable(); // turn off so it does not auto trigger
// load the synth carrier
var s = new Sampler('audio/carrier.ogg', bufferSize, sampleRate);
s.envelope = new ADSR(0, 0, 1, Infinity, 0, sampleRate);
s.envelope.disable(); // turn off so it does not auto trigger
s.loopMode = 3; // back and forth loop
var fftcar = new FFT(bufferSize, sampleRate);
var fftmod = new FFT(bufferSize, sampleRate);
var hann = new WindowFunction(DSP.HANN);
var real = new Float32Array(bufferSize);
var imag = new Float32Array(bufferSize);
var spec1, spec2;
var numBands = 32;
var bands = [];
for (var i = 0; i < bufferSize/2; i+= Math.round((bufferSize/2)/numBands) ) {
bands.push(i);
}
bands[bands.length-1] = bufferSize/2;
var audioWriter = function() {
if (s.loaded) {
var startTime = (new Date()).getTime();
if ( programStart === undefined ) {
programStart = new Date();
}
var carrier, formant, additiveSignal = [];
if ( s.envelope.isActive() && v.envelope.isActive() ) {
s.generate();
v.generate();
carrier = s.applyEnvelope();
formant = v.applyEnvelope();
spec1 = fftcar.forward(hann.process(carrier));
spec2 = fftmod.forward(hann.process(formant));
for (var i = 0; i < numBands-1; i++) {
var totalReal = 0;
var totalImag = 0;
var count = 0;
for (var b = bands[i]; b < bands[i+1]; b++) {
totalReal += Math.abs(fftmod.real[b]);
totalImag += Math.abs(fftmod.imag[b]);
count++;
}
var meanReal = totalReal/count;
var meanImag = totalImag/count;
for (var b = bands[i]; b < bands[i+1]; b++) {
real[b] = fftcar.real[b] * meanReal;
imag[b] = fftcar.imag[b] * meanImag;
}
}
additiveSignal = fftcar.inverse(real, imag);
/*
for (var i = 0; i < additiveSignal.length; i++) {
if (additiveSignal[i] > 0.8) {
additiveSignal[i] = 0.7;
} else if (additiveSignal[i] < -0.8) {
additiveSignal[i] = -0.7;
}
}
*/
} else if ( s.envelope.isActive() ) {
s.generate();
additiveSignal = s.applyEnvelope();
} else if ( v.envelope.isActive() ) {
v.generate();
additiveSignal = v.applyEnvelope();
}
// Set the global osc object
if ( typeof additiveSignal === 'undefined' ) {
additiveSignal = silence;
}
// Flush buffer
output.mozWriteAudio([]);
// Write next audio frame
output.mozWriteAudio(additiveSignal);
var endTime = (new Date()).getTime();
timePerWrite = endTime - startTime;
}
}
</script>
<script target="#keyboard" type="application/processing">
void setup() {
size(1024, 400);
setInterval(audioWriter, bufferTime); // start audioWriter timer
stroke(255);
frameRate(20);
}
void draw() {
background(255);
fill(0)
stroke(255);
if (!s.loaded) {
var loadPercent = s.samples.length / (s.duration * sampleRate);
text("Loading carrier: " + Math.round(loadPercent * 100) + "%", 0, 20);
}
if (!v.loaded) {
var loadPercent = v.samples.length / (v.duration * sampleRate);
text("Loading formant: " + Math.round(loadPercent * 100) + "%", 0, 40);
}
if ( s.loaded && v.loaded ) {
/*
// Draw formant:
stroke(0, 0, 255);
for (int i = 0; i < v.samples.length; i+=100) {
line(i/100, 50-v.samples[i]*50, i/100, 50+v.samples[i]*50);
}
// Draw carrier
stroke(255, 0, 0);
for (int i = 0; i < s.samples.length; i+=100) {
line(i/100, 150-s.samples[i]*50, i/100, 150+s.samples[i]*50);
}
*/
noStroke();
fill(0);
for (var i = 0; i < bufferSize/4; i+= 4) {
if (real[i] && imag[i]) {
rect(i, 100, 2, - fftmod.spectrum[i] * 1000);
}
}
fill(0);
for (var i = 0; i < bufferSize/4; i+= 4) {
if (real[i] && imag[i]) {
rect(i, 200, 2, - fftcar.spectrum[i] * 1000);
}
}
fill(255, 0, 0);
for (var i = 0; i < bufferSize/4; i+= 4) {
if (real[i] && imag[i]) {
rect(i, height, 2, - (Math.sqrt(real[i] * real[i] + imag[i] * imag[i])) / bufferSize * 1000);
}
}
}
}
void keyPressed() {
var semi = undefined;
if ( key == 'z' ) { // C
semi = 0;
}
if ( key == 's' ) { // C#
semi = 1;
}
if ( key == 'x' ) { // D
semi = 2;
}
if ( key == 'd' ) { // D#
semi = 3;
}
if ( key == 'c' ) { // E
semi = 4;
}
if ( key == 'v' ) { // F
semi = 5;
}
if ( key == 'g' ) { // F#
semi = 6;
}
if ( key == 'b' ) { // G
semi = 7;
}
if ( key == 'h' ) { // G#
semi = 8;
}
if ( key == 'n' ) { // A
semi = 9;
}
if ( key == 'j' ) { // A#
semi = 10;
}
if ( key == 'm' ) { // B
semi = 11;
}
if ( key == '=') { // increase octave
oct++;
}
if ( key == '-') { // descrease octave
oct--;
}
if (typeof semi !== 'undefined' && s.loaded && v.loaded) {
s.envelope.noteOn();
s.setFreq(midiNoteFreq[12*oct+semi]);
v.envelope.noteOn();
v.setFreq(midiNoteFreq[12*oct+semi]);
}
if (key == 'w') {
s.envelope.noteOn();
s.setFreq(midiNoteFreq[12*oct]);
}
if (key == 'q') {
v.envelope.noteOn();
v.setFreq(midiNoteFreq[12*oct]);
}
if (key == 'e') {
s.envelope.noteOn();
s.setFreq(midiNoteFreq[12*oct]);
v.envelope.noteOn();
v.setFreq(midiNoteFreq[12*oct]);
}
}
void keyReleased() {
if ( s.envelope.isActive() ) {
s.envelope.noteOff();
s.reset();
}
if ( v.envelope.isActive() ) {
v.envelope.noteOff();
v.reset();
}
}
</script>
<h1>Vocoder</h1>
<canvas id="keyboard" width="200px" height="200px"></canvas>
<h2>Playing sample</h2>
<p><b>Q</b> Play the voice formant sample</p>
<p><b>W</b> Play the carrier synth sample</p>
<p><b>E</b> Play the vocoded sample</p>
<p>Bottom row keys z to m are the white keys of one octave of a piano.</p>
<p>Middle row keys s,d and g,h,j are the black keys of one octave of a piano</p>
<p>- and = decrease or increase the octave</p>
</body>
</html>