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

チュートリアルホワイトノイズ・ジェネレーターを作る

📚 Source Page

このチュートリアルでは、簡単な合成とオーディオ出力を紹介します。このチュートリアルは、JUCE のオーディオ・アプリケーション(およびプラグイン)のコンセプトを理解するための鍵となります。

レベル:中級

プラットフォーム:Windows, macOS, Linux

クラス: AudioAppComponent,AudioSourceChannelInfo,AudioBuffer,Random

スタート

このチュートリアルのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。

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

このチュートリアルは、あなたがデジタルオーディオの基本原理を知っていることを前提にしています。特に、オーディオ信号がサンプリングを使ってどのように表現されるかを知っている必要があります。の考え方も知っている必要があります。サンプルレートこの文脈ではサンプリングレート,サンプリング周波数その他類似の用語)。

デモ・プロジェクト

デモプロジェクトはThe Projucer オーディオアプリケーションテンプレートがある。

オーディオ・アプリケーション・テンプレートは、The Projucer内でハイライト表示されています。
オーディオ・アプリケーション・テンプレートは、The Projucer内でハイライト表示されています。

これは、JUCEで独自のオーディオ・アプリケーションを開発するための出発点として役立ちます。このデモプロジェクトは、ホワイトノイズを合成し、ターゲットデバイスのデフォルトのオーディオハードウェアから再生します。

オーディオ・アプリケーション・テンプレート

このチュートリアルでは、オーディオ出力のみを実装しています。オーディオ入力とオーディオ入力データのリアルタイムオーディオ処理については、他のチュートリアルで説明します。オーディオ・アプリケーション・テンプレートは、GUIアプリケーション・テンプレートとよく似ています:

  • についてMainContentComponentクラスはAudioAppComponentクラスではなくComponentクラスである。
  • についてjuce_audio_utilsモジュールは、デフォルトでプロジェクトに追加される他のオーディオ関連モジュールに加えて、プロジェクトに追加されます。

Audio Application テンプレートは、ここで提供する例のような単純なアプリケーションに使用できます。また、より複雑なアプリケーション、基本的にはターゲットデバイスのオーディオハードウェアと直接対話する必要のあるアプリケーションにも拡張可能です。JUCEによるオーディオ・プラグインの作成については、他のチュートリアルで説明します。

オーディオ・アプリケーションのライフサイクル

についてAudioAppComponentクラスは抽象基底クラスで、次の3つの機能を持つ。pure virtualオーディオ・アプリケーションのライフサイクルを表す関数は、次のとおりです。マストを派生クラスに実装します:

この3つの中で最も重要なのは、おそらくAudioAppComponent::getNextAudioBlock()JUCE オーディオアプリケーションでオーディオを生成したり処理したりするのはここだからです。この仕組みを理解するために、現代のコンピュータがどのようにオーディオを生成するかを少し知っておく必要があります。オーディオハードウ ェアは、オーディオの各秒について、チャンネルごとにある数のサンプルを生成する必要があります。CD品質のサンプルレートは44.1kHzで、これは、再生するためにオーディオハードウェアに送られる、1チャンネルあたり毎秒44100サンプルが必要であることを意味します。オーディオハードウェアには、一度に1サンプルずつ渡されるのではなく、一定のサンプル数を含むバッファ(ブロック)で渡されます。例えば、44.1kHz、ブロックサイズ441の場合、次のようになります。AudioAppComponent::getNextAudioBlock()関数は1秒間に100回呼び出される。

注記

上記の441サンプルというバッファサイズは、説明のために演算をシンプルに保つために使用されている。実際には441というバッファサイズは珍しい。ハードウェアのバッファサイズは、ほぼ間違いなく偶数であり、2の累乗(256、512、1024 など)になる傾向があります。アプリケーションは、以下のような場合に対応できるようにしておくことが重要です。いずれもバッファ・サイズ。サンプル・レートとバッファ・サイズの設定については、以下を参照してください。Tutorial: The AudioDeviceManager class.

基本的に、我々はAudioAppComponent::getNextAudioBlock()のサービスを行っている。オーディオ・コールバックを呼び出します。注意すべき点は、この関数は別のスレッド (オーディオスレッドほとんどの場合)。

JUCEオーディオ・アプリケーションが正しく動作するためには、さらに2つの重要な関数があります。今回は、それらを実装するのではなく、呼び出す必要があります:

  • AudioAppComponent::setAudioChannels():必要な入出力チャンネル数を登録するために呼び出す。通常、コンストラクタでこれを行います。この関数を呼び出すと、アプリケーションのオーディオ処理が開始されます。
  • AudioAppComponent::shutdownAudio():オーディオシステムをシャットダウンするために呼び出さなければならない。一般的には、デストラクタの中でこれを行います。

オーディオアプリケーションの初期化

それでは、オーディオアプリケーションのライフサイクルをより詳しく調べることで、ノイズジェネレータの簡単な実装を探ってみましょう。コンストラクタでComponentオブジェクト(参照Tutorial: The main component).また、少なくとも1つのオーディオ出力を初期化する必要があります:

    MainContentComponent()
{
setSize (800, 600);
setAudioChannels (0, 2); // no inputs, two outputs
}

前述のようにAudioAppComponent::setAudioChannels()関数は、オーディオシステムの起動をトリガーします。特にprepareToPlay()関数である:

    void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
{
juce::String message;
message << "Preparing to play audio...\n";
message << " samplesPerBlockExpected = " << samplesPerBlockExpected << "\n";
message << " sampleRate = " << sampleRate;
juce::Logger::getCurrentLogger()->writeToLog (message);
}

この場合、ここで何かをする必要はない。pure virtual関数マスト少なくとも空の関数を実装します。ここでは、この時点でターゲットデバイスのオーディオシステムについて得ることができる、いくつかの有用な情報を記録します。そのsamplesPerBlockExpected引数は、その名前が示すように、オーディオのバッファのサイズ(サンプル数)である。getNextAudioBlock()関数で使用される。このバッファサイズはコールバックによって異なるかもしれないが、良い目安にはなる。このsampleRate引数は、ハードウェアの現在のサンプルレートを示す。音色を合成するような、周波数に依存するようなことをする場合、これが必要になる (Tutorial: Build a sine wave synthesiserまたはイコライザーを使う。また、ディレイエフェクトを使用する場合は、サンプルレートを知る必要があります。ノイズを発生させるためにこの情報は必要ありません。

オーディオデータの生成

その直後、私たちはこう呼びかけた。prepareToPlay()関数を介して、オーディオスレッドはオーディオのブロックのリクエストを開始する。AudioAppComponent::getNextAudioBlock()関数に渡される。この関数にはbufferToFill引数はAudioSourceChannelInfo構造体そのAudioSourceChannelInfostruct は、オーディオサンプルのマルチチャンネルバッファを含む。また、このバッファのどの領域がこの呼び出しで処理されるべきかを指定する2つの整数値も含まれる。より詳細にはAudioSourceChannelInfoには以下のメンバーが含まれている:

  • AudioSampleBuffer* bufferAudioSampleBufferオブジェクトは、オーディオデータのマルチチャンネルバッファであり、基本的には、次のような多次元配列である。float価値観私たちのgetNextAudioBlock()関数が呼び出されると、このバッファにはターゲット・デバイスのオーディオ入力(オーディオ入力を要求した場合)からのオーディオ・データが含まれます。私たちのgetNextAudioBlock()関数が返した時点で、バッファの該当セクションが再生したいオーディオで満たされていなければならない。
  • int startSampleこれは、バッファ内のサンプルのインデックスである。getNextAudioBlock()関数はオーディオの読み書きを開始しなければならない。
  • int numSamplesバッファ内の読み書きが必要なサンプル数。

浮動小数点値として保存されるオーディオデータは、非常に簡単です。オーディオ信号の各サンプルは、公称±1.0の範囲の値として保存されます。

フルスケール±1.0の正弦波。
フルスケール±1.0の正弦波。

このようにピークレベルが±1.0の場合、出力レベルは次のようになる。とても大音量。実際、これは、オーディオシステムがクリッピングせずに生成できる最大音量です。通常、私たちはこの±1.0の制限を超えないオーディオを出力する必要があります(ただし、最終的な出力が低くなる限り、処理の中間段階がこの制限を超えても問題ありません)。

AudioSampleBuffer クラス

AudioSampleBufferクラスは、(非常に基本的なレベルでは)マルチチャンネル配列のfloatオーディオデータを扱うための便利な関数群を提供します。これらの関数の多くは後のチュートリアルで扱いますが、ここでは以下の関数を使います:

この単純なアプリケーションでホワイトノイズを生成するには、バッファの要求されたセクションをランダムな値で満たす必要があります。これを行うには、バッファ内のチャンネルを繰り返し処理し、そのチャンネルのバッファ内で開始サンプルを見つけ、必要なサンプル数をバッファに書き込みます:

    void getNextAudioBlock (const juce::AudioSourceChannelInfo& bufferToFill) override
{
for (auto channel = 0; channel < bufferToFill.buffer->getNumChannels(); ++channel)
{
// Get a pointer to the start sample in the buffer for this audio output channel
auto* buffer = bufferToFill.buffer->getWritePointer (channel, bufferToFill.startSample);

// Fill the required number of samples with noise between -0.125 and +0.125
for (auto sample = 0; sample < bufferToFill.numSamples; ++sample)
buffer[sample] = random.nextFloat() * 0.25f - 0.125f;
}
}

Randomクラスを使ってホワイトノイズを生成する

ここではRandomクラスで乱数値を生成します (Tutorial: The Random class).ホワイトノイズを発生させるには、ゼロの周りに一様に分布する乱数を発生させる必要があります。ここでは、最初にRandom::nextFloat()関数を使います。この関数は0から1の間の値を生成し、その結果に0.25を掛け、0.125を引く。(参照Tutorial: Control audio levelsを参照のこと)。を使用していないことに注意してください。Random::getSystemRandom()関数を使用して、共有のシステム Randomオブジェクトを使用することができます。Tutorial: The Random class).これはRandom::nextFloat()関数をオーディオ・スレッド上に作成する必要がある。独自のRandomオブジェクトを使用している他のスレッドによって値が破損される可能性がある。Randomオブジェクトのインスタンスを作成する。これを実現するためにRandomクラスがMainContentComponentクラスである:

private:
juce::Random random;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};

オーディオアプリケーションのシャットダウン

アプリケーションが閉じられると、デストラクタが呼び出される。この時点でAudioAppComponent::shutdownAudio()関数である:

    ~MainContentComponent() override
{
shutdownAudio();
}

を呼び出す。AudioAppComponent::shutdownAudio()関数はAudioAppComponent::releaseResources()関数を呼び出します。これは、オーディオプロセスの実行中にリソースを割り当てた場合(例えば、メモリを割り当てたり、ファイルを開いたりした場合)、リソースを破棄するのに良い場所です。この場合、追加のリソースは必要ないので、単純なメッセージとともに関数呼び出しをログに記録します:

    void releaseResources() override
{
juce::Logger::getCurrentLogger()->writeToLog ("Releasing audio resources");
}
エクササイズ

オーディオ出力の数を変えてみてください。モノラルノイズはステレオノイズとは微妙に違って聞こえることに注意してください。マルチチャンネルのサウンドカードがあれば、2チャンネル以上のノイズを生成できるかもしれません。また、発生するノイズのレベルを変えてみることもできます。例えば、レベル0.1のノイズを生成するには、ランダムに生成された値に0.2を掛け、0.1を引く必要があります。

概要

このチュートリアルではAudioAppComponentクラスは、オーディオを生成するために Audio Application テンプレートで使用されます。以下のトピックを取り上げました:

  • オーディオシステムの初期化とシャットダウン。
  • オーディオ・コールバックに応答してオーディオ・データを書き込む。
  • を使用している。AudioSourceChannelInfo構造体とAudioSampleBufferクラスがある。

こちらも参照