親子IoTワークショップ

マイコンとクラウドを使ってIoTとプログラミングを学ぼう

[ トップ | 開催予定・概要 | 2024-12開催 | お知らせ | Facebook ]

録音した音をグラフにして「見える化」する

ここでは、ラズパイに接続したマイクから音を録音して、その音の波形(音波の波の形)をグラフにして見てみます。事前作業として、以下のページの作業を済ませておいてください。

マイクを使って録音する

まずはウォームアップとして、マイクを使って録音します。サンプル・プログラムはrecord-sound.pyです。arecordというコマンドを使って録音し、録音した音を「test.wav」という名前のWAVファイルに保存し、そのファイルをaplayというコマンドを使って再生しています。arecordを実行するときのオプション「-d 3」は録音時間が3秒であること、そして「-f cd」はCD品質で録音することを意味します。CD品質で録音ということは、サンプリング・レートが44,100 Hzだということです。arecordとaplayのコマンドについては、「マイクを使って音を録音する」を参照してください。

このプログラムを実行すると、Thonnyのシェルに

Starting to record!!!

というメッセージが表示されます。このタイミングで録音が始まり、

Finished recording.

というメッセージが表示されたら録音終了です。

録音した音をグラフに表示する

次に、録音した音をグラフに表示します。サンプル・プログラムはrecord-sound-samples.pyです。マイクから音を録音する部分は、ひとつ前のプログラム(record-sound.py)と同じです。

録音した音をWAVファイルに保存したら、続いて以下のようにして、音のサンプルを取り出します。

soundSamples = sanalysis.wavToSamples(fileName)

関数sanalysis.wavToSamples()は、WAVファイルの名前を受け取り、そのファイルの中に録音されている音のサンプルをリストとして返します。ここでは、sanalysis.wavToSamples()が返したサンプル(のリスト)を変数soundSamplesに格納しています。音のサンプルがどのようにリストに格納されているかは、「ビープ音(純音)をグラフにして「見える化」する」を参照してください。

サンプリングレートが44,100で(CD品質で)1秒間録音したので、変数soundSamplesには音のサンプルが44,100個格納されています。このサンプルをそのままグラフ表示すると、音波の線が何度も重なり合うため波として見えません(長方形のように見えてしまいます。例えばどのように見えるかは「ビープ音(純音)をグラフにして「見える化」する」を参照)。そこで、ここでは44,100個あるサンプルを間引いて、より少ない数のサンプルを取り出しています(元々のサンプルのサブセットを作っている)。

subsetSamples = soundSamples[::100]

このように[ : : 100]とすると、リストの中のデータを100個飛ばしで取り出します。つまり、1番最初のデータ、101番目のデータ、201番目のデータ・・・という風に、指定されている100を「スキップする間隔」として使います。したがって、もともとあった44,100個のサンプルが441個(44,100/100)に減ります。そして、441個に間引かれたサンプルは変数subsetSamplesに格納されます。

最後に、間引かれたサンプルをグラフ化します。

sanalysis.drawSoundSamples(soundSamples = subsetSamples,
                           duration = recordDuration,
                           samplingRate = 44100)

関数sanalysis.drawSoundSamples()に、間引かれたサンプル、音の録音時間(1秒)、サンプリングレート(44,100)を渡しています。この関数の使い方については、「ビープ音(純音)をグラフにして「見える化」する」を参照してください。表示される波形(音波の波の形)は、どんな音を録音したかによります。私の場合、マイクの前で「あーーー」と1秒声を出したら、このような形になりました。

グラフの左端(録音開始時)から、200ミリ秒目あたりまで波がない(音が録音されていない)のは、ラズパイが録音を始めてから、私が声を出すまでに遅れがあったからです。ThonnyのShellに「Starting to record!!!」という表示が出てすぐに声・音を出したつもりでも、実際には遅れが生じいていて、最初の200-300ミリ秒くらいは「音なし」になります。この音なしの時間帯を減らしたり、ゼロに近くすることも可能ですが、努力の割に得られるものが少ないので、そういうものだと思って放置するのがよいと思います。

このグラフを閉じると、record-sound-samples.pyはもうひとつ別のグラフを表示します。今度は、録音開始0.5秒後のタイミングから25ミリ秒分の波形を取り出して(ズームインして)表示します。このために、変数soundSamplesに入っている(44,100個の)サンプルの中から、その時間帯に該当する部分だけを取り出します。より具体的には、全サンプルの前半半分は無視し、ちょうど真ん中から25ミリ秒分のサンプルだけ取り出します。このように、リストの中のデータを取捨選択するには、以下のようにします。

newData = data[ X : Y :]

ここでは、リスト「data」の中に入っているデータのX番目からY番目までを取り出し、取り出したデータを新しいリストとして、newDataに入れます。

record-sound-samples.pyでは、以下のように、録音開始0.5秒後から0.525秒後までのサンプルを取り出しています。

subsetSamples2 = soundSamples[int(soundSamples.size/2) :
                              int(soundSamples.size/2 + soundSamples.size/40) :]
sanalysis.drawSoundSamples(soundSamples = subsetSamples2,
                           duration = 0.025,
                           samplingRate = 44100)

soundSamples.sizeは、soundSamples(サンプルのリスト)に何個のサンプルが入っているかを調べます。ここでは、44,100個です。全サンプルのちょうど真ん中(「soundSamples.size/2」番目)から、25ミリ秒分のサンプル(「soundSamples.size/40」個)を取り出し、取り出したサンプルを新しいリストとして変数subsetSamples2に入れています。関数sanalysis.drawSoundSamples()を呼ぶときには、この25ミリ秒分のサンプル(subsetSamples2)、そのサンプルの録音時間(25ミリ秒、0.025秒)、サンプリングレート(44,100)を渡しています。

私の「あーーー」という声の波形は、ズームインするとこのようになっていました。「ビープ音(純音)をグラフにして「見える化」する」で見たような波形とは全く違います。これは、人間の声が「純音」ではなく、もっと複雑な波(空気の震え)を作っているからです。

このグラフをよく見ると、同じような部分が繰り返されているのに気づきます。グラフ中に赤く示した部分です。これが波ひとつ分で、この波が繰り返し続いているのが、私の「あーーー」という声の正体、ということになります。

ちなみに「ビープ音(純音)をグラフにして「見える化」する」で、ビープ音(C4の純音)をグラフ化したときには、以下のような波形が表示されました。赤く示した部分が波ひとつ分で、これが繰り返し続いています。

このように、波形が単純で整然としているかどうかが、純音とそれ以外の音の違いです。

楽器の音を録音してグラフに表示する

同じプログラム(record-sound-samples.py)を使って、身近に楽器があればその音を録音してみてください。以下は、私の手持ちのウクレレでC4の音を出して1秒間録音したものの波形です。

そして、録音開始0.5秒後から25ミリ秒分の波形が以下です。赤く示した部分が、波ひとつ分です。このように、楽器の音も純音ではありません。C4の純音(ビープ音)の波形と比べるとずっと複雑な(ぐちゃぐちゃな)波形をしています。

ここで面白いのは、ウクレレもビープ音もC4の音を出したのに、波形が全く違うことです。これは、それぞれの音の「音色」が違うからです。つまり、耳で認識する「音の感じ」が違うということは、空気の震え方が違うということであり、それは音の波形が違うということです。ですから、異なる楽器で同じ音(例えばC4)を弾いても、波形はそれぞれ全く異なるものになります。もし身近に何種類か楽器があったら、それぞれの音を録音して波形を見比べてください。

同じ音であっても音色が異なれば、その音の波形は異なります。ただし周波数は同じになります。C4なら261 Hzになります。ビープ音とウクレレのグラフを見てください。それぞれのグラフ中の赤い部分、つまり波ひとつ分の長さ(時間間隔)を見ると、どちらも5ミリ秒弱です。つまり、どちらの音も周波数はとても近いことが分かります。 C4の周波数は261 Hzで、これは1秒間に同じ波が261回繰り返すということです。つまり、波ひとつ当たりの時間間隔は、1/264 = 0.0038秒(3.8ミリ秒)となります。ビープ音とウクレレのグラフをプリントアウトして、定規を使って波ひとつあたりの時間間隔を計算すれば約3.8ミリ秒になるはずです。

ぜひ、異なる高さの声や異なる楽器の音を録音し、その波形を見て、周波数がだいたいどのくらいかを計算してみてください。例えば、上記のウクレレの(C4の)波形と私の声の波形を比べると、ウクレレの場合の波ひとつ分の時間間隔が5ミリ秒弱、私の声の場合の波ひとつで10ミリ秒程度だと分かります。つまり、ウクレレの(C4の)音の方が周波数が高かったことが分かります。ということは、私が出した「あーーー」という声はC4よりも低い音だったということです。

グラフを見て目算したり、定規を使って概算して音の周波数をざっくり推測すること自体とても重要なことで、また周波数の概念を理解するための訓練として不可欠なのですが、これは「だいたいの計算」であって正確なわけではありません。正確な数値として(定量的に)音波の波形から周波数を求めるには、フーリエ変換という数学的手法を使います。これに興味があれば、「音の波形からFFTで周波数を求める」を参照ください。

自習プロジェクトの目次に戻る