pythonで音響信号処理「超」入門

  • by

音響 信号処理

「音響信号処理」というテーマで記事を考え、タイトルに据えたものの
ものすごく恐縮なので「超」入門とさせてもらいました。

Contents

  1. 音の信号処理
  2. 音を読み込む
  3. 音の可視化
  4. まとめ

1. 音の信号処理

まずは毎度のように言葉から紐解いていきます。
「信号」というは情報を載せた音や光や電波などを指すことが多いです。
昔に行われていた狼煙や、艦船に用いられている国際信号旗、日本海軍で考案された手旗信号、
信号灯に代表される回光通信機、あるいは我々が普段発している音声も信号のひとつです。

これらの信号は、伝えたい情報を符号化することによって生成されます。
符号化とはひらたく言えば特定のルールにしたがってデータ(情報)を変換することです。
単に変換するのではなく、伝えるのに効率的な変換、つまり圧縮をします。

例えば何か言葉(複数の文字)を伝えようとします。
我々がふつうにしていれば直接声に出して情報伝達することが可能です。
しかし海上で艦船同士が通信をする際に、搭乗員が目視できても音声を伝えることは困難です。
そこで日本語50音を腕で表現できるポーズにあてはめ、言葉をポーズのとり方に変換します。
文字の羅列→ポーズの羅列 この変換が符号化にあたります。
(参考ページ:手旗でメッセージ(海上保安庁)

もっと言えばひらがなをローマ字表記するのも符号化のひとつでしょう。
ひらがなは50音だけでなく濁音(「が」「ぞ」)・半濁音(「ぷ」)・拗音(「ぅ」)など
バリエーションがあるせいで数がたくさんありますが、これらひっくるめてアルファベット26文字だけで表現できます。

さて本題です。
我々の知覚できる音情報というのは、外耳道→鼓膜→耳小骨→蝸牛管内の有毛細胞→神経系→脳という道を辿ります。
このプロセスのうち、音にあたる信号の形態は空気圧だったり、振動であったり、電気であったりと様々です。
コンピュータなどで音を記録する際は、マイクロホン→(アンプなど)→PCとなります。
マイクロホンでは空気圧の変動を電圧の変動に変化してくれます。
電圧の変動はPC内で処理されて、多くの場合、-1~1までの実数で表現されます。

ディジタル

連続的なものをアナログといい、離散的なものをディジタルといいます。
音データに限定すると、時間軸と振幅がそれぞれ連続か不連続(離散的)かという表現ができます。

時間軸でいえばコンピュータは基本的にとびとびの時間しか扱えません。(とびとびと言っても等間隔です。)
このとびとびの具合を表しているのがサンプリング周波数です。
単位は「Hz(ヘルツ)」で、1秒間に何回データを見るかを表しています。
通常の音楽CDであればサンプリング周波数は44.1k [Hz]です。
これは1秒間に44.1k(つまり44100)個のデータ(-1~1)が入っていることを指しています。

そして振幅の場合のとびとび具合は量子化ビット数で表現されます。
単位はそのまんま「bit(ビット)」です。
通常の音楽CDは16 [bit]ですので振幅(音の強さ)が2^16=65536段階で表現されます。

よって音楽CDは、65536段階の音の強さのデータを1秒間に44100個格納しています。
ちなみにハイレゾというのは基本的に定義がカッチリ決まっているものではないですが、
サンプリング周波数が44.1k [Hz]を上回る、もしくは量子化ビット数が16 [bit]を上回るような音を指しています。

ここで注意が特に注意が必要なのが、サンプリング周波数です。
これは聴くとか再生するとか何かの対象とする音の高さの2倍以上が必要となります。
たとえばサンプリング周波数が8000 [Hz]だったら4000 [Hz]の音までしか扱えません。
このルールはサンプリング定理だとかいろいろ名前がついています。
以前の記事で人間は20k(=20000) [Hz]まで聞こえると書きましたが、
実はCDは44100[Hz]なので22050[Hz]までOK、つまり人間の可聴域よりも少し高くても大丈夫です。

2. 音を読み込む

pythonで音を読み込むにもいろいろな方法があります。
片っ端から試したわけではないのでもっと良い方法があるかもしれませんが、
とりあえず個人的に手っ取り早く扱える方法を紹介します。

読み込むための具体的な方法としては、

  • wave(標準ライブラリ)
  • read( scipy.io.wavfile )
  • load(librosa)
  • wavio(scikits.audiolab)

などなどありますが、今回はscipyの中にあるreadモジュールを使います。

from scipy.io.wavfile import read
import matplotlib.pyplot as plt
import numpy as np

filename = "hiroshiba_normal_001.wav"
fs, speech_data = read(filename)
plt.plot(speech_data)
plt.show()

上のプログラムでは「とりあえず読み込んで音声波形として表示」しています。
結果として表示されるのがFig. 1になります。
今回用いたデータはnico-opendataにあるDwango Media Villageの廣芝さんの
100文の読み上げ音声データを利用しています。
5行目のfilenameにカレントディレクトリにある使いたい音声データの名前を指定すると
いい感じです。

Fig. 1 音声読み込み結果

Fig. 1では横軸がサンプル数、縦軸が振幅になります。

せっかくの音声データなのでグラフを見やすくします。
jupyter notebookなどであれば以下に示すプログラムを別のセルに入力すると、
見比べることができます。

A = 0.8
speech_data = A * speech_data / max(abs(speech_data))
t = np.arange(0, len(speech_data)/fs, 1/fs)

plt.xlabel("time [s]")
plt.ylabel("amplitude")
plt.xlim(0,len(speech_data)/fs)
plt.plot(t, speech_data)

1, 2行目では振幅を調整しています。
扱いやすいので-1~1に収めたいですが、余裕を持って-0.8~0.8にしています。
3行目ではサンプル数から時間表記にするのに使っています。
arangeでは等差数列を公差を指定して生成します。
特定のサンプル数が何秒にあたるかはサンプル数をサンプリング周波数で割ることで求められます。

Fig. 2 調整したグラフ

5, 6行目みたいにラベルを追加したり、7行目みたいに表示する軸の長さを変えたりできます。

3. 音を可視化する

音を可視化するという意味では様々な手法があり、Fig. 1, 2のような(いわゆる時間波形)グラフもそのひとつです。時間波形は時間ごとの音の大きさ(振幅)のみを表しています。

スペクトログラムは時間ごと、音の高さ(周波数)ごと、の音の強さを表しています。
Fig. 3が上で読み込んだ音声のスペクトログラムです。
縦軸が周波数、横軸が時間、色の濃さが音の強さにあたります。

Fig. 3 スペクトログラムの例
plt.specgram(speech_data, Fs=fs);
plt.xlabel("time [s]");
plt.ylabel("frequency [Hz]");

上がスペクトログラムを表示するプログラムになります。
matplotlibのspecgram関数で表示できます。

4. まとめ

今回は信号処理の入り口として信号を処理する意味、
信号(音)を処理するために読み込む方法と、それを可視化する方法を紹介しました。

今回使用したプログラムはこちらのGithubのページで公開しています。
ぜひ参考にしていただければと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です