Sunday, January 3, 2016

Real Time Visualization of a Spectrogram

From www.smartjava.org.

This code needs to be added to a Tomcat server to work. This allows the microphone in Chrome to be connected. See Real Time Visualization of Raw Sound from Microphone Input using HTML5.

Some notes on this, that not all frequencies are shown in this. Also, the shift method, copies what is on the canvas and then pastes it a few pixels over, based on the shift parameters.

<html>
<head>
<script type="text/javascript">

function interpolate(x, x0, xf, y0, yf) {
    // (x - x0)/(xf - x0) = (y - y0)/(yf - y0)
    // y = ( (x-x0)/(xf-x0) ) * (yf-y0) + y0
    return Math.floor( ( (x-x0)/(xf-x0) ) * (yf-y0) + y0 );
}

// colors:['#000000', '#ff0000', '#ffff00', '#ffffff']
// positions:[0, 64-1, 192-1, 255]
// 0 = parseInt('00', 16), 256-1 = parseInt('ff', 16)
function findColor(val) {
    var r, rDec, g, gDec, b, bDec;
    if(val < 64) {
        rDec = interpolate(val, 0, 64-1, 0, 256-1);
        r = rDec.toString(16);
        g = '00';
        b = '00';
    } else if (val < 192) {
        r = 'ff';
        gDec = interpolate(val, 64, 192-1, 0, 256-1); 
        g = gDec.toString(16);
        b = '00';
    } else {
        r = 'ff';
        g = 'ff';
        bDec = interpolate(val, 192, 256-1, 0, 256-1); 
        b = bDec.toString(16);
    }
    return '#'+r+g+b;
}

function canvasFillCoordinate(leftX, topY, val) {
    getCanvas().getContext('2d').fillStyle = findColor(val);
    getCanvas().getContext('2d').fillRect(leftX, topY, 1, 1);
}

function canvasInitialize(width, height) {
    getCanvas().getContext('2d').strokeStyle='#000000';
    
    // Set canvas parameters
    getCanvas().width = width;
    getCanvas().height = height;

    // Outline
    getCanvas().getContext('2d').clearRect(0,0,width,height);
    getCanvas().getContext('2d').rect(0,0,width,height);
    getCanvas().getContext('2d').stroke();
}

function onSuccess(stream) {
    // stream -> mediaSource -> javascriptNode -> destination
    var context = new webkitAudioContext();
    var mediaStreamSource = context.createMediaStreamSource(stream);
    var analyser = context.createAnalyser();
    analyser.smoothingTimeConstant = 0.3;
    analyser.fftSize = 2048;
    var javascriptNode = context.createScriptProcessor(2048, 1, 1);
    mediaStreamSource.connect(analyser);
    analyser.connect(javascriptNode);
    javascriptNode.connect(context.destination);
    
    javascriptNode.onaudioprocess = createProcessBuffer(analyser);
}

function createProcessBuffer(analyser) {
    canvasInitialize(1024,1024);
    var time = 0;
    return function processBuffer() {
        time = time + 1;
        if(time > 1000) {
            canvasInitialize(1024,1024);
            time = 0;
        }
    
        var fftData =  new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(fftData);
    
        var average = 0;
        var min = 200;
        var max = 50;
        for(var freq=0; freq<fftData.length; freq++) {
            var sample = fftData[freq];
            canvasFillCoordinate(time, freq, sample);
            average = average + sample;
        }
        average = average/fftData.length;
        getLog().innerHTML = 'FFT Length:'+fftData.length + ' Average:' + average + '\n<br>';
    };
}

function onError() {
    alert('Error');
}

function getLog() {
    return document.getElementById('mylog');
}

function getCanvas() {
    return document.getElementById('mycanvas');
}    
  
function documentReady() {
    var dataObject = {video: false, audio: true}; // dataObject.video, dataObject.audio
    if(navigator.getUserMedia) {
        navigator.getUserMedia(dataObject, onSuccess, onError);
    } else if(navigator.webkitGetUserMedia) {
        navigator.webkitGetUserMedia(dataObject, onSuccess, onError);
    }
}
</script>
</head>
<body onload="documentReady();">
   <canvas id="mycanvas"></canvas>
   <div id="mylog"></div>
</body>
</html>


This post was reposted from scottizu.wordpress.com, originally written on June 24th, 2014.

I was curious to see if this code still worked and how quickly I could get up and running with this after over a year. It turns out the code froze after drawing the spectrogram for a few seconds, after I started making chirping sounds to see the effect on the real time visualization. A good sign was that I was able to get up and running fairly quickly. I have updated the code and it seems to work better. Here's what I did.

To run the Tomcat 7.0 server, I right clicked the task bar, Task Manager, Services Tab, Hit the Services Button, found Apache Tomcat 7.0, selected it, clicked start (this all worked because I had previously installed Apache Tomcat on my machine).

To add the file, I created a new file, "spectrogram.html", edited the file to add the code above, then dropped it into the folder "C:\Program Files\Apache Software Foundation\Tomcat 7.0\webapps\ROOT" which is Tomcat 7.0's root directory.

I opened Google Chrome and went to http://localhost:8080/spectrogram.html.

Related to A Tutorial for Real Time Pitch Detection.

No comments:

Post a Comment