EnsekiTT Blog

EnsekiTTが書くブログです。

PyAudioでマイクから入ってきた音にScipyでフィルタをかける話

つまりなにしたの?

PyAudioでマイクから入ってきた音にScipyのフィルタをかけてそのまま再生してみた。

f:id:ensekitt:20181225184330j:plain
PyAudioでマイクから入ってきた音にScipyでフィルタをかける話

環境

 % python
Python 3.6.4 (default, Mar  2 2018, 00:45:54)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyaudio
>>> pyaudio.__version__
'0.2.11'
>>> import scipy
>>> scipy.__version__

大まかな方針

前回Callback方式でデータをそのまま再生してみたので、
その再生する際のデータに途中でフィルタを掛けて見ることとする。
ここで適用したいフィルタは
Signal processing (scipy.signal) — SciPy v1.2.0 Reference Guide
ここのFilter designと書かれているもの。

コード

import pyaudio
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
import struct
import time

class AudioFilter():
    def __init__(self):
        # オーディオに関する設定
        self.p = pyaudio.PyAudio()
        self.channels = 1 # ステレオの場合は2
        self.rate = 48000
        self.dt = 1/self.rate
        self.format = pyaudio.paInt16
        self.stream = self.p.open(
                        format=self.format,
                        channels=self.channels,
                        rate=self.rate,
                        output=True,
                        input=True,
                        stream_callback=self.callback)

        # フィルタの設計を行っているところ(1500Hzで4001次のローパスフィルタをかけてみている)
        self.filter1 = signal.firwin(numtaps=4000, cutoff=1500, fs=self.rate)

    # コールバック関数(再生が必要なときに呼び出される)
    def callback(self, in_data, frame_count, time_info, status):
        amp = np.fromstring(in_data, np.int16)
        amp[::2] = np.array(signal.lfilter(self.filter1, 1, amp[::2]))
        amp[1::2] = np.array(signal.lfilter(self.filter1, 1, amp[1::2]))
        amp = np.reshape(amp, (frame_count*self.channels, 1))
        out_data = amp.astype(np.int16).tostring()
        return (out_data, pyaudio.paContinue)

    def close(self):
        self.p.terminate()

if __name__ == "__main__":
    # AudioFilterのインスタンスを作る場所
    af = AudioFilter()

    # ストリーミングを始める場所
    af.stream.start_stream()

    # ノンブロッキングなので好きなことをしていていい場所
    while af.stream.is_active():
        time.sleep(0.1)

    # ストリーミングを止める場所
    af.stream.stop_stream()
    af.stream.close()
    af.close()

FIRフィルタの使い方についてはここを参照
scipy.signal.firwin — SciPy v0.15.1 Reference Guide

実行方法

% python filter.py
クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。