Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting a WAV file to a spectrogram

Hi im very new to this thing so please bear with me. I am trying to convert a WAV file to a spectrogram but arent sure how to begin with. I read on something that says to read the PCM data(which i think is my WAV file) and store it in an array in the WavReader class before apply the FFT on it and converting it to GUI. Im currently using Naudio to achieve this but could not find anything that shows how to convert the WAV file to a spectrogram. Thanks

Edit : I found out about converting PCM to FFT with Naudio and im stuck.

     using (var reader = new AudioFileReader("test1.wav"))
        {
          // test1.wav is my file to process
          // test0.wav is my temp file

            IWaveProvider stream16 = new WaveFloatTo16Provider(reader);
            using (WaveFileWriter converted = new WaveFileWriter("test0.wav", stream16.WaveFormat))
            {
                // buffer length needs to be a power of 2 for FFT to work nicely
                // however, make the buffer too long and pitches aren't detected fast enough
                // successful buffer sizes: 8192, 4096, 2048, 1024
                // (some pitch detection algorithms need at least 2048)
                byte[] buffer = new byte[8192];
                int bytesRead;
                do
                {
                    bytesRead = stream16.Read(buffer, 0, buffer.Length);
                    converted.WriteData(buffer, 0, bytesRead);
                } while (bytesRead != 0 && converted.Length < reader.Length);
            }
        }

Edit : I would also like to know if it is possible to compare 2 spectrograms of 2 different files programmatically.

like image 313
Derrick Peh Avatar asked Dec 27 '22 01:12

Derrick Peh


1 Answers

You could also use BASS.NET library which natively provides all these features and is free.

The Visuals.CreateSpectrum3DVoicePrint Method does exactly that.

Feel free to ask for assistance if you're having a hard time using it.

EDIT : here's a quick and dirty sample

enter image description here

public partial class Form1 : Form
{
    private int _handle;
    private int _pos;
    private BASSTimer _timer;
    private Visuals _visuals;

    public Form1()
    {
        InitializeComponent();
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        bool spectrum3DVoicePrint = _visuals.CreateSpectrum3DVoicePrint(_handle, pictureBox1.CreateGraphics(),
                                                                        pictureBox1.Bounds, Color.Cyan, Color.Green,
                                                                        _pos, false, true);
        _pos++;
        if (_pos >= pictureBox1.Width)
        {
            _pos = 0;
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        string file = "..\\..\\mysong.mp3";
        if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, Handle))
        {
            _handle = Bass.BASS_StreamCreateFile(file, 0, 0, BASSFlag.BASS_DEFAULT);

            if (Bass.BASS_ChannelPlay(_handle, false))
            {
                _visuals = new Visuals();
                _timer = new BASSTimer((int) (1.0d/10*1000));
                _timer.Tick += timer_Tick;
                _timer.Start();
            }
        }
    }
}

EDIT 2

You can provide a file name but you can also provide your own audio data using the other overload that accepts an IntPtr or use Bass.BASS_StreamCreatePush with Bass.BASS_StreamPutData.

Regarding comparing spectrograms you could do the following :

  • Resize the image to a smaller size, reduce information by dithering it to 8-bit (with a good algorithm however)
  • Compare the two images

However for comparing audio data I'd strongly suggest you to use fingerprints, it roughly does that but is much more robust than my suggestion.

Here's a fingerprinting library that is free to use :

http://www.codeproject.com/Articles/206507/Duplicates-detector-via-audio-fingerprinting

Not entirely sure it would work for small samples, though.

EDIT 3

I'm afraid I can't find the link where I've read that but that's what they do: reducing data and comparing images such as the example below (last image):

(note : not to compare at all with image 1, it's something else but just to show why using a lower resolution might give better yields)

enter image description here

(from http://blog.echonest.com/post/545323349/the-echo-nest-musical-fingerprint-enmfp)

Now a very basic explanation of the process:

Comparison source A:

enter image description here

Comparison source B: (I've just changed a region of A)

enter image description here

Comparison result:

(done with Paint.Net by adding the former images as layers and setting 2nd layer blending to difference instead of normal)

enter image description here

If the fingerprints were to be identical the resulting image would be completely black.

And by reducing data to a 8-bit image you are easing the comparison process but keep in mind you will need a good dithering algorithm.

This is one is quite good :

http://www.codeproject.com/Articles/66341/A-Simple-Yet-Quite-Powerful-Palette-Quantizer-in-C

Well it's not on par with Photoshop or Hypersnap's one (which IMO is exceptional) but that might be enough for the task.

And avoid at all costs Floyd–Steinberg dithering or something that does error diffusion.

Here some attempts on creating dithering algorithms : http://bisqwit.iki.fi/story/howto/dither/jy/

Take this with caution as I'm not an expert in the field but that's roughly how it's done.

Go to https://dsp.stackexchange.com/ and ask a few questions there, you might get useful hints on achieving this.

like image 72
aybe Avatar answered Jan 13 '23 19:01

aybe