簡易的なノイズ抑制と発話検出

C++で実装。ただし、音声データは符号付き16ビットPCMとし、ひとまず計算にはfloat型を用いて処理の重さも考慮しないものとします。

ノイズ抑制 (Noise Suppression)

  • 一般的には、窓関数→FFT→ノイズ推定→減算処理→IFFT→重ね合わせ
  • しかしやってみるとなかなかうまくいかない。逆にミュージカルノイズ(ピョロピョロ音)が出る。
  • なるべくホワイトノイズを抑えて人の声を通すことだけを考える。
  • バンドパスフィルタ(HPF+LPF) + 簡単なノイズゲート くらいでよいのではないか。
  • 人の声の主成分は300〜3400 Hz くらいなので 200〜4000 Hz くらいを通すようにする。

 → NoiseSuppressor.h · GitHub : NoiseSuppressorクラスを実装。

発話検出 (Voice Activity Detection)

  • 簡易的な方法としては振幅とゼロクロス数を用いる。
  • 振幅の平均レベルが一定以上 かつ ゼロクロスが毎秒400~8000 (200〜4000 Hz) なら発話と判定。

 → SimpleVAD.h · GitHub : SimpleVADクラスを実装。

実験

  • とりあえずPC上で実験
  • サンプリング周波数16000Hz、符号付き16ビットPCM、モノラルのWAVファイルを使用する。
  • 10msec (=160サンプル=320バイト) ずつ処理する。
  • ノイズ抑制した音声データをファイルに出力する。
  • 10msecごとの平均振幅、ゼロクロス数、発話判定をテキストファイルに出力する。
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "NoiseSuppressor.h"
#include "SimpleVAD.h"

int main(void)
{
    uint8_t buf[320];

    FILE* fp = fopen("input.wav", "rb");
    if (!fp) {
        perror("fopen 1");
        return -1;
    }
    FILE* ofp = fopen("output.wav", "wb");
    if (!fp) {
        perror("fopen 2");
        return -2;
    }
    FILE* log = fopen("log.txt", "w");
    if (!fp) {
        perror("fopen 3");
        return -3;
    }

    // 先頭 44 バイトをコピー (WAV ヘッダ)
    size_t n = fread(buf, 1, 44, fp);
    if (n < 44) {
        fprintf(stderr, "source file too small\n");
        fclose(fp);
        fclose(ofp);
        return -4;
    }
    fwrite(buf, 1, 44, ofp);

    NoiseSuppressor ns(16000.0f);
    SimpleVAD vad(16000, 1);

    while (1) {
        size_t n = fread(buf, 1, 320, fp);
        if (n < 320) {
            // 最後の端数のデータはゼロパディング
            memset(buf, 0x00, n);
            fwrite(buf, 1, n, ofp);
            printf("end of file!");
            break;
        }
        else {
            // ノイズ抑制処理
            ns.process((int16_t*)buf, (int16_t*)buf, 160);
            // 発話検出処理
            bool ret = vad.process((int16_t*)buf, 160);
            fwrite(buf, 1, 320, ofp);
            fprintf(log, "%d, %d, %d\n", vad.amp, vad.zcr, vad.isSpeech ? 100 : 0);
        }
    }
    fclose(ofp);
    fclose(fp);
    fclose(log);

    return 0;
}