pythonnumpyaudiomatplotlibspectrogram

How to convert a .wav file to a spectrogram in python3


I am trying to create a spectrogram from a .wav file in python3.

I want the final saved image to look similar to this image:

I have tried the following:

This stack overflow post: Spectrogram of a wave file

This post worked, somewhat. After running it, I got

However, This graph does not contain the colors that I need. I need a spectrogram that has colors. I tried to tinker with this code to try and add the colors however after spending significant time and effort on this, I couldn't figure it out!

I then tried this tutorial.

This code crashed(on line 17) when I tried to run it with the error TypeError: 'numpy.float64' object cannot be interpreted as an integer.

line 17:

samples = np.append(np.zeros(np.floor(frameSize/2.0)), sig)

I tried to fix it by casting

samples = int(np.append(np.zeros(np.floor(frameSize/2.0)), sig))

and I also tried

samples = np.append(np.zeros(int(np.floor(frameSize/2.0)), sig))    

However neither of these worked in the end.

I would really like to know how to convert my .wav files to spectrograms with color so that I can analyze them! Any help would be appreciated!!!!!

Please tell me if you want me to provide any more information about my version of python, what I tried, or what I want to achieve.


Solution

  • Use scipy.signal.spectrogram.

    import matplotlib.pyplot as plt
    from scipy import signal
    from scipy.io import wavfile
    
    sample_rate, samples = wavfile.read('path-to-mono-audio-file.wav')
    frequencies, times, spectrogram = signal.spectrogram(samples, sample_rate)
    
    plt.pcolormesh(times, frequencies, spectrogram)
    plt.imshow(spectrogram)
    plt.ylabel('Frequency [Hz]')
    plt.xlabel('Time [sec]')
    plt.show()
    

    Be sure that your wav file is mono (single channel) and not stereo (dual channel) before trying to do this. I highly recommend reading the scipy documentation at https://docs.scipy.org/doc/scipy- 0.19.0/reference/generated/scipy.signal.spectrogram.html.

    Putting plt.pcolormesh before plt.imshow seems to fix some issues, as pointed out by @Davidjb, and if unpacking error occurs, follow the steps by @cgnorthcutt below.

    Alternate graph: Note that we log spectogram values, so that the frequency spectrum is clearer to see. Also we don't need plt.imshow call ( thanks to @cgnorthcutt )

    plt.pcolormesh(times, frequencies, np.log(spectrogram))
    #plt.imshow(spectrogram)
    plt.ylabel('Frequency [Hz]')
    plt.xlabel('Time [sec]')
    plt.show()