メインコンテンツまでスキップ

オーディオ入力の処理

チュートリアル: オーディオ入力の処理

📚 Source Page

このチュートリアルでは、オーディオ入力を処理してオーディオ出力に渡す方法を示します。

レベル: 初級
プラットフォーム: Windows, macOS, Linux
クラス: Random, BigInteger, AudioBuffer

はじめに

このチュートリアルのデモプロジェクトをここからダウンロードしてください: PIP | ZIP。プロジェクトを解凍し、Projucerで最初のヘッダーファイルを開きます。

ヒント

オペレーティングシステムがマイクへのアクセス許可を要求する必要がある場合(現在はiOS、Android、macOS Mojave)、Projucerの該当するエクスポーターの下で対応するオプションを設定し、プロジェクトを再保存する必要があります。

このステップに関するヘルプが必要な場合は、Tutorial: Projucer Part 1: Getting started with the Projucerを参照してください。

デモプロジェクト

デモプロジェクトは、入力信号をホワイトノイズで変調します。ホワイトノイズのレベルを変更でき、全体的な出力レベルに影響します(ホワイトノイズを生成するために使用される技術については、Tutorial: Control audio levelsを参照してください)。結果は、入力信号の非常に「ファジー」なバージョンになります。

警告

アプリケーションを実行する際は、フィードバックを避けるように注意してください(ただし、効果はかなり面白いものになる可能性があります!)。

別のマイクとヘッドフォンを使用するのが最善です。もちろん、プロジェクトが正しく動作するには、何らかのオーディオ入力デバイスが必要です。

オーディオ入力

このチュートリアルでは、デモプロジェクトアプリケーションの基礎としてAudioAppComponentクラスを使用します。他のチュートリアルでは、getNextAudioBlock()関数内でオーディオを生成します---Tutorial: Build a white noise generatorTutorial: Control audio levelsTutorial: Build a sine wave synthesiserを参照してください。このチュートリアルでは、オーディオ入力を読み取り、オーディオも出力します。MainContentComponentコンストラクタで、2つのオーディオ入力と2つのオーディオ出力を要求します:

setAudioChannels (2, 2);
ヒント

利用可能な入力または出力の実際の数は、要求した数よりも少ない場合があります。

バッファの再利用

入力バッファと出力バッファが完全に分離されていないことを知っておくことが重要です。入力と出力には同じバッファが使用されます。これをテストするには、getNextAudioBlock()関数内のすべてのコードを一時的にコメントアウトします。その後、アプリケーションを実行すると、オーディオ入力が直接出力に渡されます。getNextAudioBlock()関数では、bufferToFill構造体内のAudioSampleBufferオブジェクトのチャンネル数は、入力チャンネル数、出力チャンネル数、またはその両方よりも大きくなる場合があります。要求した_そして_利用可能な入力チャンネル数と出力チャンネル数を参照するデータのみにアクセスすることが重要です。特に、出力チャンネルよりも入力チャンネルが多い場合、読み取り専用データを含むべきチャンネルを変更しては_いけません_。

アクティブなチャンネルの取得

getNextAudioBlock()関数では、アクティブな入力チャンネルと出力チャンネルのリストを_ビットマスク_として表すBigIntegerオブジェクトを取得します(これはstd::bitsetクラスやstd::vector<bool>オブジェクトの使用に似ています)。これらのBigIntegerオブジェクトでは、チャンネルはBigInteger値を構成するビットの0(非アクティブ)または1(アクティブ)で表されます。

ヒント

BigIntegerオブジェクトに対して実行できる他の操作については、Tutorial: The BigInteger classを参照してください。

void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
{
auto* device = deviceManager.getCurrentAudioDevice();
auto activeInputChannels = device->getActiveInputChannels();
auto activeOutputChannels = device->getActiveOutputChannels();

反復処理する必要がある最大チャンネル数を計算するために、BigIntegerオブジェクトのビットを調べて、最も高い番号のビットを見つけます。最大チャンネル数は、これより1つ多くなります。

auto maxInputChannels = activeInputChannels.getHighestBit() + 1;
auto maxOutputChannels = activeOutputChannels.getHighestBit() + 1;

次に、レベルスライダーから希望のレベルを取得し、各出力チャンネルを1つずつ処理してオーディオの処理に進みます。入力チャンネルの最大数がゼロの場合(ハードウェアにオーディオ入力がない場合、2つのチャンネルを要求したにもかかわらずこれが発生する可能性があります)、オーディオを処理しようとしてはいけません。この場合、出力チャンネルバッファを単にゼロにします(無音を出力するため)。個々の出力チャンネルも非アクティブである可能性があるため、チャンネルの状態を確認し、非アクティブな場合はそのチャンネルについても無音を出力します:

auto level = (float) levelSlider.getValue();

for (auto channel = 0; channel < maxOutputChannels; ++channel)
{
if ((!activeOutputChannels[channel]) || maxInputChannels == 0)
{
bufferToFill.buffer->clear (channel, bufferToFill.startSample, bufferToFill.numSamples);
}

次に、入力データを出力に処理します:

else
{
auto actualInputChannel = channel % maxInputChannels; // [1]

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);

for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
{
auto noise = (random.nextFloat() * 2.0f) - 1.0f;
outBuffer[sample] = inBuffer[sample] + (inBuffer[sample] * noise * level);
}
}
}
}
}

コードは十分に自明なはずですが、いくつかのハイライトを示します:

  • [1]: 入力チャンネルよりも多くの出力チャンネルを要求した可能性があるため、アプリはこれらの追加出力について何をするかを決定する必要があります。この例では、出力が入力よりも多い場合、単に入力チャンネルを繰り返します。(これを行うために、モジュロ演算子を使用して、利用可能な入力チャンネル数に基づいて入力チャンネルへのアクセスを「ラップ」します。)他のアプリケーションでは、入力チャンネルよりも出力チャンネルが多い場合、より高い番号のチャンネルに無音を出力する方が適切な場合があります。
  • [2]: 個々の入力チャンネルが非アクティブである可能性があるため、この場合も無音を出力します。
  • [3]: この最後のブロックが_実際に_処理を行います!ここでは、入力バッファと出力バッファのサンプルへのポインタを取得し、入力サンプルにノイズを追加します。
注記

演習: この例では、Tutorial: Build a sine wave synthesiserで行っているような振幅変化の平滑化を行っていません。これは、例をシンプルに保つためでもありますが、おそらくクラックリング効果により追加のグリッチは聞こえないでしょう。入力チャンネルをレベルスライダーの値でスケーリングして単に出力するようにコードを変更しますが、グリッチがないようにレベル変化を平滑化してください。

まとめ

このチュートリアルでは、JUCEアプリケーションでオーディオ入力からオーディオを処理することを紹介しました。特に、次のことがわかったはずです:

  • コンピュータのオーディオ入力ハードウェアからオーディオにアクセスできるようにオーディオアプリケーションを設定する方法。
  • 入力バッファと出力バッファが共有されていること。
  • アクティブなチャンネルと非アクティブなチャンネルに対処する方法。
  • 入力チャンネルと出力チャンネルの数が異なる場合に何をすべきかを推論する方法。

参照