前回途中で終わってましたので、引き続き音声処理部分を調査です。
今までで一番難しいことしているね~
前回に引き続き、ホワイトノイズでの変調アプリの音声処理部分を見ていきたいと思います。
公式チュートリアルのページはこちらです。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・JUCEで音声プログラミングを学びたい人
・JUCEの「Processing audio input」チュートリアルを進めている人
getNextAudioBlock関数の続き
出力チャンネルへのエラー処理など
[1]では、希望の音量をスライダーオブジェクトから取得します。float型でキャストしてlevel変数に格納しています。
//[1]スライダーの値を取得します。
auto level = (float) levelSlider.getValue();
//[2]出力チャンネル数分ループします。
for (auto channel = 0; channel < maxOutputChannels; ++channel)
{
//[3]バッファをゼロにする場合の条件です。
if ((! activeOutputChannels[channel]) || maxInputChannels == 0)
{
bufferToFill.buffer->clear (channel, bufferToFill.startSample, bufferToFill.numSamples);
}
[2]では、出力チャンネル数分のループを行っています。前回の記事のイメージで、ここのループの終了が、出力チャンネル(maxOutputChannels)となっていることが理解できました。getNextAudioBlockでは、出力チャンネル分の出力バッファを作成する処理、ととらえることが良さそうです。
[3]では、バッファを0(clear関数で無音に)にする場合の条件となっています。次の2つの条件のときに無音にする処理を施します。
①出力チャンネルが有効でない場合
「! activeOutputChannels[channel])」という条件です。BigIntegerクラスのオブジェクトなので、channelで指定したビット目のチャンネルが1であれば出力が有効です。そのため、「!」をすることで、0となるとif文は「False」となり、activeOutputChannnelsの任意のチャンネルのビットが「1」で有効な時は、if文の条件に当てはまらなくなります。
②入力チャンネルがない場合
「maxInputChannels」変数を参照して、実際の入力が存在しない場合、出力のバッファをゼロにする必要があります。入力が0のとき、このアプリからの出力は無音とするようにしています。
音声処理部分
ここでは、出力チャンネルが入力チャンネルより大きい場合の入力チャンネルの適切な処理も行います。
else
{
// [1]正しい入力チャンネルを取得します。([3]で利用します)
auto actualInputChannel = channel % maxInputChannels;
if (! activeInputChannels[channel]) // [2]出力に対応する入力チャンネルが存在しない場合。
{
bufferToFill.buffer->clear (channel, bufferToFill.startSample, bufferToFill.numSamples);
}
else // [3]出力チャンネルに対応する入力が存在する場合です。
{
auto* inBuffer = bufferToFill.buffer->getReadPointer (actualInputChannel,
bufferToFill.startSample);
auto* outBuffer = bufferToFill.buffer->getWritePointer (channel, bufferToFill.startSample);
//[4]音声処理を行います。
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
outBuffer[sample] = inBuffer[sample] * random.nextFloat() * level;
}
}
}
}
[1]で、actualInputChannelという変数に、正しい入力チャンネルを設定しています。channel変数は出力チャンネル回数ループするので、入力チャンネルが出力チャンネルより少ないときはこの処理で入力チャンネルが実際に「存在する入力チャンネル」の値とする必要があります。
「実際に存在する入力チャンネル」は、今のチャンネル数から剰余演算を行うことで、入力チャンネルの数値が最大入力チャンネルの値を超えないようなチャンネルに設定します。
入力チャンネル3、出力チャンネル5の場合
channelは出力チャンネル数回ループするので、
channel % maxInputChannels = actualInputChannel
の内容は次のようになります。
0回目:0%3=0
1回目:1%3=1
2回目:2%3=2
3回目:3%3=0
4回目:4%3=1
と5回ループします。剰余演算を利用することで、最大入力チャンネルを超えることがないような数値をactualInputChannelに取得することができます。
[2]では、対応する入力チャンネルが存在していない出力チャンネルに対しては、AudioBufferクラスのclear関数で出力を無音に設定します。
[3]では、入力が存在する出力チャンネルの処理を行うような条件になっています。最初に取得した「存在する入力チャンネル」の数値である「actualInputChannel」変数を「bufferToFill.buffer->getReadPointer」でバッファを指定して、入力音声を取得します。入力音声のポインタが「inBuffer」に格納されますので、この変数(配列)の値を取得することで、音声サンプルの値に触れることができます。
出力する音声のバッファとしては、「bufferToFill.buffer->getWritePointer」でバッファのポインタを取得しています。書き込み可能なバッファのポインタが取得でき、ここに音声バッファを書き込むことで出力となるようです。
[4]で、音声処理を行っています。forループは、今までと同様、音声バッファ(ブロック)のサンプル数分繰り返しを行います。「inbuffer」が入力された音声のゲイン値で、その値にRandomクラスのnextFloat関数で取得した0~1までのランダムな値を掛け合わせます。そして、最後に0~0.25の間で取得される音量を掛け合わせることで出力の値としています。
バッファ回りのイメージの更新
脳内イメージに、AudioBufferの中身を追加しました、今のところまでで何となくイメージしている音声処理でのバッファのイメージです。
出力チャンネル数ベースでループを行うので、入力がない場合の処理もしっかり行わなければいけません。