【问题标题】:How to convert a .wav file to a spectrogram in python3如何在python3中将.wav文件转换为频谱图
【发布时间】:2017-11-30 22:18:57
【问题描述】:

我正在尝试从 python3 中的 .wav 文件创建频谱图。

我希望最终保存的图像与此图像相似:

我尝试了以下方法:

此堆栈溢出帖子: Spectrogram of a wave file

这篇文章在某种程度上奏效了。运行后,我得到了

但是,此图表不包含我需要的颜色。我需要一个有颜色的频谱图。我尝试修改此代码以尝试添加颜色,但是在花费大量时间和精力之后,我无法弄清楚!

然后我尝试了this 教程。

当我尝试运行此代码时出现错误 TypeError: 'numpy.float64' object cannot be compiled as an integer.

第 17 行:

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

我尝试通过强制转换来修复它

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

我也试过了

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

然而这些最终都没有奏效。

我真的很想知道如何将我的 .wav 文件转换为带颜色的频谱图,以便我分析它们!任何帮助将不胜感激!!!!!!

如果您希望我提供有关我的 python 版本、我尝试过的内容或想要实现的目标的更多信息,请告诉我。

【问题讨论】:

  • Audacity 是一款出色的音频应用程序,它可以显示输入音频文件的实时频谱图...... sonic-visualiser 是另一个用于此目的的重要音频工具...... 他们会确认什么是正确的频谱图您的音频应该看起来像...了解如何编写一个我建议您花时间了解傅立叶变换的概念...只是在某个库上苦苦挣扎不会让您欣赏将数据从时域转换为频率域...玩得开心,欢迎来到 SO

标签: python numpy audio matplotlib spectrogram


【解决方案1】:

使用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()

在尝试执行此操作之前,请确保您的 wav 文件是单声道(单声道)而不是立体声(双声道)。我强烈建议阅读https://docs.scipy.org/doc/scipy- 0.19.0/reference/generated/scipy.signal.spectrogram.html 的 scipy 文档。

plt.pcolormesh 放在plt.imshow 之前似乎可以解决一些问题,正如@Davidjb 指出的那样,如果出现解包错误,请按照下面@cgnorthcutt 的步骤操作。

【讨论】:

  • 对我来说,这只是显示了一个空白图表。我将plt.imshow(spectrogram) 移动到plt.pcolormesh(...) 之后,然后它起作用了。知道为什么吗?
  • 如果您无法使其正常工作,请尝试两件事:(1) 删除 plt.imshow(..) 和 (2) 改为在 np.log(spectrogram) 上尝试 plt.pcolormesh
  • 我得到ValueError: too many values to unpack (expected 2)plt.pcolormesh
  • @MartinThoma 你检查你的samples.shape了吗?
  • @MartinThoma,我也遇到了同样的问题;我的问题是我使用的是立体声 wav 文件而不是单声道。
【解决方案2】:
import os
import wave

import pylab
def graph_spectrogram(wav_file):
    sound_info, frame_rate = get_wav_info(wav_file)
    pylab.figure(num=None, figsize=(19, 12))
    pylab.subplot(111)
    pylab.title('spectrogram of %r' % wav_file)
    pylab.specgram(sound_info, Fs=frame_rate)
    pylab.savefig('spectrogram.png')
def get_wav_info(wav_file):
    wav = wave.open(wav_file, 'r')
    frames = wav.readframes(-1)
    sound_info = pylab.fromstring(frames, 'int16')
    frame_rate = wav.getframerate()
    wav.close()
    return sound_info, frame_rate

对于A Capella Science - Bohemian Gravity!,这给出了:

使用graph_spectrogram(path_to_your_wav_file)。 我不记得我拿这个 sn-p 的博客。每当我再次看到它时,我会添加链接。

【讨论】:

  • 能否请您添加一些注释如何解释图像?什么是轴?颜色是什么意思?
  • sound_info = pylab.fromstring(frames, 'int16') 这应该替换为sound_info = pylab.frombuffer(frames, 'int16')
  • 如果没有 pylab b/c 会重做 pylab 是邪恶的
  • pylab 方法(this)对我有用。不同答案的 scipy 方法看起来很奇怪。
【解决方案3】:

我已经修复了您在http://www.frank-zalkow.de/en/code-snippets/create-audio-spectrograms-with-python.html 中遇到的错误
此实现更好,因为您可以更改binsize(例如binsize=2**8

import numpy as np
from matplotlib import pyplot as plt
import scipy.io.wavfile as wav
from numpy.lib import stride_tricks

""" short time fourier transform of audio signal """
def stft(sig, frameSize, overlapFac=0.5, window=np.hanning):
    win = window(frameSize)
    hopSize = int(frameSize - np.floor(overlapFac * frameSize))

    # zeros at beginning (thus center of 1st window should be for sample nr. 0)   
    samples = np.append(np.zeros(int(np.floor(frameSize/2.0))), sig)    
    # cols for windowing
    cols = np.ceil( (len(samples) - frameSize) / float(hopSize)) + 1
    # zeros at end (thus samples can be fully covered by frames)
    samples = np.append(samples, np.zeros(frameSize))

    frames = stride_tricks.as_strided(samples, shape=(int(cols), frameSize), strides=(samples.strides[0]*hopSize, samples.strides[0])).copy()
    frames *= win

    return np.fft.rfft(frames)    

""" scale frequency axis logarithmically """    
def logscale_spec(spec, sr=44100, factor=20.):
    timebins, freqbins = np.shape(spec)

    scale = np.linspace(0, 1, freqbins) ** factor
    scale *= (freqbins-1)/max(scale)
    scale = np.unique(np.round(scale))

    # create spectrogram with new freq bins
    newspec = np.complex128(np.zeros([timebins, len(scale)]))
    for i in range(0, len(scale)):        
        if i == len(scale)-1:
            newspec[:,i] = np.sum(spec[:,int(scale[i]):], axis=1)
        else:        
            newspec[:,i] = np.sum(spec[:,int(scale[i]):int(scale[i+1])], axis=1)

    # list center freq of bins
    allfreqs = np.abs(np.fft.fftfreq(freqbins*2, 1./sr)[:freqbins+1])
    freqs = []
    for i in range(0, len(scale)):
        if i == len(scale)-1:
            freqs += [np.mean(allfreqs[int(scale[i]):])]
        else:
            freqs += [np.mean(allfreqs[int(scale[i]):int(scale[i+1])])]

    return newspec, freqs

""" plot spectrogram"""
def plotstft(audiopath, binsize=2**10, plotpath=None, colormap="jet"):
    samplerate, samples = wav.read(audiopath)

    s = stft(samples, binsize)

    sshow, freq = logscale_spec(s, factor=1.0, sr=samplerate)

    ims = 20.*np.log10(np.abs(sshow)/10e-6) # amplitude to decibel

    timebins, freqbins = np.shape(ims)

    print("timebins: ", timebins)
    print("freqbins: ", freqbins)

    plt.figure(figsize=(15, 7.5))
    plt.imshow(np.transpose(ims), origin="lower", aspect="auto", cmap=colormap, interpolation="none")
    plt.colorbar()

    plt.xlabel("time (s)")
    plt.ylabel("frequency (hz)")
    plt.xlim([0, timebins-1])
    plt.ylim([0, freqbins])

    xlocs = np.float32(np.linspace(0, timebins-1, 5))
    plt.xticks(xlocs, ["%.02f" % l for l in ((xlocs*len(samples)/timebins)+(0.5*binsize))/samplerate])
    ylocs = np.int16(np.round(np.linspace(0, freqbins-1, 10)))
    plt.yticks(ylocs, ["%.02f" % freq[i] for i in ylocs])

    if plotpath:
        plt.savefig(plotpath, bbox_inches="tight")
    else:
        plt.show()

    plt.clf()

    return ims

ims = plotstft(filepath)

【讨论】:

    【解决方案4】:

    您可以使用librosa 来满足您的 mp3 频谱图需求。这是我找到的一些代码,感谢Parul Pandey from medium。我用的代码是这样的,

    # Method described here https://stackoverflow.com/questions/15311853/plot-spectogram-from-mp3
    
    import librosa
    import librosa.display
    from pydub import AudioSegment
    import matplotlib.pyplot as plt
    from scipy.io import wavfile
    from tempfile import mktemp
    
    def plot_mp3_matplot(filename):
        """
        plot_mp3_matplot -- using matplotlib to simply plot time vs amplitude waveplot
        
        Arguments:
        filename -- filepath to the file that you want to see the waveplot for
        
        Returns -- None
        """
        
        # sr is for 'sampling rate'
        # Feel free to adjust it
        x, sr = librosa.load(filename, sr=44100)
        plt.figure(figsize=(14, 5))
        librosa.display.waveplot(x, sr=sr)
    
    def convert_audio_to_spectogram(filename):
        """
        convert_audio_to_spectogram -- using librosa to simply plot a spectogram
        
        Arguments:
        filename -- filepath to the file that you want to see the waveplot for
        
        Returns -- None
        """
        
        # sr == sampling rate 
        x, sr = librosa.load(filename, sr=44100)
        
        # stft is short time fourier transform
        X = librosa.stft(x)
        
        # convert the slices to amplitude
        Xdb = librosa.amplitude_to_db(abs(X))
        
        # ... and plot, magic!
        plt.figure(figsize=(14, 5))
        librosa.display.specshow(Xdb, sr = sr, x_axis = 'time', y_axis = 'hz')
        plt.colorbar()
        
    # same as above, just changed the y_axis from hz to log in the display func    
    def convert_audio_to_spectogram_log(filename):
        x, sr = librosa.load(filename, sr=44100)
        X = librosa.stft(x)
        Xdb = librosa.amplitude_to_db(abs(X))
        plt.figure(figsize=(14, 5))
        librosa.display.specshow(Xdb, sr = sr, x_axis = 'time', y_axis = 'log')
        plt.colorbar()    
    

    干杯!

    【讨论】:

    • 缺少必要的 import librosaimport librosa.display 导入。
    【解决方案5】:

    上面的初学者回答非常好。我没有 50 个代表,所以我无法评论它,但如果你想要频域中的正确幅度,stft 函数应该如下所示:

    import numpy as np
    from matplotlib import pyplot as plt
    import scipy.io.wavfile as wav
    from numpy.lib import stride_tricks
    
    """ short time fourier transform of audio signal """
    def stft(sig, frameSize, overlapFac=0, window=np.hanning):
        win = window(frameSize)
        hopSize = int(frameSize - np.floor(overlapFac * frameSize))
    
        # zeros at beginning (thus center of 1st window should be for sample nr. 0)   
        samples = np.append(np.zeros(int(np.floor(frameSize/2.0))), sig)    
        # cols for windowing
        cols = np.ceil( (len(samples) - frameSize) / float(hopSize)) + 1
        # zeros at end (thus samples can be fully covered by frames)
        samples = np.append(samples, np.zeros(frameSize))
    
        frames = stride_tricks.as_strided(samples, shape=(int(cols), frameSize), strides=(samples.strides[0]*hopSize, samples.strides[0])).copy()
        frames *= win
        
        fftResults = np.fft.rfft(frames)
        windowCorrection = 1/(np.sum(np.hanning(frameSize))/frameSize) #This is amplitude correct (1/mean(window)). Energy correction is 1/rms(window)
        FFTcorrection = 2/frameSize
        scaledFftResults = fftResults*windowCorrection*FFTcorrection
    
        return scaledFftResults
    

    【讨论】:

      猜你喜欢
      • 2013-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-16
      • 1970-01-01
      • 1970-01-01
      • 2019-12-31
      • 1970-01-01
      相关资源
      最近更新 更多