Python リアルタイム音声処理: pydubとPyAudioの組み合わせ

## はじめに
リアルタイム音声処理は、音声通話アプリケーション、ライブパフォーマンス、音声認識システムなど、多くの分野で重要な役割を果たしています。この記事では、Pythonの音声処理ライブラリpydubと、オーディオI/OライブラリPyAudioを組み合わせて、リアルタイムの音声処理システムを構築する方法を紹介します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/1b84f3cf-7d6f-e531-bdb9-1b00793b63da.png)

## 目次

1. 環境設定
2. コードの全体像
3. 主要コンポーネントの解説
4. 実行方法と注意点
5. カスタマイズと拡張の可能性
6. まとめと今後の展望

## 1. 環境設定

まず、必要なライブラリをインストールします：

```bash
pip install pyaudio pydub numpy matplotlib scipy
```

注意: PyAudioのインストールは環境によって追加の設定が必要な場合があります。特に、Windowsユーザーは適切なバージョンのwheelファイルを手動でインストールする必要があるかもしれません。

## 2. コードの全体像

以下が、リアルタイム音声処理システムの完全なコードです：

```python
import pyaudio
import numpy as np
from pydub import AudioSegment
import io
from pydub.effects import low_pass_filter, high_pass_filter
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.fftpack import fft, ifft
from scipy.signal import resample
import threading

CHUNK = 1024
FORMAT = pyaudio.paFloat32
CHANNELS = 1
RATE = 44100

p = pyaudio.PyAudio()

# グローバル変数
audio_data = np.zeros(CHUNK)
noise_profile = None

def apply_effects(audio):
    # ローパスフィルター
    audio = low_pass_filter(audio, 1000)
    # ハイパスフィルター
    audio = high_pass_filter(audio, 300)
    return audio

def noise_reduction(audio_data):
    global noise_profile
    
    # ノイズプロファイルの更新（最初の数フレーム）
    if noise_profile is None:
        noise_profile = np.abs(fft(audio_data))
        return audio_data
    
    # スペクトル減算
    spectrum = fft(audio_data)
    noise_reduction_factor = 0.9
    reduced_spectrum = spectrum - noise_reduction_factor * noise_profile
    reduced_spectrum = np.maximum(reduced_spectrum, 0)
    
    return np.real(ifft(reduced_spectrum))

def pitch_shift(audio_data, semitones):
    factor = 2 ** (semitones / 12)
    stretched = resample(audio_data, int(len(audio_data) / factor))
    return resample(stretched, len(audio_data))

def process_audio(audio_data):
    # NumPy配列をpydubのAudioSegmentに変換
    audio = AudioSegment(
        audio_data.tobytes(), 
        frame_rate=RATE,
        sample_width=audio_data.dtype.itemsize, 
        channels=CHANNELS
    )
    
    # エフェクトを適用
    processed_audio = apply_effects(audio)
    
    # ノイズリダクション
    processed_audio = noise_reduction(np.array(processed_audio.get_array_of_samples()).astype(np.float32))
    
    # ピッチシフト
    processed_audio = pitch_shift(processed_audio, semitones=2)
    
    return processed_audio

def callback(in_data, frame_count, time_info, status):
    global audio_data
    audio_data = np.frombuffer(in_data, dtype=np.float32)
    processed_data = process_audio(audio_data)
    return (processed_data.astype(np.float32).tobytes(), pyaudio.paContinue)

# 音声ストリームの設定
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                output=True,
                frames_per_buffer=CHUNK,
                stream_callback=callback)

# 可視化の設定
fig, ax = plt.subplots()
x = np.arange(0, CHUNK)
line, = ax.plot(x, np.random.rand(CHUNK))

def update_plot(frame):
    global audio_data
    line.set_ydata(audio_data)
    return line,

# アニメーションの設定（修正後）
ani = FuncAnimation(fig, update_plot, blit=True, cache_frame_data=False)

# 音声処理スレッド
def audio_processing_thread():
    stream.start_stream()
    while stream.is_active():
        pass

# メイン処理
if __name__ == "__main__":
    processing_thread = threading.Thread(target=audio_processing_thread)
    processing_thread.start()

    try:
        plt.show()
    except KeyboardInterrupt:
        stream.stop_stream()
        stream.close()
        p.terminate()
        processing_thread.join()
```

## 3. 主要コンポーネントの解説

### 3.1 基本設定

```python
CHUNK = 1024
FORMAT = pyaudio.paFloat32
CHANNELS = 1
RATE = 44100
```

これらの定数は音声ストリームの基本的なパラメータを設定します。CHUNKはバッファサイズ、FORMATはサンプルフォーマット、CHANNELSはチャンネル数（モノラル）、RATEはサンプリングレートです。

### 3.2 エフェクト適用

```python
def apply_effects(audio):
    audio = low_pass_filter(audio, 1000)
    audio = high_pass_filter(audio, 300)
    return audio
```

この関数では、pydubのエフェクトを使用してローパスフィルターとハイパスフィルターを適用しています。

### 3.3 ノイズリダクション

```python
def noise_reduction(audio_data):
    global noise_profile
    
    if noise_profile is None:
        noise_profile = np.abs(fft(audio_data))
        return audio_data
    
    spectrum = fft(audio_data)
    noise_reduction_factor = 0.9
    reduced_spectrum = spectrum - noise_reduction_factor * noise_profile
    reduced_spectrum = np.maximum(reduced_spectrum, 0)
    
    return np.real(ifft(reduced_spectrum))
```

この関数では、シンプルなスペクトル減算法を使用してノイズリダクションを行っています。最初のフレームでノイズプロファイルを作成し、その後のフレームでそのプロファイルを使用してノイズを削減します。

### 3.4 ピッチシフト

```python
def pitch_shift(audio_data, semitones):
    factor = 2 ** (semitones / 12)
    stretched = resample(audio_data, int(len(audio_data) / factor))
    return resample(stretched, len(audio_data))
```

この関数では、scipy.signalのresample関数を使用して、音声のピッチを変更しています。

### 3.5 メインの処理ループ

```python
def process_audio(audio_data):
    audio = AudioSegment(
        audio_data.tobytes(), 
        frame_rate=RATE,
        sample_width=audio_data.dtype.itemsize, 
        channels=CHANNELS
    )
    
    processed_audio = apply_effects(audio)
    processed_audio = noise_reduction(np.array(processed_audio.get_array_of_samples()).astype(np.float32))
    processed_audio = pitch_shift(processed_audio, semitones=2)
    
    return processed_audio
```

この関数が音声処理の中心となります。入力された音声データに対して、エフェクト適用、ノイズリダクション、ピッチシフトを順に適用します。

### 3.6 可視化

```python
fig, ax = plt.subplots()
x = np.arange(0, CHUNK)
line, = ax.plot(x, np.random.rand(CHUNK))

def update_plot(frame):
    global audio_data
    line.set_ydata(audio_data)
    return line,

ani = FuncAnimation(fig, update_plot, blit=True, cache_frame_data=False)
```

matplotlibを使用して、リアルタイムで音声波形を可視化しています。`cache_frame_data=False`を設定することで、不要なメモリ使用を防ぎ、警告メッセージを回避しています。

## 4. 実行方法と注意点

1. 上記のコードをPythonファイル（例：`realtime_audio_processing.py`）として保存します。
2. コマンドラインで `python realtime_audio_processing.py` を実行します。
3. マイクからの入力音声がリアルタイムで処理され、処理後の音声が出力されます。
4. 同時に、音声波形がリアルタイムでプロットされます。
5. プログラムを終了するには、プロットウィンドウを閉じるか、ターミナルでCtrl+Cを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/0dc94825-e624-9fe1-66af-ac5c7ff7f53d.png)

注意点：
- 適切な音声入力デバイス（マイク）が接続されていることを確認してください。
- 処理の重さによっては、より高性能なマシンが必要になる可能性があります。
- フィードバックループを避けるため、ヘッドフォンの使用をお勧めします。

## 5. カスタマイズと拡張の可能性

このコードは基本的な機能を提供していますが、以下のようなカスタマイズや拡張が可能です：

1. GUIの追加：PyQt5やTkinterを使用して、ユーザーがリアルタイムでパラメータを調整できるインターフェースを追加する。
2. 追加のエフェクト：リバーブ、ディレイ、ディストーションなど、より多くの音声エフェクトを実装する。
3. 高度な信号処理：ウェーブレット変換やケプストラム分析など、より洗練された信号処理技術を導入する。
4. 機械学習の統合：TensorFlowやPyTorchを使用して、音声認識や音声合成などの機能を追加する。
5. ネットワーク機能：WebRTCなどを使用して、ネットワーク経由でのリアルタイム音声処理を実現する。

## 6. まとめと今後の展望

この記事では、PyAudioとpydubを使用してリアルタイム音声処理システムを構築する方法を紹介しました。基本的なオーディオストリーミングから始まり、エフェクトの適用、ノイズリダクション、ピッチシフト、そしてリアルタイムの可視化まで実装しました。

リアルタイム音声処理は常に進化を続ける分野であり、新しい技術や手法が次々と登場しています。今後は、機械学習や深層学習を活用したより高度な音声処理、5Gなどの高速ネットワークを利用したクラウドベースの処理、あるいはARやVRと組み合わせた没入型音声体験の創出など、さまざまな方向への発展が期待されます。

この記事で紹介した基本的な手法を理解し、さらに発展させることで、革新的な音声処理アプリケーションの開発に挑戦できるでしょう。音声処理の世界は広大で、まだまだ探求の余地がたくさんあります。ぜひ、自分なりのアイデアを実装し、新しい可能性を切り開いてください。

## 参考リンク

- [PyAudio公式ドキュメント](https://people.csail.mit.edu/hubert/pyaudio/docs/)
- [pydub公式ドキュメント](https://github.com/jiaaro/pydub)
- [SciPy公式ドキュメント](https://docs.scipy.org/doc/scipy/reference/)
- [Matplotlib公式ドキュメント](https://matplotlib.org/stable/contents.html)
- [リアルタイムデジタル信号処理の基礎](https://www.dsprelated.com/freebooks/pasp/)
- [音声信号処理の最新トレンド](https://www.sciencedirect.com/journal/speech-communication)

この記事が、Pythonでのリアルタイム音声処理の理解と実装の一助となれば幸いです。さらに探求を進め、素晴らしい音声処理アプリケーションを作り上げてください！