Pythonで音声処理入門:PyAudioの使い方ガイド

Pythonで音声を扱いたいときに便利なのが PyAudio ライブラリです。

PyAudioを使うと、マイクからの入力を録音したり、音声データを再生したり、リアルタイムで処理したりできます。

本記事では、インストール方法から基礎的な使い方、サンプルコード、高度な設定例、最後に演習問題とその解答例まで幅広く解説します。初心者の方にもわかりやすいように、順を追って丁寧に説明していきます。


PyAudioとは

PyAudioは、PortAudioというC言語ベースのクロスプラットフォーム音声入出力ライブラリのPythonバインディングです。

Windows、macOS、Linuxで動作し、数行のコードで音声のキャプチャや再生を実現できます。

メリット
  • クロスプラットフォーム対応
  • リアルタイム処理が可能
  • 比較的シンプルなAPI

インストール方法

PyAudioを使うには、まずPortAudio本体とPyAudioパッケージをインストールします。以下の手順を参考にしてください。

PortAudioのインストール

Windows

公式サイトからバイナリを取得するか、conda install portaudio

macOS
brew install portaudio
Linux(Ubuntuなど)
sudo apt-get update sudo apt-get install portaudio19-dev

PyAudioのインストール

pip install pyaudio

基本的な録音・再生の流れ

PyAudioで音声を扱う場合、以下の3ステップが基本です。

  1. ストリームの作成
    音声入出力の設定(サンプリングレート、チャネル数、フォーマットなど)
  2. ストリームの開始と読み書き
    stream.read() で録音、stream.write() で再生
  3. ストリームの終了・解放
    stream.stop_stream() → stream.close() → p.terminate()

サンプルコード:録音→保存→再生

以下の例は、マイクから音声を2秒間録音し、WAVファイルに保存、その後再生を行うシンプルなコードです。

import pyaudio
import wave

# 録音設定
FORMAT = pyaudio.paInt16   # 16ビット整数
CHANNELS = 1               # モノラル
RATE = 44100               # サンプリングレート
CHUNK = 1024               # フレームサイズ
RECORD_SECONDS = 2         # 録音時間
WAVE_OUTPUT_FILENAME = "output.wav"

# PyAudioオブジェクト生成
p = pyaudio.PyAudio()

# 録音用ストリーム開始
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

print("Recording...")

frames = []
for _ in range(int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("Finished recording.")

# ストリーム停止・解放
stream.stop_stream()
stream.close()
p.terminate()

# WAVファイルに保存
wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

# 再生
wf = wave.open(WAVE_OUTPUT_FILENAME, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True)

print("Playing back...")
data = wf.readframes(CHUNK)
while data:
    stream.write(data)
    data = wf.readframes(CHUNK)

stream.stop_stream()
stream.close()
p.terminate()
print("Done.")

open() メソッドで指定できる主なストリームプロパティについて

よく使うものを中心に詳しく解説します。

プロパティ名型/値の例説明
formatpyaudio.paInt16
pyaudio.paFloat32
など
音声データのサンプル形式(フォーマット)を指定します。
– paInt16:16 ビット符号付き整数(最も一般的)
– paInt24:24 ビット整数
– paInt32:32 ビット整数
– paFloat32:32 ビット浮動小数点
– paUInt8:8 ビット符号なし整数
channels1(モノラル)
2(ステレオ)
オーディオチャンネル数を指定します。
– 1:モノラル
– 2:ステレオ
複数チャンネル(サラウンド音声など)を扱いたい場合は、さらに大きな値も可能です。
rate44100
16000
サンプリングレート(Hz 単位)を指定します。
– 44100:CD 品質
– 48000:プロ向けオーディオ品質
– 8000~16000:音声通話品質
frames_per_buffer1024
2048
バッファあたりのフレーム数(読み書き単位のサイズ)を指定します。
小さくするとリアルタイム性が向上する反面、CPU 負荷が増加します。
inputTrue/False入力(録音)ストリームとして開くかどうかを指定します。True を指定するとマイクなどから音声を読み取れるようになります。
outputTrue/False出力(再生)ストリームとして開くかどうかを指定します。True を指定するとスピーカーなどへ音声を書き込めるようになります。
input_device_index0, 1, …利用する入力デバイス(マイク)のインデックス番号を指定します。
複数のオーディオデバイスがある場合に、pyaudio.PyAudio().get_device_info_by_index(i) で一覧を取得して設定します。
output_device_index0, 1, …利用する出力デバイス(スピーカー)のインデックス番号を指定します。
stream_callbackcallback 関数非同期・コールバック方式でストリームを動かす場合に、データ処理用のコールバック関数を指定します。
start/stop内部メソッドストリームの開始・停止を行うメソッドです。stream.start_stream()、stream.stop_stream() を呼んで制御します。

各プロパティの選び方ガイド

format
  • 一般的な音声録音/再生は paInt16 で十分。
  • 高精度・科学用途なら paFloat32、軽量・音声通話なら paUInt8 や低レートフォーマットも検討。
channels

ステレオ音声を扱いたい場合は必ず 2。モノラルでよければ 1 を指定して処理量を抑えましょう。

rate

音楽用途は 44100 Hz、動画用途は 48000 Hz、通話用途は 8000~16000 Hz が目安です。

frames_per_buffer

1024~2048 あたりがバランス良く、リアルタイム処理・遅延が気になるなら 512 以下に。

デバイスインデックス

環境依存エラーを避けるため、明示的にインデックス指定しておくと堅牢です。

# 例
p = pyaudio.PyAudio()
for i in range(p.get_device_count()):
    info = p.get_device_info_by_index(i)
    print(i, info['name'], '(入力)' if info['maxInputChannels'] > 0 else '')

ストリームの詳細設定

非同期コールバック方式

大きなバッファを用意せず、イベント駆動でリアルタイム処理したい場合には、コールバック方式を使います。

import pyaudio

def callback(in_data, frame_count, time_info, status):
    # ここでin_dataを加工可能
    return (in_data, pyaudio.paContinue)

p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paInt16,
                channels=1,
                rate=44100,
                input=True,
                frames_per_buffer=1024,
                stream_callback=callback)

stream.start_stream()

# 5秒間待機
import time; time.sleep(5)

stream.stop_stream()
stream.close()
p.terminate()

トラブルシューティング

webrtcvad.Error: Error while processing frame

ほとんどの場合「VAD に渡している音声バッファの長さ(またはフォーマット)が正しくない」ことが原因です。

WebRTC VAD が受け付けるオーディオは以下の条件に厳密に合致している必要があります。

  1. フレーム長:10 ms、20 ms、または 30 ms
  2. サンプリングレート:8 kHz、16 kHz、32 kHz、または 48 kHz
  3. フォーマット:モノラルの 16bit PCM

例えば 16 kHz、30 ms の場合、必要なバッファサイズは以下になります。

サンプル数 = RATE × (フレーム長 [ms] / 1000)
  • RATE = 16000
  • フレーム長 = 30 ms
  • サンプル数 = 16000 × 30/1000 = 480 samples

→ frames_per_buffer=480 にする必要があります。

OSError: [Errno -9996] Invalid output device (no default output device)

出力デバイスが見つからない。p.get_device_count() や p.get_device_info_by_index() で確認。

録音時のノイズや遅延

frames_per_buffer の値を小さくすると遅延は減るが、CPU負荷は上がる。

インストール時のビルドエラー(特にWindows)

公式のホイールファイルを利用するか、Visual Studio Build Toolsが必要。


演習問題

以下の課題に挑戦してみましょう。

  1. ステレオ録音
    モノラルではなくステレオ(2チャンネル)で3秒間録音し、stereo.wav に保存してください。
  2. 音声の正規化
    録音した音声の振幅を最大限に引き上げる正規化処理を実装し、正規化後のWAVを出力してください。
  3. リアルタイム音声反響エフェクト
    コールバック方式を使い、入力音声に簡単なエコー(反響)効果をリアルタイムで付加して再生してください。

演習問題の解答例

1. ステレオ録音

import pyaudio
import wave

FORMAT = pyaudio.paInt16
CHANNELS = 2              # ステレオ
RATE = 44100
CHUNK = 1024
RECORD_SECONDS = 3
WAVE_OUTPUT_FILENAME = "stereo.wav"

p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

frames = []
for _ in range(int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

2. 音声の正規化

import wave
import struct

# 読み込み
wf = wave.open('output.wav', 'rb')
frames = wf.readframes(wf.getnframes())
wf.close()

# 16ビットPCMを整数列に変換
samples = struct.unpack('<' + 'h'*(len(frames)//2), frames)
max_amp = max(abs(s) for s in samples)
factor = 32767 / max_amp  # 正規化係数

# 正規化
normalized = [int(s * factor) for s in samples]
normalized_frames = struct.pack('<' + 'h'*len(normalized), *normalized)

# 書き出し
wf = wave.open('normalized.wav', 'wb')
wf.setnchannels(1)
wf.setsampwidth(2)
wf.setframerate(44100)
wf.writeframes(normalized_frames)
wf.close()

3. リアルタイム音声反響エフェクト

import pyaudio
from collections import deque

FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024
DELAY_CHUNKS = 5  # 約(1024/RATE)*5 秒の遅延

delay_buffer = deque([b'\x00' * CHUNK * 2] * DELAY_CHUNKS, maxlen=DELAY_CHUNKS)

def callback(in_data, frame_count, time_info, status):
    # 古いデータを取得
    echo_data = delay_buffer[0]
    # 新しいデータをバッファに追加
    delay_buffer.append(in_data)
    # ミックスして返す(音量調整に注意)
    mixed = bytearray(len(in_data))
    for i in range(0, len(in_data), 2):
        orig = struct.unpack_from('<h', in_data, i)[0]
        echo = struct.unpack_from('<h', echo_data, i)[0]
        val = int(orig * 0.8 + echo * 0.2)
        struct.pack_into('<h', mixed, i, max(min(val, 32767), -32768))
    return (bytes(mixed), pyaudio.paContinue)

p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                output=True,
                frames_per_buffer=CHUNK,
                stream_callback=callback)

stream.start_stream()
import time; time.sleep(5)
stream.stop_stream()
stream.close()
p.terminate()

以上がPythonのPyAudioライブラリを使った音声処理の入門ガイドです。

基礎から実践までを一通り学習し、演習問題で手を動かして理解を深めてみてください。

音声処理の世界は奥が深く、リアルタイム分析や音声認識、機械学習との組み合わせなど、応用範囲は無限大です。ぜひ次のステップとして、より高度なアルゴリズムやライブラリに挑戦してみましょう!