ホワイトノイズでの変調アプリの音声部分を詳しく勉強しました。
一番重要なとこだね~
前回は、ホワイトノイズでマイクからの入力音声を変調しました。アプリをまず動かすことを目的としていましたので、今回からは、音声処理の内容がどのようになっているかを詳細にみていきたいと思います。
公式チュートリアルのページはこちらです。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・JUCEで音声プログラミングを学びたい人
・JUCEの「Processing audio input」チュートリアルを進めている人
Reusing buffersの項目
ここで言われていることは、インプットバッファとアウトプットバッファは完全に分離されていない、とのことです。getNextAudioBlockの処理をすべてコメントアウトすると、完全に入力音声を出力にスルーするようなものとなります。
getNextAudioBlock関数の仮引数「bufferToFill」はAudioSourceChannelInfo構造体へのスマートポインタなので、bufferToFillのbufferメンバ変数は図のように、チャンネルそれぞれへのバッファを指し示していると思います。なので、入力されたbufferToFillからbufferを変更せずに出力するということはこのようなイメージになりそうです。
BufferToFillのチャンネル数が入出力チャンネルよりも大きいときがあるようで、この時も、明示的に必要なバッファのみを処理するようにするのが良さそうです。
また、入力チャンネルよりも出力チャンネルが多いときは、読み取り専用のバッファを変更してはいけない、とあります。
入力チャンネル数が出力より多いことは確かにあると思うので、おそらく、先ほどのイメージから行くと、出力チャンネルへ出力されないAudioBufferは読み取るのみで、出力のあるバッファへの処理へ利用しなさいということでしょうか。
上の図のイメージのときは、チャンネル2のバッファを読み取って、チャンネル1のバッファに加算すればミキサーのように音声を合成してチャンネル1に出力できそうですが。読み取るだけ、とは、このようなことを言っているのでしょうかね。
いくつか、何となくふわっと雰囲気は感じ取れたのですが、まだここの項目の内容は完全に理解できてないようです^^;
ま~、いつかわかるときが来ると思います。
さらっとスルーしたね
getNextAudioBlock関数での音声処理
getNextAudioBlock関数の中身を見ていきます。チュートリアルでは、「Getting active channels」の項目からです。
void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
{
auto* device = deviceManager.getCurrentAudioDevice();//[1]
//[2]
auto activeInputChannels = device->getActiveInputChannels();
auto activeOutputChannels = device->getActiveOutputChannels();
[1]の「deviceManager」は、AudioAppComponentクラスのpublicなメンバ変数です。
deviceManagerはAudioDeviceManagerクラスの変数なので、「getCurrentAudioDevice」関数から、「AudioIODevice」クラスのポインタを取得することができます。この「AudioIODevice」は、現在アクティブな音声入出力デバイスのことらしいです。それをdeviceという変数に(ポインタに)入れています。
[2]では、先ほど取得したdevice変数(音声入出力クラスへのポインタ)を利用して「getActiveOutputChannels」関数と「getActiveInputChannels」関数をアロー演算子で呼び出し、有効な入手力チャンネルを「activeInputChannels」変数と「activeOutputChannels」変数に取得しています。この変数には、「BigInteger」というクラスで返されてくる値を格納します。
BigIntegerについて
今回取得できた「activeInputChannels」は「BigInteger」というクラスのオブジェクトが入ってくるのですが、このBigIntegerとは何者か、少しだけ調査してみました。
BigIntegerのチュートリアルがありましたので、少し眺めてみたところ、入出力チャンネルをビットのようにして表現しているようです。
例えば、上の図が入力チャンネルを表現しているとしたら、チャンネル0とチャンネル1は有効、というように表現されるようです。
アプリに次のようにコンソール出力をさせて、「activeInputChannel」を表示させてみました。
juce::String message;
message << activeInputChannels.toString(2,1);
juce::Logger::getCurrentLogger()->writeToLog(message);
BigIntegerはStringに変換する関数を持っているのでこのようにしてStringクラスに変換しています。第一引数を2とすることで二進数表記になりますので、0と1で表現されるようになります。
次に、入出力チャンネル数を取得します。BigIntegerの「getHighestBit」関数は、一番大きなビットが何ビットかを取得できるようです。
auto maxInputChannels = activeInputChannels .getHighestBit() + 1;
auto maxOutputChannels = activeOutputChannels.getHighestBit() + 1;
ビット数のカウントは0からはじまるので、チャンネル数に変換するために1を足しています。
先の図で2と出ているのは、この「activeInputChannels」変数を表示したものです。