Фильтр нижних частот с изменяющейся во времени частотой среза, с питоном - PullRequest
0 голосов
/ 19 сентября 2018

Как применить фильтр нижних частот с частотой среза, изменяющейся линейно (или с более общей кривой, чем линейная) от, например, 10000 Гц до 200 Гц по времени , с numpy / scipy и, возможно, без какой-либо другой библиотеки?

Пример:

  • в 00: 00 000, отсечка нижних частот = 10000 Гц
  • в 00: 05 000, отсечка нижних частот = 5000 Гц
  • в 00: 09 000, отсечка нижних частот = 1000 Гц
  • , затем отсечка остается на частоте 1000 Гц в течение 10 секунд, затем отсечка уменьшается до 200 Гц

Вот как сделать простую передачу нижних частот 100 Гц:

from scipy.io import wavfile
import numpy as np
from scipy.signal import butter, lfilter

sr, x = wavfile.read('test.wav')
b, a = butter(2, 100.0 / sr, btype='low')  # Butterworth
y = lfilter(b, a, x)
wavfile.write('out.wav', sr, np.asarray(y, dtype=np.int16))

но как сделать изменение среза?

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

Ответы [ 2 ]

0 голосов
/ 22 сентября 2018

Одним из сравнительно простых методов является сохранение фильтра фиксированным и модуляция времени сигнала.Например, если время сигнала увеличивается в 10 раз быстрее, низкочастотный диапазон 10 кГц будет действовать как низкочастотный диапазон 1 кГц в стандартное время.

Для этого нам нужно решить простое ОДУ

dy       1
--  =  ----
dt     f(y)

Здесь t это модулированное время y реальное время и f желаемое время отсечки y.

Реализация прототипа:

from __future__ import division
import numpy as np
from scipy import integrate, interpolate
from scipy.signal import butter, lfilter, spectrogram

slack_l, slack = 0.1, 1
cutoff = 50
L = 25

from scipy.io import wavfile
sr, x = wavfile.read('capriccio.wav')
x = x[:(L + slack) * sr, 0]
x = x

# sr = 44100
# x = np.random.normal(size=((L + slack) * sr,))

b, a = butter(2, 2 * cutoff / sr, btype='low')  # Butterworth

# cutoff function
def f(t):
    return (10000 - 1000 * np.clip(t, 0, 9) - 1000 * np.clip(t-19, 0, 0.8)) \
        / cutoff

# and its reciprocal
def fr(_, t):
    return cutoff / (10000 - 1000 * t.clip(0, 9) - 1000 * (t-19).clip(0, 0.8))

# modulate time
# calculate upper end of td first
tdmax = integrate.quad(f, 0, L + slack_l, points=[9, 19, 19.8])[0]
span = (0, tdmax)
t = np.arange(x.size) / sr
tdinfo = integrate.solve_ivp(fr, span, np.zeros((1,)),
                             t_eval=np.arange(0, span[-1], 1 / sr),
                             vectorized=True)
td = tdinfo.y.ravel()
# modulate signal
xd = interpolate.interp1d(t, x)(td)
# and linearly filter
yd = lfilter(b, a, xd)
# modulate signal back to linear time
y = interpolate.interp1d(td, yd)(t[:-sr*slack])

# check
import pylab
xa, ya, z = spectrogram(y, sr)
pylab.pcolor(ya, xa, z, vmax=2**8, cmap='nipy_spectral')
pylab.savefig('tst.png')

wavfile.write('capriccio_vandalized.wav', sr, y.astype(np.int16))

Пример вывода:

Spectrogram of first 25 seconds of BWV 826 Capriccio filtered with a time varying lowpass implemented via time bending.

Спектрограмма первых 25 секунд BWV 826 Каприччио, отфильтрованная с изменяющимся во времени нижним проходом, реализованным посредством изгибания по времени.

0 голосов
/ 22 сентября 2018

вы можете использовать scipy.fftpack.fftfreq и scipy.fftpack.rfft для установки порогов

fft = scipy.fftpack.fft(sound)
freqs = scipy.fftpack.fftfreq(sound.size, time_step)

для time_step Я сделал удвоенную частоту дискретизации звука

fft[(freqs < 200)] = 0

это установило бы все установки всех частот менее 200 Гц на ноль

для меняющегося времени, я бы разделил звук и затем применил бы фильтры.предполагая, что звук имеет частоту дискретизации 44100, фильтр 5000 Гц будет начинаться с выборки 220500 (пять секунд)

10ksound = sound[:220500]
10kfreq = scipy.fftpack.fftreq(10ksound.size, time_step)
10kfft = scipy.fftpack.fft(10ksound)
10kfft[(10kfreqs < 10000)] = 0

, затем для следующего фильтра:

5ksound = sound[220500:396900]
5kfreq = scipy.fftpack.fftreq(10ksound.size, time_step)
5kfft = scipy.fftpack.fft(10ksound)
5kfft[(5kfreqs < 5000)] = 0

и т. д.

edit: чтобы сделать это «скользящим» или постепенным фильтром вместо кусочков, вы могли бы сделать «кусочки» намного меньшими и применить все более большие пороги частоты к соответствующему кусочку (5000 -> 5001 -> 5002)

...