Выяснение объема частотного диапазона в PyAudio / FFT - PullRequest
2 голосов
/ 09 июня 2019

Моя цель - написать программу на Python, которая записывает звук с микрофона и непрерывно печатает громкость определенного частотного диапазона (используя полосовой фильтр).

Ниже приведен код, который у меня есть на данный момент.Я часами гуглил, чтобы узнать больше об этой теме или примерах кода, но я не нашел никакой полезной информации (которую я могу понять) или подходящих примеров кодов.К сожалению, мои знания в БПФ очень ограничены.

В настоящее время у меня есть две проблемы:

Проблема 1: Полосовой фильтр не работает,Я установил его на 300 Гц. 500 Гц, но он ничего не фильтрует.В качестве примера я добавил код, который отображает основную частоту сигнала, чтобы я мог видеть, работает ли фильтр (для тестирования я воспроизводю звуки с помощью приложения генератора частоты).Обнаружение частоты работает отлично, но сигнал не фильтруется до 300,500 Гц.

Проблема 2: Как определить громкость?Моя идея заключалась в том, чтобы использовать noiselevel = np.average(fftData), чтобы узнать среднюю громкость по всем частотам в массиве fftData.Но это не работает: вывод кажется произвольным и не реагирует на громкие звуки, воспроизводимые в микрофон.

Буду благодарен за любую помощь.Большое спасибо!

#!/usr/bin/env python3

import pyaudio
import numpy as np
from scipy.signal import butter, sosfilt

def butter_bandpass(lowcut, highcut, fs, order=5):
        nyq = 0.5 * fs
        low = lowcut / nyq
        high = highcut / nyq
        sos = butter(order, [low, high], analog=False, btype='band', output='sos')
        return sos

def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
        sos = butter_bandpass(lowcut, highcut, fs, order=order)
        y = sosfilt(sos, data)
        return y

CHUNK = 2*4096 # number of data points to read at a time
RATE = 48000 # time resolution of the recording device (Hz)
DEVICE = 0 # default

p = pyaudio.PyAudio()

stream=p.open(format=pyaudio.paInt16,channels=1,rate=RATE,input=True, input_device_index=DEVICE,
              frames_per_buffer=CHUNK)

while True:
    indata = np.fromstring(stream.read(CHUNK),dtype=np.int16)

    # Remove everything except 300Hz..500Hz
    # TODO: does not work
    #indata = butter_bandpass_filter(indata, 300, 500, RATE, order=5)

    # Take the fft and square each value
    fftData=abs(np.fft.rfft(indata))**2

    # TODO: find out volume
    noiselevel = np.average(fftData)
    print("Volume of 300Hz..500Hz: %f" % (noiselevel))

    # JUST FOR TESTING: Find out frequency (to see if band pass works correctly)
    # find the maximum
    which = fftData[1:].argmax() + 1
    # use quadratic interpolation around the max
    if which != len(fftData)-1:
        y0,y1,y2 = np.log(fftData[which-1:which+2:])
        x1 = (y2 - y0) * .5 / (2 * y1 - y2 - y0)
        # find the frequency and output it
        thefreq = (which+x1)*RATE/CHUNK
        print("Frequency: %f Hz." % (thefreq))
    else:
        thefreq = which*RATE/CHUNK
        print("Frequency: %f Hz." % (thefreq))
    # END <JUST FOR TESTING>

stream.close()
p.terminate()

ОБНОВЛЕНИЕ: Вот моя вторая попытка, без фильтров:

while True:
    indata = np.fromstring(stream.read(CHUNK),dtype=np.int16)

    # Get volume of 440 Hz

    fftData=np.fft.rfft(indata)
    freqs = np.fft.rfftfreq(fftData.size)

    idx = (np.abs(freqs-440)).argmin()

    volume = np.abs(fftData[idx]) # does not work
    print(volume)
...