🎵

【Python】pydub使ったら音声データの解析がめっちゃ簡単だった

2024/01/25に公開2

音声データをPythonで取り込んで表示したり解析したりしたいと思ったことはありませんか?

この記事では音声データの入出力をPythonで行えるpydubのインストールと基本的な使用方法(音声データの読み込み)について説明します。

pydubで音声データファイルの入力ができれば,Numpyが提供する関数等を使用してPython上で音声データの解析・加工ができるようになります。

動作検証済み環境

macOS Catalina(10.15.7), python3.7.10, Jupyter Notebook, OpenCV 3.4.2

Pythonで音声データを読み込み,波形を描画する方法

pydubのインストール

Pythonで音声データファイルの読み込みをするときは,pydubが便利です。

pydubは内部で ffmpegというライブラリを使用しますので,あらかじめインストールしておく必要があります。(ffmpegはgifアニメーションを作るときにも使用しますので,インストールしておいても損はないと思います。)

homebrewでパッケージ管理している場合,ffmpegをインストールするには,以下のようにターミナルに打ち込みます。

$ brew install ffmpeg

ffmpeg のインストールが終わったら,pip経由でpydubをインストールします。

$ pip install pydub

環境によっては,pipコマンドがpip3の場合があります。ご自身のPythonの環境に合わせて使い分けてください。

ボイスメモで録音

mac に標準で搭載されている「ボイスメモ.app」を使って録音し,pydub で音声データを読み込み,波形を表示してみます。

ボイスメモを起動し,録音ボタンを押すと音声を録音することができます。ここでは,自分の声を5秒間録音したものを使用することにします。

ボイスメモで録音した音声ファイルは,ドラッグ&ドロップで好きなディレクトリにコピーすることができます。標準では,ファイルの拡張子は .m4a となっています。

音声データの読み込みと波形の表示,周波数解析

先程録音した音声ファイルの名前を好きな名前(ここでは「aiueo.m4a」)に変更し,pythonスクリプトを保存するディレクトリ(例えば,Desktop/LabCode/python/read-sounds)に配置します。

音声データを読み込んで波形を表示し,波形に含まれている周波数を解析してみます。なお,周波数解析の方法とその解釈については,numpy.fftを使った周波数解析のページで説明しています。

下記のコードを pydub-demo.pyという名前で保存し,先程音声ファイルを保存したディレクトリと同じところ(ここでは,Desktop/LabCode/python/read-sounds)に保存します。

pydub-demo.py
import matplotlib.pyplot as plt
import numpy as np
from pydub import AudioSegment

# ボイスメモで収録したm4aファイルを読み込む
sounds = AudioSegment.from_file('aiueo.m4a', 'm4a')

# 基本情報の表示
print(f'channel: {sounds.channels}')
print(f'frame rate: {sounds.frame_rate}')
print(f'duration: {sounds.duration_seconds} s')

# チャンネルが2 (ステレオ) の場合,交互にデータが入っているので,二つおきに読み出す。
# ただし,今回の場合はモノラルのはず。つまり,sounds.channels = 1
sig = np.array(sounds.get_array_of_samples())[::sounds.channels]
dt = 1.0/sounds.frame_rate # サンプリング時間

# 時間アレイを作る
tms = 0.0 # サンプル開始時間を0にセット
tme = sounds.duration_seconds # サンプル終了時刻
tm = np.linspace(tms, tme, len(sig), endpoint=False) # 時間ndarrayを作成

# DFT
N = len(sig)
X = np.fft.fft(sig)
f = np.fft.fftfreq(N, dt) # Xのindexに対応する周波数のndarrayを取得

# データをプロット
fig, (ax01, ax02) = plt.subplots(nrows=2, figsize=(6, 8))
plt.subplots_adjust(wspace=0.0, hspace=0.6)

ax01.set_xlim(tms, tme)
ax01.set_xlabel('time (s)')
ax01.set_ylabel('x')
ax01.plot(tm, sig) # 入力信号

ax02.set_xlim(0, 2000)
ax02.set_xlabel('frequency (Hz)')

ax02.set_ylabel('|X|/N')
ax02.plot(f[0:N//2], np.abs(X[0:N//2])/N) # 振幅スペクトル

plt.show()

プログラムを実行する

ターミナルを開き,

$ cd Desktop/LabCode/python/read-sounds

と入力し,ディレクトリを移動します。あとは以下のコマンドで pydub-demo.pyを実行するだけです。( $マークは無視してください)。

$ python3 pydub-demo.py

実行結果

以下のようなウィンドウが立ち上がれば成功です。

一段目は横軸を時間として音声データをプロットしたもの,二段目は音声データの周波数解析の結果で,振幅スペクトルを表示したものです。

私はマイクに向かってゆっくりと「あいうえお」としゃべった音声ファイルを使用したので,「あ」,「い」「う」,「え」,「お」に対応する振幅の大きい部分が一段目に見えています!

また,周波数解析によって,私(成人男性)の声の成分は500Hz以下に集中している事がわかります。

コードの解説

上に書いたコードの解説をしていきます。

import matplotlib.pyplot as plt
import numpy as np
from pydub import AudioSegment

まず,matplotlibnumpy,それからpydubAudioSegmentをインポートします。

# ボイスメモで収録したm4aファイルを読み込む
sounds = AudioSegment.from_file('aiueo.m4a', 'm4a')

# 基本情報の表示
print(f'channel: {sounds.channels}')
print(f'frame rate: {sounds.frame_rate}')
print(f'duration: {sounds.duration_seconds} s')

ボイスメモで収録した音声ファイルを読み込み,基本情報(チャンネル数,フレームレート,音声記録時間)を表示します。

AudioSegment.from_file('aiueo.m4a', 'm4a') の第1引数はファイルのパス,第2引数はファイルの拡張子です。読み込みたいファイルの種類によって書き換えてください。対応しているファイルの種類はffmpegが取り扱えるファイルの種類と同じです。大抵の音声ファイルは対応済みです。

詳しくは,ffmpeg の対応ファイルを一覧ページを参照してください (XやE,IX,EXとなっているものが対応しています)。

# チャンネルが2 (ステレオ) の場合,交互にデータが入っているので,二つおきに読み出す。
# ただし,今回の場合はモノラルのはず。つまり,sounds.channels = 1
sig = np.array(sounds.get_array_of_samples())[::sounds.channels]
dt = 1.0/sounds.frame_rate # サンプリング時間

# 時間アレイを作る
tms = 0.0 # サンプル開始時間を0にセット
tme = sounds.duration_seconds # サンプル終了時刻
tm = np.linspace(tms, tme, len(sig), endpoint=False) # 時間ndarrayを作成

波形を表示したり加工したりするためにに,Numpy配列に変換します。ステレオ音声の場合は,右と左の音声が交互にデータとして入っているらしいので,sig には片方のみが入るようにします。ただし,ボイスメモで収録した場合,モノラル音声になっているはずなので,スキップは必要ありません。ステレオ音声の場合はこの操作は必須です。

また,時系列データとして取り扱いたいので,時間のNumpy配列を作成しています。

# DFT
N = len(sig)
X = np.fft.fft(sig)
f = np.fft.fftfreq(N, dt) # Xのindexに対応する周波数のndarrayを取得

離散フーリエ変換をして音声ファイルに含まれる周波数成分を解析しています。

# データをプロット
fig, (ax01, ax02) = plt.subplots(nrows=2, figsize=(6, 8))
plt.subplots_adjust(wspace=0.0, hspace=0.6)

ax01.set_xlim(tms, tme)
ax01.set_xlabel('time (s)')
ax01.set_ylabel('x')
ax01.plot(tm, sig) # 入力信号

ax02.set_xlim(0, 2000)
ax02.set_xlabel('frequency (Hz)')

ax02.set_ylabel('|X|/N')
ax02.plot(f[0:N//2], np.abs(X[0:N//2])/N) # 振幅スペクトル

plt.show()

データのプロットをします。一段目には音声データを横軸を時間として表示するように,二段目には振幅スペクトルを0 ~ 2000 Hzまで表示するようにしています。

ax02.plot(f[0:N//2], np.abs(X[0:N//2])/N) として,前半の半分しかデータをプロットしないのは,解析信号が実数のため,後半部分が示す負の周波数成分に興味がないからです。

最後に

pydubを使用して音声データファイルを読み込む方法について説明しました。音声データファイルを読み込むことができれば,上で述べたDFT以外にも色々とデータを解析してデータに含まれる情報を抽出したり,音声データに色々と手を加えてアレンジしたりと,様々なことができるようになります。

今回ご紹介したような信号処理の内容をまとめた技術書を執筆しましたので、ご興味ある方はぜひご覧ください!
フーリエ変換やウェーブレット変換を用いた周波数解析についても具体的に解説しています!

https://zenn.dev/labcode/books/3v5y3msrej7vu9

Discussion

AyumuAyumu

HomebrewのWebサイト上、指定のURL(/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)")を読み込んだ後にターミナルでbrewコマンドを実行したのですが、コマンドが見つからないとのエラーメッセージが出ました。
対処法を教えていただけると幸いです

LabCode(ラボコード)LabCode(ラボコード)

ご質問ありがとうございます。

おそらくですが、brewに関するPATHが通ってないのではないかと推測されます。
こちらのページを参考にPATHを通していただけますか?
m1のmacを使用されている場合はこちらを参考にしてください。

よろしくお願いいたします。