Praatスクリプト入門(2):音を合成してみる

概要
Praatスクリプトの入門講座その2。Praatの音声合成機能を使って、簡単な音を合成する。Praatのメニューをスクリプトから呼び出すことと、メッセージを表示することなど。

GUIメニューから音を合成しよう

本入門講座の目次は「Praatスクリプト入門:目次」というページをご覧いただきたい。また、前回の講座を見たい人は「Praatスクリプト入門(1):はじめに」というページをご覧いただきたい。

まず、Praatを使って音を合成するところから始めよう。

音の本質は波だ。単純な波であれば、それを数式で表すことができる。このため、適当な数式があれば、音を合成することができる

周波数と正弦波について調べてみよう
  1. 周波数とは何か。ヘルツとはどんな単位か。
  2. 周波数が高くなると、音はどうなるか。
  3. $-2\pi \leq x \leq 2\pi$の範囲で、正弦関数$\sin(x)$のグラフを描け。ここで、ラジアンによる角度の表し方に注意。普通の角度の表し方での360度は、ラジアンにすると$2\pi$になる。180度ならば$\pi$で、90度ならば$\displaystyle\frac{\pi}{2}$となる。
  4. 正弦波というのはどんな波か。音波としての正弦波はどのような性質があるか。

理論的なことはさておき、Praatで実際に音を合成してみよう。今回は、1000 Hzの正弦波を作っていく。なお、1000 Hzの正弦波の音のファイルを用意したので実際に聞いてもらいたい。

まず、Praatを起動しよう。いくつかウィンドウが出てくるが、このうち、Praat Objects というウィンドウを選ぶ。使っているコンピュータによって表示が異なるかもしれないが、ウィンドウの上の方にメニューが並んでいるはずだ。そのメニューの中から New を選んでクリックしよう。すると、詳しいメニューが開くので、Sound にマウスを合わせ、Create Sound from formula… [1] を選んでクリックしよう。

すると、色々な入力欄があるウィンドウが出てくる。このウィンドウのタイトルは Create Sound from formula となっているはずだ。このウィンドウに色々入力することで、どういう音を合成するのかをPraatに伝えることができる。

入力欄を1つずつ見ていこう。

一番上にあるのが、Nameという入力欄である。ここには、作成する音声の名前を入力する。分かりやすい名前をつけておくと、どんな音声だったか後で確認しやすくなる。実際にプログラムを書く段階にはまだ入っていないけれども、「分かりやすい名前をつける」ということは、プログラムを書く際の重要な心構えの1つになる。今から「分かりやすい名前をつける」ということを心がけよう。ここでは、1000 Hzの正弦波 (sine wave) を作るので、sineWave1000 と入れておこう。間にスペースなどを入れず、一気に sineWave1000 と入力してほしい。

sineWave1000のように、単語の区切りを大文字で表す手法のことをキャメルケースと言う。プログラミングでは、単語を区切るためにスペースを入れられない場合にキャメルケースが使われることがある。キャメルケースを使う場合、2番目以降の単語の最初の文字を大文字にする。1番目の単語の最初の文字は大文字にする場合もあるが、ここでは小文字にする。例えば、“first name”なら firstName に、“special blend tea”なら specialBlendTea になる。

次の入力欄を見てみよう。Number of channels (e.g. 2 = stereo) とあるはずだ。これは、作成する音声のチャンネル数を指定する欄だ。チャンネル数が2の場合、ステレオ音声となり、イヤホンで聴いた場合、右と左とで違った音声が流れることになる。これに対して、チャンネル数が1の場合は、モノラルとなる。このときは、右と左とで音が異なるようなことはなく、音が1種類だけになる。この入門講座ではモノラル音声で構わないので、この欄には 1 と入れておいてほしい。

その次の Start time (s) は音声の始まる時間を秒単位で入力する。とりあえず 0.0 と入力しよう。

また、その次の End time (s) は音声の終わる時間を秒単位で入力する。とりあえず 1.0 と入れておこう。開始時間を0.0秒、終了時間を1.0秒と設定したから、音声の持続時間はちょうど1秒になる。

次に、 Sampling frequency (Hz) という欄で作成する音声のサンプリング周波数を指定する。サンプリング周波数とは、音をどれだけきめ細かに合成するかを示す値だ。値が大きければ大きいほど、きめ細かなものになる。ここでは 44100 と入れておく。1秒を44100の点で表現することになる。ここでのサンプリング周波数と、これから合成する正弦波の周波数は、周波数という名前が付いている点では同じだが、意味合いが異なる。サンプリング周波数はあくまでも合成される音のきめ細かさを示すものだ。

最後に、 Formula の欄に、合成したい音声がどのようなものなのかを数式で書き入れる。1000 Hzの正弦波の数式は、 sin(2*pi*1000*x) となるので、これをそのまま入力しよう。なお、数式の中のアスタリスク (*) はかけ算の記号である。例えば、 3*2 は 3×2 という意味で、答えは6になる。また、 pi は円周率の$\pi$を示す。

かけ算の記号としてアスタリスク (*) を用いるのは、Praatに限ったことではない。他のプログラミング言語でも同様だ。

かけ算に限らず、プログラミング言語での数式の書き表し方は、日常生活での数式の書き表し方と異なる点がある。この書き表し方を覚えておくと、Praatに限らず、他のプログラミング言語を使う際にも役立つだろう。

  • かけ算:かけ算は×の代わりに、アスタリスク (*) を用いて表す。例えば、4×7 ならば、 4*7 と表記する。
  • 割り算:割り算は÷の代わりに、スラッシュ (/) を用いて表す。例えば、9÷3 ならば、 9/3 と表記する。
  • 足し算と引き算:足し算と引き算に関しては、日常生活と同じように書き表す。例えば、足し算なら 2+1 のように、引き算なら 6-5 のように書き表せばよい。
  • 累乗:84のような累乗は、サーカムフレックス (^) を用いて、 8 ^ 4 のように表記する。なお、84 = 8 × 8 × 8 × 8 である。

すべて入力し終わったら、ウィンドウの下の方にある OK というボタンを押そう。すると、先ほどまでの入力ウィンドウが消え、新しい音声が加わり。 Praat Objects ウィンドウを見ると、 1. Sound sineWave1000 というのが選択された状態になっているはずだ。なお、操作をやり直したり、繰り返したりした場合、 1. Sound sineWave1000 の最初の数字は、 1 でなく、別の数字になっている可能性がある。別の数字になっていても特に問題はない。

さて、合成した音声を聞いてみよう。 1. Sound sineWave1000 が選択された状態のままで、 Praat Objects ウィンドウの Play というボタンを押す。これで音声が再生される。高く澄んだ音が聞こえただろうか。もし、うまく音が聞こえないようだったら、コンピュータ、スピーカーあるいはイヤホンの音量を調節してみてほしい。それでもうまくいかないようだったら、最初からやり直そう。

音が無事聞こえたら、合成した音声はとりあえず不要となる。そこで、 1. Sound sineWave1000 が選択された状態のままで、 Praat Objects ウィンドウの Remove というボタンを押す。すると、先ほど作成した音声が消える。

スクリプトから音を合成しよう

さて、先ほどGUIメニューで処理したことを今度はスクリプトを使って処理してみよう。

Praatのスクリプトウィンドウを出して [2] 、そこに以下のように入力する。大文字と小文字を間違えるとうまくいかないので注意してほしい。

do("Create Sound from formula...", "sineWave1000", 1, 0.0, 1.0, 44100, "sin(2*pi*1000*x)")
do("Play")
do("Remove")

入力し終わったら、早速実行しよう。Praatのスクリプトを実行するには次のようにする。まず、Praatのスクリプトウィンドウの上の方にあるメニューから Run を選んでクリックする。すると、詳しいメニューが出てくるので、そこから Run を選んでクリックすればよい。こうすると、1000 Hzの正弦波の音が聞こえるはずだ。もし聞こえなかったり、エラーメッセージが出たりした場合は、自分の入力したスクリプトがこのページで掲げたものと違っていないか一字一句確認してほしい。コンマやダブルクォーテーションを入れる場所が間違っていたり、大文字と小文字を取り違えたりするだけでも、うまく動かなくなる可能性がある。そのような誤りがないかよく見てほしい。

メニューからスクリプトを実行する代わりに、キーボードショートカットを使って実行することもできる。Windowsの場合は、 Ctrl キーを押しながら R キーを押すとスクリプトが実行される。Macintoshの場合は、⌘ を押しながら R キーを押そう。

今入力したスクリプトについて解説したい。このスクリプトでは、どの行も do という記述から始まっている。そして、 do の後に “Create Sound from formula…” や “Play” といったダブルクォーテーションで囲まれた記述がある。これらは、先ほどマウスを使って操作したときのメニューあるいはボタンの名前である。Praat のスクリプトでは、 do の後に、GUIメニューの名前を並べることで、そのメニューを実行することができる。だから、マウスを使ってメニューから操作しなくても、スクリプトで do を使って記述することで同じことができるのだ。

Praatのスクリプトでは、 do を使って、GUIメニューでの操作と同じことが実行できる。

さて、 do についてもう少し詳しく説明しよう。まず、 do(“……”) というのが基本構造なので、これをよく覚えておこう。 do の後に丸括弧をおき、その後にGUIメニューの名前をダブルクォーテーションに囲んで入れるのだ。つまり、 do(“……”) というのが基本構造で、……にメニューの名前が入る。先ほどのスクリプトの2行目にあったように、 Play ならば do(“Play”) となる。同様に、 Remove をスクリプトから実行したければ、 do(“Remove”) とする。

先ほどのスクリプトの1行目は、 do(“……”) という基本構造よりずっと複雑に見えるかもしれない。しかし、基本構造をしっかりおさえれば理解は難しくない。

GUIメニューから音を合成したときは、メニューの中から、 Create Sound from formula… というものを選んだ。スクリプトから実行する場合は、 do(“……”) という基本構造に従えば、 do(“Create Sound from formula…”) となりそうである。しかし、これではうまくいかない。先ほどメニューから音を合成したときは、音を設定するためのウィンドウが出てきて、そこで色々と設定をした。どういう音なのか設定しないかぎり、Praat はどんな音を合成していいか分からないのだ。だから、スクリプトで do を使うときも、音の設定を Praat に教える必要がある。

Praat のメニューには、 Create Sound from formula… のように、末尾にピリオドが3つあるものと、 Play のように末尾に特に何もないものがある。末尾のピリオドの有無には、ちゃんとした意味がある。末尾にピリオドが3つあるメニューを実行すると、細かな設定をするためのウィンドウが出現する。これに対して、末尾に特に何もないものは、設定のためのウィンドウが出現しない。ただし、スクリプトの場合は、末尾にピリオドが3つあるメニューを実行しても、細かな設定をするためのウィンドウが出現しない。このため、設定内容をあらかじめ書いておく必要が出てくる。

先ほど、スクリプトの1行目に、 do(“Create Sound from formula…”, “sineWave1000”, 1, 0.0, 1.0, 44100, “sin(2*pi*1000*x)”) と記述した。こう記述することで、1000 Hzの正弦波が合成される。この部分をもっと詳しく見てみよう。まず、 do がある。これは「GUIメニューでのコマンドを実行せよ」という意味だった。しかし、 do だけではどんなコマンドを実行するか分からない。ここの do がすることの詳細は、丸括弧〔“(”と“)”〕の中に書かれている。というわけで、丸括弧の中身を見よう。丸括弧の中身はコンマによって区切られているのが分かるだろう。

理論的な話をすると、Praatのスクリプトにおいて、 do は関数の一種である。プログラミングにおける「関数」は数学における「関数」とは意味が若干異なる。極めて単純にいえば、プログラミングでの関数とは、何かしらの作業を実行して結果を出すものだ。「関数」の代わりに「命令」と言う場合もある。Praatのスクリプトでは関数を並べることで様々な作業を行う。関数は、引数というものをとることがほとんどだ。引数を用いることで関数がする作業を細かく設定することができる。Praatでは、関数の後に丸括弧をおいて、その中に引数を記述する。例えば、 sqrt という平方根を求める関数がある。この関数ではどんな数の平方根を求めるのかということを引数の形で指定する必要がある。3の平方根を求めたければ、 sqrt(3) とするのだ。括弧の中にあるのが引数というのはこういうことだ。また、引数を複数持つ関数もある。このような場合、ある引数と別の引数との境界をはっきりしめす必要が出てくる。ここではコンマを使って引数と引数を区切る。引数がコンマによって区切るというのは do に限りません。Praatのスクリプトではみなそうである。なお、他のプログラミング言語でもコンマで区切るという場合が多い。

コンマによって区切られているわけだから、 do(“Create Sound from formula…”, “sineWave1000”, 1, 0.0, 1.0, 44100, “sin(2*pi*1000*x)”) の丸括弧の中身は、以下のように7つの部分に分かれることになる [3]

最初の “Create Sound from formula…” は、GUIメニューの音を作るコマンドである Create Sound from formula… を実行するということを示している。2番目の “sineWave1000” から7番目の “sin(2*pi*1000*x)” までがどのような音を作るかの設定を示している。この設定内容は、メニューから選んだときに出てくる細かな設定をするためのウィンドウの入力欄に対応している。細かな設定をするためのウィンドウの入力欄に入れるべきものを順に並べているのだ。細かな設定をするためのウィンドウで一番上にあったのは Name という入力欄だった。この入力欄の内容が “sineWave1000” に対応する。次に Number of channels (e.g. 2 = stereo) という入力欄が 1 に、 Start time (s) という入力欄が 0.0 にといったように順に対応している。

ところで、 do の中身は、ダブルクォーテーションに囲まれているものとそうでないものがある。 “Create Sound from formula…” や “sineWave1000” はダブルクォーテーションで囲まれているが、 0.0 や 44100 は囲まれていなん。単純に言うと、数値はダブルクォーテーションで囲み、文字列はダブルクォーテーションで囲むということになる。 0.0 や 44100 というのは数値だからダブルクォーテーションで囲まなかったのだ。

数値はダブルクォーテーションで囲まず、文字列はダブルクォーテーションで囲む。

色々書いてきたが、まとめに入りたい。GUIメニューでの操作は、スクリプトから do を使って実行することができる。メニューの名前を do の後の括弧の中にそのまま書けば良い。詳細設定を行う場合も、 do の後の括弧の中に記述する。だから、先ほどのスクリプトの1行目では、 do(“Create Sound from formula…”, “sineWave1000”, 1, 0.0, 1.0, 44100, “sin(2*pi*1000*x)”) と入力し、音声の合成をした。その次に、メニューから実行したときは、 Play というボタンを押して音声を再生した。これをスクリプトで実行するには、スクリプトに do(“Play”) と書けば良いのだ。実際、先ほどのスクリプトでは2行目に do(“Play”) と書いたのだった。さらに、音を消すときに Remove というボタンを押した。これをスクリプトから実行するには、 Play の例と同様に、 do(“Remove”) とスクリプトに書けば良い。つまり、メニューから行った操作を1つずつスクリプトに書いていけば、メニューから実行した場合と同じことができるのだ。

do の働きについて確認し、のこぎり波の音の合成に応用しよう
  1. 先ほどのスクリプトの1行目の do の後の括弧の中にある要素が、GUIメニューで合成したときの細かな設定をするためのウィンドウの入力欄とどう対応しているか述べてください。
  2. のこぎり波の音はどんな音だろうか。調べてみよう。
  3. 正弦波の合成の例にならって、Praatのメニューから、1000 Hzののこぎり波を合成してみよ。なお、数式として 1000*x-floor(1000*x) と入力せよ。なお、参考として、1000 Hzののこぎり波の音のファイルを掲載しておく。
  4. 今度は、Praatのスクリプトから、1000 Hzののこぎり波を合成してみよ。
のこぎり波の波形
のこぎり波の波形

メッセージを表示しよう

先ほど作った1000 Hzの正弦波を合成するためのスクリプトは次のようなものであった。

do("Create Sound from formula...", "sineWave1000", 1, 0.0, 1.0, 44100, "sin(2*pi*1000*x)")
do("Play")
do("Remove")

これで特に問題はないのだが、音を合成して再生するだけでは無愛想な感じだ。そこで、音を合成したら、メッセージを表示するようにしてみよう。

Praatのスクリプトでメッセージを表示するには、 writeInfoLine という命令を使う。 writeInfoLine は、 writeInfoLine の後の括弧の中身を表示する命令である。先ほどのスクリプトの途中に、 writeInfoLine(“Created sineWave1000”) という命令を書いた行を付け加えてほしい。付け加える際に、 writeInfoLine の大文字と小文字の使い分けに気をつけよう。 writeinfoline のように全部小文字にしてしまうとうまく動かなくなるのだ。

修正されたスクリプトは以下のようになるはずだ。

do("Create Sound from formula...", "sineWave1000", 1, 0.0, 1.0, 44100, "sin(2*pi*1000*x)")
writeInfoLine("Created sineWave1000")
do("Play")
do("Remove")

上記のスクリプトを実行すると、 Praat Info というウィンドウにCreated sineWave1000と表示されるはずである。ここで、 writeInfoLine を使う際に、 Created sineWave1000 をダブルクォーテーションで囲んでいることに注意してほしい。これは do の時と同じ話で、文字列なのでダブルクォーテーションで囲んでいるのだ。

メッセージをもう1つ追加してみよう。

do("Create Sound from formula...", "sineWave1000", 1, 0.0, 1.0, 44100, "sin(2*pi*1000*x)")
writeInfoLine("Created sineWave1000")
do("Play")
writeInfoLine("Played sineWave1000")
do("Remove")

上記のスクリプトを実行すると、 Praat Info ウィンドウにまずCreated sineWave1000と表示され、正弦波の再生が終わると、Created sineWave1000に代わってPlayed sineWave1000と表示されるはずだ。

ところで、Played sineWave1000が表示された後、Created sineWave1000というメッセージはどうなってしまったのだろうか。このスクリプトでは、Played sineWave1000が表示されると、Created sineWave1000というメッセージは消去され、どこにも残されない。

実は、 writeInfoLine という命令は、それまで Praat Info ウィンドウに表示していたものを消した上で新しいメッセージを表示する命令なのだ。このため、一度 writeInfoLine を使うと、今まで Praat Info ウィンドウに表示されていたものはなくなってしまう。

今まで表示していたものを消さずに新しいメッセージを追加するには、 writeInfoLine の代わりに、 appendInfoLine [4] を使う。

以下は、先ほどのスクリプトの4行目の writeInfoLine を appendInfoLine に置き換えたものだ。このスクリプトを実行すると、 Praat Info ウィンドウには、Created sineWave1000がまず表示され、その下の行にPlayed sineWave1000が表示されるはずだ。

do("Create Sound from formula...", "sineWave1000", 1, 0.0, 1.0, 44100, "sin(2*pi*1000*x)")
writeInfoLine("Created sineWave1000")
do("Play")
appendInfoLine("Played sineWave1000")
do("Remove")

Praatのスクリプトでは、メッセージを新しく出すときには writeInfoLine を、その後に追加でメッセージを出すときには appendInfoLine を使う。

多くの場合、スクリプトの最初のところだけ writeInfoLine を用い、他は appendInfoLine を用いることになると思う。

なお、Praatのスクリプトでメッセージを表示する命令には、他に writeInfo と appendInfo というのがある。先ほど扱った writeInfoLine と appendInfoLine は、メッセージを表示した後、改行をする。これに対して、 writeInfo と appendInfo は改行をしない。つまり、 writeInfoLine(“foo”) を実行した上で、 appendInfoLine(“bar”) を実行すると、1行目にfooが表示され、2行目にbarが表示される。これに対し、 writeInfo(“foo”) を実行した上で、 appendInfo(“bar”) を実行すると、1行にfoobarと表示される。なお、 writeInfo は writeInfoLine と同様に、 Praat Info ウィンドウに表示していたものをすべて消してからメッセージを表示する。これに対し、 appendInfo は appendInfoLine と同様に今まで表示されているものの後にメッセージを表示する。

この回で扱った内容を復習しよう
  1. Praatのメニューで、ピリオド3つ( … )で終わっているものとそうでないものの違いは何か。
  2. 正弦波とは何か。Praatではどのようにして正弦波の音を作ることができるか。
  3. のこぎり波とは何か。Praatではどのようにしてのこぎり波の音を作ることができるか。
  4. writeInfoLine と appendInfoLine の違いは何か。

本入門講座の続きは「Praatスクリプト入門(3):周波数を変更してみる その1」というページをご覧いただきたい。

脚注
  1. “Create Sound from formula”とは、「数式から音を作る」という意味である。 []
  2. Praatのスクリプトウィンドウを出す方法は、本講座の第1回「はじめに」を参照のこと。 []
  3. 要するに、ここでは関数の do が、7つの引数を持っているということになる。 []
  4. 英語で“append”は「付け加える」という意味である。 []