【Praat】wavファイルをまとめてF0分析し、結果をcsvに出力【Praat script】

# はじめに
音声分析って、面倒ですよね…

分析ソフト(GUI)で音声ファイルを開いて、F0平均値を取得して、Excelにコピペして、あれ、この結果、どのファイルのだっけ…（絶望）

そんな貴方におすすめ、音声分析ソフト[Praat](http://www.fon.hum.uva.nl/praat/)のマクロ機能[^1]「Praat script」！

Praat scriptを使って、F0分析をバッチ処理でぶん回し、結果をまとめてcsvに出力しましょう。

できたcsvはPandas, R, Excelなどでお好きなように弄ってください。

**退屈な分析を楽チンに。そう、Praat scriptならね。**

# 環境

```
appendInfoLine(praatVersion$)
> 6.1.04
```

Praat scriptは後方互換性があるので、Praatが上記バージョン以上なら問題なく動くと思います。
（不具合があった場合はコメントで教えていただければ幸いです。)

# コード
[GitHub - Syuparn/praat_batch_analysis: Extract F0 and Intensity of all .wav files, then save to .csv table](https://github.com/Syuparn/praat_batch_analysis)


- f0statistics.praat
指定したディレクトリ内の全wavファイルのF0平均、最大値、最小値を(1つの)csvファイルに集計し出力

- f0contours.praat
指定したディレクトリ内の各wavファイルのF0時系列データを、音声ファイルと同名のcsvファイルに出力
（単位は[Hz]）

- intensitystatistics.praat
指定したディレクトリ内の全wavファイルのインテンシティ平均、最大値、最小値を(1つの)csvファイルに集計し出力
（単位は[dB]）

ループ内の分析処理を変えればフォルマント等別の分析もバッチ処理できると思います。(丸投げ)


# 使い方

（例：`f0contours.praat` （他２つも同様））

Praatを起動して

<img width="392" alt="praat_open.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239887/b6795ddb-7958-a2d7-a2a1-681e36849c3d.png">

「Praat」→「Open Praat script」→「Run」(or Ctrl+R)
で実行します。

<img width="692" alt="run.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239887/7d8b8960-e65f-b4fb-fce4-a100a44b753b.png">


実行するとフォームが出てくるので、wavファイルのあるディレクトリを指定します。

<img width="391" alt="dialog.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239887/af8f0454-6b37-aa0a-665b-0a969c718b1e.png">


終わり！

これで、ディレクトリ内の全wavファイルの分析結果がcsvファイルに書き出されました。


## コマンドラインから実行
GUIを使わずにコマンドラインからスクリプトを実行することもできます。

```
> .\Praat.exe --run --utf8 C:\path\to\f0contours.praat C:\path\to\wavs 75 600 0
```

(ただし、デフォルト引数が代入されないため、すべての引数を明示的に渡す必要があります)


# コード詳細
## Praat Scriptの文法について
（マニアックな話になるので、「とにかく使いたい！」という場合は読み飛ばしてください）


Praat scriptは、基本的には多くの手続き型言語と同じ文法です。
ぱっと見でも大体のコードの流れが掴めると思います。

Praat script公式リファレンス
[http://www.fon.hum.uva.nl/praat/manual/Scripting.html](http://www.fon.hum.uva.nl/praat/manual/Scripting.html)

一方、他の言語と大きく違う箇所が2つあります。

### ユーザー関数はない(procedureしか定義できない)

Praat scriptでは、組み込み関数以外に関数は存在しません。ユーザーが定義できるのはprocedureだけです。procedureは戻り値を返すことができません(VBAのsubプロシージャのようなもの)。

```:sample.praat
# ("#": 行頭コメント、";": 行中コメント)

procedure hello(.name$)
  appendInfoLine("hello, ", .name$)
endproc

# 呼び出しは先頭に"@"
@hello("Tom") ; "hello, Tom"
```

### ローカル変数はprocedureのフィールド

Praat scriptでは、頭に"."をつけるとローカル変数、付けないとグローバル変数になります。

```:sample.praat
foo$ = "global"

procedure hoge()
  .foo$ = "local"
  appendInfoLine(foo$) ; "global"
  appendInfoLine(.foo$) ; "local"
endproc

@hoge()
# appendInfoLine(.foo$) ; error
```

ここまでは普通の言語と同じです。
違うのは、procedure内の変数に外部からアクセスできるところです。

言い換えると、ローカル変数はスコープを抜けても永遠に生き続けます。

```:procscope.praat
procedure myProc()
  .val = 1
endproc

@myProc()
appendInfoLine(myProc.val) ; 1
```


procedureから値を返したい時に、このトリックを使っています。

```:returnproc.praat
procedure add(.x, .y)
  # ローカル変数.returnに結果を束縛
  .return = .x + .y
endproc

@add(2, 3)
# ローカル変数add.returnを参照
appendInfoLine(add.return) ; 5
```


# 最後に
当記事では、Praat scriptを使ってF0とインテンシティの分析、集計を自動化しました。

ですが、Praat scriptで自動化できるのはこれだけではありません...
PraatのGUIで可能な**全ての**処理はPraat scriptで自動化可能です。

Praatメイン画面から
「Praat」→「New Praat script」→「Edit」→「Paste History」で
GUIで行った作業の履歴をコード化したものが得られます[^2]。
「この作業何度も繰り返しているような…」と思ったら、ぜひこのコマンドを使ってみてください。
コードを汎用化すれば、自動化の始まりです！

後はPraatにまかせて、コーヒーを飲んで一休みしましょう。

[^1]: マクロと言ってもただのコマンドではなく、れっきとしたチューリング完全な言語です。

[^2]: 残念ながら、表示やマウスでの範囲選択は上手くコード化されませんが…