トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索   ヘルプ   最終更新のRSS

MATLAB Note/音声の分析/VoiceSauce

Last-modified: 2015-08-28 (金) 07:57:18

VoiceSauce v1.19 を使って音声を解析する

  • VoiceSauce (Yen-Liang Shue, EDU, 2011) はフリーの音声解析ソフト
  • 解析可能な特徴量は以下の通り。
    F0
    Formants F1-F4
    H1(*)
    H2(*)
    H4(*)
    A1(*)
    A2(*)
    A3(*)
    H1(*)-H2(*)
    H2(*)-H4(*)
    H1(*)-A1(*)
    H1(*)-A2(*)
    H1(*)-A3(*)
    Energy
    Cepstral Peak Prominence
    Harmonic to Noise Ratios
    • アスタリスクがついているものは、Iseli & Alwan のアルゴリズムによって訂正した値(マニュアル参照)
    • 特徴量によっては STRAIGHT, Snack Sound Toolkit, Praat の3種類のアルゴリズムそれぞれの解析結果を出力してくれる
  • Limitations (以下、日本語訳したもの)
    • 推定するパラメータの多くはF0に依存するので、有声音しか意味のある結果になりません。 雑音の多さはF0推定の正確さに影響するため、推定値に影響を及ぼすかもしれません。
    • フォルマント周波数の修正がもっとも良く働くのは、F1が正確に推定されたとき、それはすなわちF0とF1が互いにあまり近くならないときです。 例えば、高い母音を話すピッチの高い音声は、不正確な結果を返すかもしれません。
    • 非英語名のフォルダに含まれている .wav ファイルはフォルマントの計算に失敗する場合があるようです。

MATLAB版を使ってみる

  • リンク先の 「Matlab m-code (v1.12 - Feb. 5, 2012)」より MATLAB 版をダウンロード
  • 以下、VoiceSauce Manual の手順に従って解説しています。
  • 以下、「MATLAB」と発音したサンプル英語音声を解析してみます。
     load mtlb;          %MATLABと発音した英語音声
     disp(strcat('サンプリング周波数は',  int2str(Fs), '[Hz] サウンドデータは mtlb'));
     sound(mtlb, Fs)     %再生
     wavwrite(mtlb, Fs, 32, 'mtlb_file.wav');  %量子化ビット数32でファイルを書き出す
  • まずは VoiceSauce.m を実行する
    • 下のウインドウが出る
      VS001.png
  • Parameter Estimation(音声解析)
    • .wavファイルの入っているディレクトリを選んで、.wavファイルを選んで「Start」>ちょっと待つ>「Processing Complete.」と出たら「Close」>Parameter Estimation ウインドウも閉じる
      VS002.png
      • "Process using 16kHz sampling rate"にチェックすると、各Wavファイルをサンプリングレート16000Hzに変換した後で解析されます。元々サンプリングレートが16000Hz未満のファイルを解析する場合は、このチェックを入れておかないとエラーが出るようです。
      • 【注意】指定したディレクトリ内に複数.wavがあったとき、どれかだけを指定して解析することはできない(?)
      • .wavファイルと同じ場所に解析結果のファイル(.wavと同名、拡張子なしのmat形式ファイル)ができる。
      • ファイルが長いと結構時間がかかる。
  • Output to text(.tsvファイル書き出し)
    • 音声解析がすんでいるなら、「Input .mat directory:」で解析結果ファイルのディレクトリを指定して、「Output file:」で出力先を選び、「Start!」を押す
      VS003.png
      • 「Output file;」欄で書き出すファイルの名前を指定します。デフォルトでは output.txt です。
      • 例えば filemtlb_file.txt のような形式で書き出される。
    • .TextGridも読み込めるから、Praatの解析結果を手軽に.tsv形式に変換するのにも使えるかも。
  • Parameter Display(解析結果表示)
    • 音声解析がすんでいるなら、「wav directory:」に.wavファイル、「mat directory:」に解析結果のファイルを指定して、結果の視覚化ができる。
      • 指定したディレクトリ内に、.wavや解析結果のデータが複数あるとうまくいかないかも。
    • 以下はF0(STRAIGHT, Snack, Praat)の解析結果。
      VS004.png
  • F0解析の結果についてもう少し詳しく見てみます。F0解析結果の検討 を参照して下さい。
  • Settings(設定)
    • 解析に使用する各種パラメータの設定。下記は全てデフォルト設定。
      VS005.png
  • フォルマント解析の結果は H1H2c などの声質パラメータの解析結果に影響するので、Formantsの項目は慎重に設定する必要があります。 H1-H2解析結果の検討 を参照して下さい。
    • デフォルトではSnackのフォルマントを声質パラメータの解析に使用する設定になっています(Formants > Used for parameter estimation: Snack)。
    • Praatを使用する設定に変えた場合(Formants > Used for parameter estimation: Praat)、(特に女性音声では)Number of formants = 5, Maximum formant = 5500 に変えたほうが良いかもしれませんPraat/フォルマント解析/フォルマント解析(burg法)のパラメータについてを参照して下さい*2
  • Outputs > Smoothing window size は、Output to text 機能を使って H1H2c などの声質パラメータをテキストファイルに書き起こす際のスムージング窓のサイズです。詳しくはH1-H2解析結果の検討 を参照して下さい。
  • Manual Data(手動データ入力)
    • ユーザが独自に解析したデータで、Parameter Estimation の再計算ができる。詳細は省略します。

特徴量の計算方法の詳細

  • 以下は、VoiceSauce Documentationの解説の一部分を引用して訳したものです。
  • F0*3
    • VoiceSauceは、倍音の位置を推定するためにF0を使います。 VoiceSauceはF0の測定値を2つの異なるアルゴリズムから推定可能です-STRAIGHT(Kawahara et al. 1998)またはSnack Sound Toolkit(Sjölander 2004)*4。 VoiceSauceは倍音の振幅を求めるために、デフォルトではSTRAIGHTのF0測定値を使います。STRAIGHTは、1ミリ秒の間隔でF0解析を行います。
  • Formants F1-F4*5
    • 最初の4つのフォルマントの周波数と帯域幅を求めるためにSnack Sound Toolkit(Sjölander 2004)を使用します。デフォルトでは共分散法を使い、プリエンファシス係数0.96、フレームシフト1ms、ウインドウ長25msです。フレームシフトは、STRAIGHTがF0を計算するときの値と同じにしています。
  • Harmonic and Formant Amplitude (H1, H2, H4, A1, A2, A3, H1-H2, H2-H4, H1-A1, H1-A2, H1-A3)*6
    • VoiceSauceは、4つのピッチ期間ウインドウ上で、ピッチに同期した倍音の大きさを計算します。これによって、固定された時間ウインドウで計算を行った際の変動を除外できます。標準的な最適化技術(F0からスペクトルのピークの位置を求める)によって倍音の位置を特定します。これは、非常に長いFFTウインドウを使うことに等しいです。
    • F0がSTRAIGHTによってうまく推定できなければ、H1を含むすべての値は不正確です。同様に一つ以上のフォルマントがSnackによってうまく推定できなければ、対応する値は不正確です。
    • もしF1の推定が間違っているならば、A1とH1-A1の値も間違いです。F1のエラーはおそらく、気息音、鼻音音、高ピッチの母音で多いです。
    • すべての振幅訂正は、正確なフォルマント値に依存します。 したがって、F0とフォルマントの推定結果をチェックすることを推奨します。
  • 【注】H1は基本周波数F0の振幅、H2は基本周波数F0の二倍音F0*2の振幅です。H1とH2の差を求めたものがH1-H2です。この値が大きいほどスペクトルの傾斜が急であることをあらわし、声質が息漏れ音になると考えられます(新谷ら, 2008 より引用)*7
  • 【注】H1とH2の値はフォルマント(スペクトル包絡の概形)の影響を受けて歪むので、フォルマントの値に基づいて修正を加えたH1*、H2*(下記)が提案されました。詳しくは Keating & Esposito 2006 を参照して下さい。
  • Harmonic and Formant Amplitude (*)*8
    • Iseli & Alwan (2004, 2006, 2007)のアルゴリズムによって、全ての倍音は母音のフォルマントの影響を考慮して修正することができます。
    • 振幅の値は、フォルマント周波数とSnack toolkitで求めた帯域幅を使って、各フレームごとに修正されます。たとえば、H1-H2の修正のためにF1とF2が使われます。H1-A3の修正のためにF1,F2,F3が使われます。これらの値は移動平均フィルタ(デフォルトで20ミリ秒の長さ)で滑らかにされます。
    • VoiceSauceでは、すべての修正された処置は、アスタリスクで示されます。 たとえば、H1*-H2*は、H1-H2の修正された形です。
  • Energy*9
    • エネルギーはthe Root Mean Square(RMS*10)で求めます。5つのピッチに基づく可変ウインドウ上の各フレームについて計算されます。可変ウインドウ間の相関関係を減らすために、エネルギーの値はF0を使って正規化されます。
  • Cepstral Peak Prominence*11
    • ケプストラムピークプロミネンス(CPP)は、Hillenbrand et al. (1994)のアルゴリズムに基づいて計算されます。計算では5つのピッチ区間に等しい可変ウインドウが使われました。 ハミング窓にかけた後で、データは実数ケプストラムに変換されます。ピッチ区間のケフレンシーの周囲で最大値の探索によってCPPが求まります。このピークは1ms~最大ケフレンシーの間の線形回帰によって正規化されます。
      • 【注】CPPの解説としてRachel Garrett, 2013 p.18-19 が参考になります。対数パワースペクトラムのフーリエ逆変換によってケプストラムを求めて、ケプストラムの横軸(ケフレンシー軸)上で回帰直線を求め、もっとも突出したケプストラムの縦軸(振幅)の値と、回帰直線との間の距離を求めたものが、CPPです。
  • Harmonic to Noise Ratios*12
    • 倍音-ノイズ比率(HNR)の値はde Krom (1993)のアルゴリズムで求めます。5つのピッチ区間と同等な可変ウインドウを使って、ケプストラムのピッチ構成要素のリフタリングと、倍音のエネルギーを雑音と比較することによってHNRが求まります。HNR05は0-500Hzの間でHNRを測ったもの、HNR15は0-1500Hzの間でHNRを測ったもの、HNR25は0-2500Hzの間でHNRを測ったものです。

F0解析結果の検討

  • VoiceSauceを使って、サンプル音声 mtlb_file.wav を解析した結果 filemtlb_file.mat と、それを Output to text 機能で書き出した filemtlb_file.txt を比較します。
    %mtlb_file.matの検討
    load('mtlb_file.mat');
    subplot(2,1,1); plot([strF0 pF0 sF0]); ylim([-100 500]);
    title('VoiceSauce - MAT'); legend('strF0', 'pF0', 'sF0');
    
    %mtlb_file.txtの検討
    fid = fopen('mtlb_file.txt', 'rt');
    % ヘッダ情報(1行目の文字列)を得る
    text = textscan(fid, '%s', 1, 'delimiter', '\n');
    % データ部(文字列2列、数値67列、区切り記号はタブ)を得る
    text = textscan(fid, strcat('%s %s ', repmat(['%f '],1,67) ), 'delimiter', '\t');
    fclose(fid);
    % 42列目(strF0)を順番に表示してみる
    %for count = 1 : 1 : length(text{1})
    %     disp(text{5}(count));
    %end
    strF0 = text{42};
    sF0 = text{43};
    pF0 = text{44};
    subplot(2,1,2); plot([strF0 pF0 sF0]); ylim([-100 500]);
    title('VoiceSauce - TXT'); legend('strF0', 'pF0', 'sF0');
VoiceSauce_F0_1.png
  • PraatやSnackの計算結果は無声部が出力されていないのに対して、STRAIGHTの計算結果では無声部が補完されていることに注意してください。有声部の計算結果はSTRAIGHTが最も正確のようです。*13
  • MATファイルではpF0とsF0の無声部はNaN、Output to text で書き出したTXTファイルではpF0とsF0の無声部は0になっていることに注意してください。strF0のTXTファイルの結果にも0が含まれています。
  • VoiceSauce v1.19 のデフォルトパラメータ(Settingsで設定)で計算した strF0 と、STRAIGHTV40_007d のデフォルトパラメータで計算した F0 の値を比較してみます。以下、F0の解析には全て STRAIGHTV40_007d 付属の MulticueF0v14.m を使用しています。
    load mtlb;          %MATLABと発音した英語音声
    mtlb = resample(mtlb, 16000, Fs); %サンプリングレートを16000に変換
    Fs = 16000;
    
    %VoiceSauce v1.19 のデフォルトパラメータで計算した strF0
    strParam.F0searchLowerBound = 40;
    strParam.F0searchUpperBound = 500;
    [f0raw1, vuv] = MulticueF0v14(mtlb, Fs, strParam);
    [f0raw2] = MulticueF0v14(mtlb, Fs, strParam);
    subplot(2,1,1); plot([f0raw1 f0raw2]); ylim([-100 500]);
    legend('use vuv', 'no vuv'); title('VoiceSauce v1.19');
    
    %STRAIGHTV40_007d のデフォルトパラメータで計算した F0
    [f0raw1, vuv] = MulticueF0v14(mtlb, Fs);
    [f0raw2] = MulticueF0v14(mtlb, Fs);
    subplot(2,1,2); plot([f0raw1 f0raw2]); ylim([-100 500]);
    legend('use vuv', 'no vuv'); title('STRAIGHTV40 007d');
VoiceSauce_F0_2.png
  • VoiceSauceとSTRAIGHTで、無声部の推定値がやや変わりますが、有声部では近い値になっています。
  • MulticueF0v14.m では 第二出力引数を指定するかどうかによって第一出力引数の結果が変わります。 第二出力 vuv を指定すると、第一出力 F0raw は有声・無声に関わらずF0推定値が出力されます。vuv は有声のフレームが1、無声のフレームが0になります。第二出力 vuv を指定しない場合、無声部の F0raw は0になります。

フォルマント解析結果の検討

  • mtlb_file.wav を解析した結果 filemtlb_file.mat と、それを Output to text 機能で書き出した filemtlb_file.txt を使って、MATファイルとTXTファイルの結果、及び Praat と Snack のフォルマント解析の結果を比較します(Settings > Formants > Praat > Number of formants の値は 5 を指定)。
    %mtlb_file.matの検討
    load('mtlb_file.mat');
    subplot(2,1,1); plot([pF1 sF1 pF2 sF2]); ylim([-100 3000]);
    title('VoiceSauce - MAT'); legend('pF1', 'sF1', 'pF2', 'sF2');
    
    %mtlb_file.txtの検討
    fid = fopen('mtlb_file.txt', 'rt');
    text = textscan(fid, '%s', 1, 'delimiter', '\n');
    text = textscan(fid, strcat('%s %s ', repmat(['%f '],1,67) ), 'delimiter', '\t');
    fclose(fid);
    sF1 = text{47};
    sF2 = text{48};
    pF1 = text{51};
    pF2 = text{52};
    subplot(2,1,2); plot([pF1 sF1 pF2 sF2]); ylim([-100 3000]);
    title('VoiceSauce - TXT'); legend('pF1', 'sF1', 'pF2', 'sF2');
VoiceSauce_Formant.png
  • 有声部では、PraatとSnackで第二フォルマントの値はあまり違いませんが、第一フォルマントの値は異なります。
  • MAT > TXT 変換ではほとんど違いはありませんが、MATファイルでNaNになっていた開始・終了部のフレーム(無声区間)は0に置き換わっています。

H1-H2解析結果の検討

  • H1-H2に関するものとして、MATファイルには変数 H1, H2, H1H2c が含まれています。Output to text によって書き出されたTXTファイルには、H1u, H2u, H1c, H2c, H1H2u, H1H2c 列が書き出されています。
    • H1c, H2c は、Iseli & Alwan (2004, 2006, 2007)のアルゴリズムによって修正された(フォルマント周波数帯による振幅への影響を除外した)H1, H2 の値です。H1u, H2u は、修正前の H1, H2 の値です。
  • どのアルゴリズムのフォルマント解析結果を使うかによって、H1c, H2c の値は変わることに注意する必要があります(上で見たようにPraatとSnackではフォルマントの推定値が異なる)。どのアルゴリズムを使うかは、Settings > Formants > Used for parameter estimation: で変更できます。デフォルトではSnackが選ばれています。
  • 比較のため、以下の4つのファイルを使います。
  • これらの値の違いを確認します。
    %mtlb_file.matの検討
    load('mtlb_file.mat');
    sna_H1 = H1;
    sna_H2 = H2;
    sna_H1H2c = H1H2c;
    load('mtlb_file_praatFormantUsed.mat');
    pra_H1 = H1;
    pra_H2 = H2;
    pra_H1H2c = H1H2c;
    
    subplot(3,1,1);
    plot([sna_H1H2c pra_H1H2c (sna_H1 - sna_H2) (pra_H1 - pra_H2)]); ylim([-20 30]);
    title('VoiceSauce - MAT'); legend('sna H1H2c', 'pra H1H2c', 'sna H1-H2', 'pra H1-H2');
    
    %mtlb_file.txtの検討
    fid = fopen('mtlb_file.txt', 'rt');
    text = textscan(fid, '%s', 1, 'delimiter', '\n');
    text = textscan(fid, strcat('%s %s ', repmat(['%f '],1,67) ), 'delimiter', '\t');
    fclose(fid);
    sna_H1c = text{6};
    sna_H2c = text{7};
    sna_H1u = text{27};
    sna_H2u = text{28};
    sna_H1H2c = text{13};
    sna_H1H2u = text{35};
    fid = fopen('mtlb_file_praatFormantUsed.txt', 'rt');
    text = textscan(fid, '%s', 1, 'delimiter', '\n');
    text = textscan(fid, strcat('%s %s ', repmat(['%f '],1,67) ), 'delimiter', '\t');
    fclose(fid);
    pra_H1c = text{6};
    pra_H2c = text{7};
    pra_H1u = text{27};
    pra_H2u = text{28};
    pra_H1H2c = text{13};
    pra_H1H2u = text{35};
    
    subplot(3,1,2);
    plot([sna_H1H2c pra_H1H2c sna_H1H2u pra_H1H2u]); ylim([-20 30]);
    title('VoiceSauce - TXT'); legend('sna H1H2c', 'pra H1H2c', 'sna H1H2u', 'pra H1H2u' );
    
    subplot(3,1,3);
    plot([(sna_H1c - sna_H2c) (pra_H1c - pra_H2c) (sna_H1u - sna_H2u) (pra_H1u - pra_H2u)]); ylim([-20 30]);
    legend('sna H1c-H2c', 'pra H1c-H2c', 'sna H1u-H2u', 'pra H1u-H2u');
VoiceSauce_H1H2_1.png
  • まず、MATファイルとTXTファイルの結果が違うことに注意してください。TXTファイルが出力するH1-H2値はスムージングがかかっています。*14
    • スムージングの値は Settings > Outputs > Smoothing window size で設定します。この値を0にするとスムージングはかかりません(MATファイルとTXTファイルの値は完全に一致します)。
  • TXTファイルで、H1H2u は H1u - H2u、H1H2c は H1c - H2c と完全に一致しています(中段と下段の比較)。
  • MATファイルの H1 - H2 の値や、TXTファイルの H1u - H2u、および H1H2u の値は、Formants > Used for parameter estimation: にPraatを指定してもSnackを指定しても全く同じ値になっています。H1, H2 (H1u, H2u) の値はF0の推定値のみに依存するため、フォルマント解析にどのアルゴリズムを使用したかは関係ありません(デフォルトでは STRAIGHT が推定したF0によって決まる)。
  • MATファイルの H1H2c の値や、TXTファイルの H1c - H2c、および H1H2c の値は、フォルマント解析のアルゴリズムによってかなり違いがあります。
VoiceSauce_H1H2_2.png
  • スペクトログラムの定常部分(特に母音の定常部分)では、H1H2、H1H2c(Praat)、H1H2c(Snack) の違いが少なく、それ以外の部分では差が大きくなっています。
  • STRAIGHTで求めたF0、H1、H2、Snackで求めたF1、F2から H1H2c を求めてみます。filemtlb_file.mat を使います。VoiceSauceのfunc_getBWfromFMT.mfunc_correct_iseli_z.m を実行可能にしておきます。
    % VoiceSauceの計算結果
    load('mtlb_file.mat');
    sna_H1 = H1;
    sna_H2 = H2;
    sna_H1H2c = H1H2c;
    fid = fopen('mtlb_file.txt', 'rt');
    text = textscan(fid, '%s', 1, 'delimiter', '\n');
    text = textscan(fid, strcat('%s %s ', repmat(['%f '],1,67) ), 'delimiter', '\t');
    fclose(fid);
    sna_H1u_sm = text{27};
    sna_H2u_sm = text{28};
    sna_H1c_sm = text{6};
    sna_H2c_sm = text{7};
    subplot(2,1,1); plot([(sna_H1 - sna_H2) sna_H1H2c (sna_H1c_sm - sna_H2c_sm)]); ylim([-20 30]);
    title('VoiceSauce'); legend('H1 - H2', 'H1c - H2c', 'H1c - H2c(smoothing)' );
    
    % STRAIGHTで求めたF0、H1、H2、Snackで求めたF1、F2から計算
    load('mtlb_file.mat');
    sna_H1 = H1;
    sna_H2 = H2;
    sna_F1 = sF1;
    sna_F2 = sF2;
    sna_B1 = func_getBWfromFMT(sna_F1, strF0, 'hm');  % Hawks & Miller, 1995 の関数でB1を推定
    sna_B2 = func_getBWfromFMT(sna_F2, strF0, 'hm');  % Hawks & Miller, 1995 の関数でB2を推定
    sna_H1c = sna_H1 - func_correct_iseli_z(strF0, sna_F1, sna_B1, Fs);
    sna_H1c = sna_H1c - func_correct_iseli_z(strF0, sna_F2, sna_B2, Fs);    % H1* を求める
    sna_H2c = sna_H2 - func_correct_iseli_z(2*strF0, sna_F1, sna_B1, Fs);
    sna_H2c = sna_H2c - func_correct_iseli_z(2*strF0, sna_F2, sna_B2, Fs);  % H2* を求める
    smoothwin = 20;
    sna_H1c_sm = filter(ones(smoothwin,1)/smoothwin, 1, sna_H1c);
    sna_H2c_sm = filter(ones(smoothwin,1)/smoothwin, 1, sna_H2c);
    subplot(2,1,2); plot([(sna_H1 - sna_H2) (sna_H1c - sna_H2c) (sna_H1c_sm - sna_H2c_sm)]); ylim([-20 30]);
    title('Calculate'); legend('H1 - H2', 'H1c - H2c', 'H1c - H2c(smoothing)' );
VoiceSauce_H1H2_3.png
  • 同じ結果を再現できました。func_getBWfromFMT.m ではHawks & Miller, 1995 の関数を使ってフォルマントとF0の値からBパラメータを推定しています。func_correct_iseli_z.m ではIseli & Alwan, 1999 の関数を使ってフォルマント、F0、Bパラメータの値からH1c、H2cを求めています。各関数の詳細は省略します。

*1 早大浅井君のご指摘によります。ありがとうございます。
*2 VoiceSauce/Windows/praatformants.praat 内に positive time_step = 0.005, positive window_length = 0.025, integer num_formants = 4, integer maximum_formant_frequency = 5500 と記述がありますが、これはPraatスクリプトの引数の初期値で、実際にはこの値は使われない(Settingで指定した値が使われる)ので、注意してください。西川さんに教えていただきました、ありがとうございます。
*3 VoiceSauce Documentation - F0 より引用
*4 「オープンソースソフト WaveSurferのコアとなっている音声処理エンジンAPI」とのこと(The Snack Sound Toolkitより引用)
*5 VoiceSauce Documentation - Formants より引用
*6 VoiceSauce Documentation - Harmonic and Formant Amplitude より引用
*7 新谷敬人, 西川賢哉, 五十嵐陽介, 北原真冬, 馬塚れい子, 対乳児発話における母音の音質― 『理研日本語母子会話コーパス』を用いた分析 ―, 第22回日本音声学会全国大会予稿集, 117-122, 2008.
*8 VoiceSauce Documentation - Amplitude Corrections より引用
*9 VoiceSauce Documentation - Amplitude Energy より引用
*10 RMSは音圧の実効値。音の大きさ、パワー。
*11 VoiceSauce Documentation - CPP より引用
*12 VoiceSauce Documentation - Harmonic to Noise Ratio より引用
*13 STRAIGHTが出力する無声部のF0の値に意味があるのかどうかは、分かっていません。すみません。ピッチレンジの計算などでは、pF0で無声部と判定されているところは、strF0の値も使わないほうが良さそうです。
*14 VoiceSauce v1.19 では、個々のパラメータのスムージング処理を行う関数は func_buildMData.m です。