0

I'm attempting to draw a wave form from a WAV file based on this answer: https://stackoverflow.com/a/1215472/356635

Unfortunately, it produces a big black square every time. I'm struggling to work out why this is, is anyone able to spot the problem? All values in normalisedData fall between -1 and +1. The test.wav file is a Windows wav file that plays a 5 second alarm sound.

Here's my code:

using System;
using System.Drawing;
using System.IO;

public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        const string rootDir = "C:\\inetpub\\wwwroot\\";
        if (File.Exists(rootDir + "test.png"))
        {
            File.Delete(rootDir + "test.png");
        }

        var stream = new MemoryStream(File.ReadAllBytes(rootDir + "test.wav"));
        var data = stream.ToArray();
        stream.Dispose();
        var normalisedData = FloatArrayFromByteArray(data);

        // Get max value in data
        var maxValue = 0f;
        for (var i = 0; i < normalisedData.Length; i++)
        {
            if (normalisedData[i] > maxValue) maxValue = normalisedData[i];
        }

        // Normalise data
        for (var i = 0; i < normalisedData.Length; i++)
        {
            normalisedData[i] = (data[i] / maxValue) * 100f;
        }       

        // Save picture
        var picture = DrawNormalizedAudio(normalisedData, Color.LimeGreen);
        picture.Save(rootDir + "test.png", System.Drawing.Imaging.ImageFormat.Png);
        picture.Dispose();
    }
    public static Bitmap DrawNormalizedAudio(float[] data, Color color)
    {
        Bitmap bmp;
        bmp = new Bitmap(500,500);

        int BORDER_WIDTH = 5;
        int width = bmp.Width - (2 * BORDER_WIDTH);
        int height = bmp.Height - (2 * BORDER_WIDTH);

        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.Clear(Color.Black);
            Pen pen = new Pen(color);
            int size = data.Length;
            for (int iPixel = 0; iPixel < width; iPixel++)
            {
                // determine start and end points within WAV
                int start = (int)((float)iPixel * ((float)size / (float)width));
                int end = (int)((float)(iPixel + 1) * ((float)size / (float)width));
                float min = float.MaxValue;
                float max = float.MinValue;
                for (int i = start; i < end; i++)
                {
                    float val = data[i];
                    min = val < min ? val : min;
                    max = val > max ? val : max;
                }
                int yMax = BORDER_WIDTH + height - (int)((max + 1) * .5 * height);
                int yMin = BORDER_WIDTH + height - (int)((min + 1) * .5 * height);
                g.DrawLine(pen, iPixel + BORDER_WIDTH, yMax,
                    iPixel + BORDER_WIDTH, yMin);
            }
        }
        return bmp;
    }

    public float[] FloatArrayFromByteArray(byte[] input)
    {
        float[] output = new float[input.Length / 4];
        System.Buffer.BlockCopy(input, 0, output, 0, input.Length);
        return output;
    }
}
Community
  • 1
  • 1
Tom Gullen
  • 61,249
  • 84
  • 283
  • 456

1 Answers1

1

WAV files are in a more complicated format than your code is assuming. They have headers and other metadata that describe the audio. The audio data is usually stored as signed 16-bit or 8-bit integer values, though other formats are possible. You'll either need to find a library that will decode the WAV file into floating point values that you can use in your application, or a use utility to convert the WAV file into raw floating point format files.

Your normalization code also isn't correct. Since the sample values can be negative, you need to find the maximum value of the absolute value of the samples. You shouldn't multiply by 100 because that normalizes them in the range -100,100.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112