LoginSignup
0
1

More than 1 year has passed since last update.

【Python】wave形式のファイルから波形データを取り出す

Last updated at Posted at 2022-05-28

久しぶりに音声処理をしたくなりました。目標はMFCCと呼ばれるメルケプストラム係数で特徴量抽出をすることです(2022/06/12: 達成しました)。

このあたりの記事をググってもPython2系だったりと10年前ぐらいの記事が多いため、新しく記事を書くことにしました。

ここでの実行環境は次の通りです。

Python 3.8.1
Ubuntu 20.04.4 LTS

検証するwaveファイル

ここでは、440Hzのsin波を1秒間だけ再生するwave形式の音声ファイル(440Hz.wav)で検証を行うことにします。ファイルパラメータは次の通りです。

・チャネル数:モノラル
・1サンプルあたりのバイト数:2 byte
・サンプリング周波数:44100 Hz
・再生時間:1秒

【簡単】waveファイルの波形データをnumpy配列に変換

音声処理をするPythonのライブラリの選択肢は多いです。今回はsoundfileを使用します。

import soundfile

fname = "440Hz.wav"
data, fs = soundfile.read(fname) #data:波形データ, fs:サンプリング周波数

理解していれば3行で波形データを抽出できるので使い勝手がよいです。しかし、これで満足してしまうと少々危険です。なぜかというとwaveの音声データから-1~+1に正規化された波形データになるところがブラックボックスになっているためです。

もう少し詳しく調べるには、組込みwaveモジュールを取り扱う必要があります。ただし、多少のバイナリも触れる必要がある。

以下からは、解説記事になります。

【waveモジュール】組込みwaveモジュールでwaveの波形データを詳細に調べる

wave形式に詳しくなければ、一度自分で調べたいところ。その場合は、組込みwaveモジュールが有効です。

まずは、ファイルパラメータを確認してみます。

import wave

fname = "440Hz.wav"
wave_file = wave.open(fname, 'r')

nchannels = wave_file.getnchannels() #チャネル数
print("nchannels: {0}".format(nchannels))
sample_width = wave_file.getsampwidth() #1サンプルサイズのバイト数
print("sample_width: {0} byte".format(sample_width))
fs = wave_file.getframerate() #サンプリングレート
print("fs: {0} Hz".format(fs))
nframes = wave_file.getnframes() #音声データのデータ点数
print("nframe: {0}".format(nframes))

実行結果は次のようになる。

nchannels: 1
sample_width: 2 byte
fs: 44100 Hz
nframe: 44100

もしくは、次のコードでまとめてパラメータを表示させることができる。

params = wave_file.getparams()
print(params)

実行結果は次のようになります。

_wave_params(nchannels=1, sampwidth=2, framerate=44100, nframes=44100, comptype='NONE', compname='not compressed')

さて、本題に移ります。
どのように正規化された波形データを取得するかを調べます。

手順としては、

1. 一度waveファイルのバイナリの波形データを取得する。
2. 16bit符号付き整数型に変換する。
3. -2^15 ~ 2^15 - 1 の範囲でサンプルデータが量子化されているため、戻してあげると-1~+1の範囲で正規化される。

※なぜ16bit符号付き整数にするかは、標準の形式があるらしく、少々奥が深いので割愛します。

import wave
import numpy as np

fname = "440Hz.wav"

wave_file = wave.open(fname, 'r') 
x = wave_file.readframes(wave_file.getnframes()) #waveをバイナリ化 
x = np.frombuffer(x, dtype="int16") / (2**15-1) #16bit符号付き整数型に変換し,-1~1で正規化
print(max(x), min(x))

実行結果は次のようになります。確かに44100サンプルあるデータが-1~+1の範囲に収まっています。

0.999969481490524 -0.999969481490524

このx配列と、soundfileライブラリで取得したdata配列を比較すると、同じ配列になっていることがわかります。ファイルパラメータさえ確認してしまえば、納得しながらsoundfileライブラリで波形データが取得できると思います。

公式ドキュメント

wave --- WAVファイルの読み書き

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1