Search code examples
algorithmlanguage-agnosticmathgraphicsaudio

How do I visualize audio data?


I would like to have something that looks something like this. Two different colors are not nessesary.

audacity on mac
(source: sourceforge.net)

I already have the audio data (one sample/millisecond) from a stereo wav in two int arrays, one each for left and right channel. I have made a few attempts but they don't look anywhere near as clear as this, my attempts get to spikey or a compact lump.

Any good suggestions? I'm working in c# but psuedocode is ok.

Assume we have

  • a function DrawLine(color, x1, y1, x2, y2)
  • two int arrays with data right[] and left[] of lenght L
  • data values between 32767 and -32768

If you make any other assumptions just write them down in your answer.

for(i = 0; i < L - 1; i++) {
  // What magic goes here?
}

This is how it turned out when I applied the solution Han provided. (only one channel)
alt text http://www.imagechicken.com/uploads/1245877759099921200.jpg


Solution

  • You'll likely have more than 1 sample for each pixel. For each group of samples mapped to a single pixel, you could draw a (vertical) line segment from the minimum value in the sample group to the maximum value. If you zoom in to 1 sample per pixel or less, this doesn't work anymore, and the 'nice' solution would be to display the sinc interpolated values. Because DrawLine cannot paint a single pixel, there is a small problem when the minimum and maximum are the same. In that case you could copy a single pixel image in the desired position, as in the code below:

    double samplesPerPixel = (double)L / _width;
    double firstSample = 0;
    int endSample = firstSample + L - 1;
    for (short pixel = 0; pixel < _width; pixel++)
    {
        int lastSample = __min(endSample, (int)(firstSample + samplesPerPixel));
        double Y = _data[channel][(int)firstSample];
        double minY = Y;
        double maxY = Y;
        for (int sample = (int)firstSample + 1; sample <= lastSample; sample++)
        {
            Y = _data[channel][sample];
            minY = __min(Y, minY);
            maxY = __max(Y, maxY);
        }
        x = pixel + _offsetx;
        y1 = Value2Pixel(minY);
        y2 = Value2Pixel(maxY);
        if (y1 == y2)
        {
            g->DrawImageUnscaled(bm, x, y1);
        }
        else
        {
            g->DrawLine(pen, x, y1, x, y2);
        }
        firstSample += samplesPerPixel;
    }
    

    Note that Value2Pixel scales a sample value to a pixel value (in the y-direction).