SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

WAVEファイルにリアルタイムで加工を施し再生する

ステレオデータのモノラル化と簡易ボーカルキャンセル機能

  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード ソースファイル (3.2 KB)

再生

 再生部分の流れは次のとおりです。

  1. 読み込む
  2. 加工
  3. 書き込む

 バッファのサイズ分を読み込んで再生し、再生が終わったら、また読み込んで再生することを繰り返します。

再生
int readsize = mmioRead(hmmio,(HPSTR)buf,bufferSize);
//加工
whdr.dwBufferLength =readsize;
mmioWrite(hWave, &whdr , sizeof(WAVEHDR));

 whdr構造体には、bufやその他の情報が入っています。

 mmioRead関数は読み込んだサイズを返すので、そのサイズをセットしておきます。そうしないと、前回読み込んだデータを再生してしまう可能性があるからです。

 それと、注意すべき事があります。mmioReadはチャンクに関係なく、指定された分を読んできます。ですから、読み込んだ分を記録して、オーバーしないように気を付けなければなりません。そうしないと、もし「data」の後に何か別のチャンクがあったとしても構わず再生してしまうことになり、雑音が入ってしまいます。

データ加工

 いよいよ本題です。

 16ビットの音データは0を基準に上下に振れています。型は2バイトの符号付き整数です(大抵の環境ではshortだと思います)。つまり、符号付き2バイト整数が単純に並んでいるわけです。

 ですが、これはモノラルの場合であり、ステレオの場合は「左右左右………」というように交互にデータが並んでいます。つまり添え字で考えると、偶数番目が左、奇数番目が右になります。

 というわけで、次のようにして、モノラル化やボーカルキャンセル(真ん中の音を消す)の加工を行うことができます。

データ加工
short *buf;//データが入っている
int i;
//2ずつインクリメント
for(i=0;i<bufferLenght;i+=2){
    //モノラル化
    //short t1 = (short)((((int)buf[i]) +((int)buf[i+1]))/2);

    //真ん中の音を消す
    short t1 = (short)(((int)buf[i]) -((int)buf[i+1]));

    buf[i] = buf[i+1] =t1;
}

 好きな方を使ってください。他にも、特定の周波数を足すといった処理もできると思います。

マルチバッファリング

 例えば、WAVEデータを全部メモリに展開すれば、バッファ切れに注意する必要はなくなります。しかし、それは現実的な方法とは言い難いです。例えば、サンプリング周波数 44.1kHzで16ビットのステレオデータの場合、1分だと約10Mバイトほどになってしまいます。

サンプリング周波数 44.1kHz、16ビットのステレオデータの場合
44.1k(周波数) * 2(チャンネル)* 2(バイト⇒16ビット)
= 176400 [B/s] = 172.3 [KB/s]

 そこで、大きなデータを一括ではなく、小さなデータを必要な分だけ書き込むようにします(1~3秒くらいずつ)。

 waveOutWriteで書き込んだデータは、再生が終わると標準でMM_WOM_DONEというメッセージを送ってくるので、単純に考えると、そのメッセージが送られてきた段階で、また新たにwaveOutWriteでデータを書き込めばよいことになります。

 しかし、音はリアルタイムで流れているため、MM_WOM_DONEメッセージを受け取った後、ファイルからデータを読み込んで処理し書き込んで、ということを行っていると、一時的にバッファが切れた状態になってしまいます。そのため、変なノイズが混じってしまいます(例えば、「ブツッ」という音が入ったりします)。

 そこで、次の手順に従い「マルチバッファリング」を行います。

  1. 2つのバッファ「A」「B」を用意します。
  2. A、Bの順にwaveOutWriteします。
  3. Aの再生が終わるとMM_WOM_DONEが送られてきます。その間、裏では自動的にBの再生が始まります。
  4. Aを書き込みます。
  5. Bの再生が終わるとMM_WOM_DONEが送られてきます。その間、裏では自動的にAの再生が始まります。

 イメージとしては、次のソースコードのようになります。

マルチバッファリング
//データの長さ
whdr[0].dwBufferLength = 0;
whdr[1].dwBufferLength = 0;

//マルチバッファリングをするために空データを書き込み
waveOutWrite(hWave , &(whdr[0]) , sizeof(WAVEHDR));
waveOutWrite(hWave , &(whdr[1]) , sizeof(WAVEHDR));

//セレクタの設定
int bufferSelect = 2%BUFFER_COUNT;

{
case MM_WOM_DONE:
    //バッファが切れたときに呼び出される。
    {
        //データを持ってくる
        int readsize = readWaveData(
            hmmio,wWave[bufferSelect],readsize,bufferLenght);

        //再生バッファに書き込み
        whdr[bufferSelect].dwBufferLength =readsize;
        waveOutWrite((HWAVEOUT)wParam , &(whdr[bufferSelect]) ,
            sizeof(WAVEHDR));

        //バッファセレクタのインクリメント
        bufferSelect = (bufferSelect+1)%BUFFER_COUNT;
    }
}

 この方法は、3つや4つなどのバッファリングにも対応しています。

 最初にwaveOutWriteで長さ0のデータを書き込んでいますが、こうすることでreadWaveDataをあちこちで呼ばなくて済むようになります。

連続再生

 WAVEデータの再生が終了したら、WAVEをデータの開始点までシークする必要があります。

 それにはmmioSeekを使います。ただし、ファイルの先頭には戻らないようにしてください。そんなことをしたら、フォーマットチャンクまで再生されてしまいます。

連続再生
//waveDataSizeはデータサイズ。その分戻るので「-」を付けている。
mmioSeek(hmmio , -waveDataSize , SEEK_CUR);

 恐らく、現在の地点からの移動であるSEEK_CURしか使えないでしょう。

その他補足事項

 ここまで読んで大体の感じをつかめた方は、ソースコードを読んでください。コメントは大量に入れてあります。ソースコードは350行くらいです。ちょっとごちゃごちゃしていますが、大丈夫でしょう。

 分からない点がある方は参考資料で学ぶと良いと思います。私も1週間ほどで0からここまでできるようになりました。それでも分からない方は質問していただければ答えます。

まとめ

 私は、マルチバッファリングのやり方が分からずに苦労しました。それと、この記事書きの途中まではフォーマットチャンクも再生していました。危なかったです。

 今後はmp3なども扱えるようにすると良いと思います。ただし、その場合にはバッファが複雑なことになると思います。また、エコーなどをかけてみるのも面白いと思います。ですが、エコーなどはDirectShowで簡単に実装できそうなので、その方が楽かもしれません。

 寄せ集めの知識ですが、最後まで読んでいただきありがとうございました。そして、参考にさせて頂いた資料と、それを作った方々にも感謝します。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

利都(りと)

陸奥でプログラムをしている学生。LOGOからVB6.0へ。現在は主にC系言語を利用。Atelier BlueにてManaged DirectXについてのTipsを書いている。http://www.atelier-blue.com/IL(CIL,MSIL)もしているのでそちらに興味がある方もどうぞ。Manag...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/368 2006/05/15 12:58

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング