第 8 章 デジタル・フィルター 97
8.6 音声信号の高音部をカットする実験
Mathematica のノートブック fourier20160114.nb2 を用意した (2016/12/22リンク切れ直しま した)。
2http://nalab.mind.meiji.ac.jp/~mk/fourier/fourier20160114.nb
fourier20160114.nb
snd = Import["http://nalab.mind.meiji.ac.jp/~mk/fourier/piano-do-mi-so.wav"]
{left, right} = snd[[1, 1]];
sr = snd[[1, 2]]
take[tbl_, t1_, t2_] := Take[tbl, {Floor[t1*sr], Floor[t2*sr]}]
take1[tbl_, t_] := Take[tbl, {Floor[t*sr], Floor[t*sr] + sr - 1}]
g = ListPlot[tbl = take1[left, 1.0], PlotRange -> All]
ListPlay[tbl]
ListPlay[tbl, SampleRate -> sr]
ListPlay[tbl, SampleRate -> sr/2]
ListPlay[tbl, SampleRate -> sr*2]
ListPlay[tbl, SampleRate -> Floor[sr*1.5]]
c = Fourier[tbl, FourierParameters -> {-1, -1}];
g = ListPlot[Abs[c], Joined -> True, PlotRange -> All]
cutoff[f_] :=
Join[Table[1, {n, f + 1}], Table[0, {n, sr - 2*f - 1}], Table[1, {n, f}]];
440.0*2^(-{9, 5, 2}/12) c2 = c*cutoff[500];
g3 = ListPlot[Abs[c2], Joined -> True, PlotRange -> All]
Export["do-mi-so-cutoff500.eps", g3]
tbl2 = Re[InverseFourier[c2, FourierParameters -> {-1, -1}]];
ListPlay[tbl2, SampleRate -> sr]
do = Re[InverseFourier[c*cutoff[300], FourierParameters -> {-1, -1}]];
domi = Re[InverseFourier[c*cutoff[360], FourierParameters -> {-1, -1}]];
domiso = Re[InverseFourier[c*cutoff[450], FourierParameters -> {-1, -1}]];
domiso2 = Re[InverseFourier[c*cutoff[900], FourierParameters -> {-1, -1}]];
original = Re[InverseFourier[c, FourierParameters -> {-1, -1}]];
ListPlay[do, SampleRate -> sr]
ListPlay[domi, SampleRate -> sr]
ListPlay[domiso, SampleRate -> sr]
ListPlay[domiso2, SampleRate -> sr]
ListPlay[original, SampleRate -> sr]
ダウンロードして、実行してみよう。
Safari でアクセスする場合は、この講義の WWW サイト http://nalab.mind.meiji.ac.jp/
~mk/fourier/を開き、fourier20160114.nb3 を Ctrl+クリックして、「リンク先のファイルを別名 でダウンロード」を選択し、自分の選ぶ場所に保存し、Mathematica で開く。
8.6.1 最初は以前やったことの復習
上から順に一つ一つ Shift + Enter で評価するか、[評価]→ [ノートブックを評価]で一気に全体 を評価する。
3http://nalab.mind.meiji.ac.jp/~mk/fourier/fourier20160114.nb
snd = Import["http://nalab.mind.meiji.ac.jp/~mk/fourier/piano-do-mi-so.wav"]
ピアノでド・ミ・ソの和音を弾いたのを録音したファイルpiano-do-mi-so.wav をインポートし、
変数snd に代入する。
{left, right} = snd[[1, 1]];
sr = snd[[1, 2]]
音声の PCM データの数値リストを変数 left, right に代入する (それぞれ左チャンネル、右チャ ンネル)。またサンプリング周波数を変数sr に代入する(値は 44100 Hzである)。
take[tbl_, t1_, t2_] := Take[tbl, {Floor[t1*sr], Floor[t2*sr]}]
take1[tbl_, t_] := Take[tbl, {Floor[t*sr], Floor[t*sr] + sr - 1}]
t1 秒後から t2 秒後までのデータを取り出す関数take[] (今回は使わない) と、t 秒後から 1秒分 のデータを取り出す関数take1[] を定義する。
(Take[] はリストの指定範囲を抜き出す関数、Floor[] は小数データを切り捨てで整数にする関
数である。)
ListPlot[tbl = take1[left, 1.0], PlotRange -> All]
最初から1.0秒のところから1秒分の左チャンネルの信号データを変数 tbl に代入し、かつプロッ トしてみる。
10 000 20 000 30 000 40 000
-0.4 -0.3 -0.2 -0.1 0.1 0.2 0.3
図 8.1: ピアノでド・ミ・ソの和音を弾く
8.6.2 サンプリング周波数を変えて再生
ListPlay[tbl, SampleRate -> sr]
数値データはListPlay[] 関数で再生(音を出力) することが可能である。その際に、サンプリン グ周波数の指定が必要である。(sndはサンプリング周波数のデータを含んでいるが、tblは信号の 数値データのみしか含んでいないので、別途指定する必要がある。)
SampleRate -> sr とすれば、正しく再生できるが、それを変えると本来の音の高さとは違う高
さで再生される。
• 半分にすると、1オクターブ低い音で再生される。再生時間は2倍になる。
• 2倍にすると、1オクターブ高い音で再生される。再生時間は1/2 になる。
サンプリング周波数を変えて遊ぶ
ListPlay[tbl, SampleRate -> sr/2]
ListPlay[tbl, SampleRate -> Floor[1.5*sr]]
(周波数を1.5倍にする。Mathematica では、サンプリング周波数は整数で指定する必要がある
ので、Floor[]を用いて整数にしている。)
8.6.3 離散 Fourier 変換してスペクトルを表示
(ここは以前やったことの軽い復習)
離散Fourier変換してスペクトルを表示
c = Fourier[tbl, FourierParameters -> {-1, -1}];
ListPlot[Abs[c], Joined -> True, PlotRange -> All]
10 000 20 000 30 000 40 000
0.005 0.010 0.015
離散 Fourier変換して、離散 Fourier係数 Cnをプロットする。変数 c は数値のリストでc[[i]] が Ci−1 である(1≤i≤N =sr = 44100)。
1 秒分の信号を離散Fourier変換 (Fonurier 係数を近似計算) したので、cn,c−n は周波数 nHz の 成分である。対応する離散 Fourier 係数 Cn, CN−n は、それぞれc[[n+1]], c[[N-n+1]] に記憶さ れている。
ピアノのド・ミ・ソ, 3つの鍵盤を叩いて出した音であるが、ピークは3つだけでなくたくさん出 ていることが分かる。これは (もちろん) 倍音成分を含んでいるためである。
8.6.4 高い音をカットしてみる
ド・ミ・ソの基音の高さは、440×2−9/12, 440×2−5/12, 440×2−2/12Hz である。
440.0*2^(-{9, 5, 2}/12)
結果は {261.626, 329.628, 391.995}, つまりド、ミ、ソの周波数は 261.6, 329.6, 392.0 Hz で
ある。
cutoff[f_] := Join[Table[1, {n, f + 1}], Table[0, {n, sr - 2*f - 1}], Table[1, {n, f}]];
c2=c*cutoff[500];
ListPlot[Abs[c2], Joined -> True, PlotRange -> All]
cutoff[] は、f Hz より高い周波数成分はカットするための補助関数である(先頭に f+ 1 個の1, 末尾に f 個の1, その他はすべて0 という数値のリストを返す)。
c2 は 501 Hz 以上の成分を含んでいない。ド・ミ・ソの基音の部分だけしか含んでいない (2倍音
以上はカットしてある)。
10 000 20 000 30 000 40 000
0.002 0.004 0.006 0.008 0.010 0.012 0.014
逆離散Fourier変換して (音声信号に戻して) 再生
tbl2 = Re[InverseFourier[c2, FourierParameters -> {-1, -1}]];
ListPlay[tbl2, SampleRate -> sr]
501 Hz 以上をカットした音を再生して聴いてみる。(注意 以前のMathematicaでは、実部を取る
ために Re[]を施す必要がなかったが、最近の Mathematica では、tbl2 に少しでも 0 でない虚部 があると、ListPlay[tbl2,· · ·] が実行されなかった。)
その他、カットする周波数を色々変えてやってみる。
do = Re[InverseFourier[c*cutoff[300], FourierParameters -> {-1, -1}]];
domi = Re[InverseFourier[c*cutoff[360], FourierParameters -> {-1, -1}]];
domiso = Re[InverseFourier[c*cutoff[450], FourierParameters -> {-1, -1}]];
domiso2 = Re[InverseFourier[c*cutoff[900], FourierParameters -> {-1, -1}]];
original = Re[InverseFourier[c, FourierParameters -> {-1, -1}]];
ListPlay[do, SampleRate -> sr]
ListPlay[domi, SampleRate -> sr]
ListPlay[domiso, SampleRate -> sr]
ListPlay[domiso2, SampleRate -> sr]
ListPlay[original, SampleRate -> sr]
色々聴き比べてみてどうだろうか。
do はドの基音だけなのでドと聞こえる?もっとも倍音が含まれていないので、あまりピアノらし くない。
試しにやってみる? 指定した範囲の周波数成分を抜き出すことによって、ミの音だけを聴くため にはどうすれば良いか。
やり方と、その説明、そうして出来たミの音のファイルの3点をまとめよ。