JUCEで作るASIO対応アプリ -上級編-

前回前々回に引き続き、JUCEからASIOドライバ対応のアプリケーションを作る手順を紹介します。本記事では、Projucerの”Audio Application”テンプレートから作る際の手順を紹介していきます。JUCEライブラリが、どのようにしてオーディオデバイスと連携し、音声入出力を行っているのかについても触れていきますので、上級者向けの内容となっております。

■本記事の概要

1. ASIO SDKをダウンロードする
2. ASIO SDKのzipファイルを解凍する
3. Projucerで “Audio Application”のテンプレートから新規プロジェクトを作成する
4. [Modules] → [juce_audio_devices] → [JUCE_ASIO]を “Enabled”にする
5. [Exporters] → [Header Search paths]にASIO SDKのパスを入力する
6. プロジェクトを保存し、IDE (Visual Studio)でプロジェクトを開く
7. コーディングの前に:プロジェクトの初期構成について
8. コーディングの前に:”AudioDeviceManager”クラスについて
9. コーディングの前に:”AudioDeviceSelectorComponent”クラスについて
10. コーディング:”AudioDeviceSelectorComponent”を表示する
11. コーディング:サイン波を出力する処理を実装する
12. ビルドしたアプリを起動し、オーディオ設定パネルからASIOを有効にする
補足説明:AudioAppComponentに実装された仕組みについて

1.ASIO SDKをダウンロードする

Steinbergの開発者向けページからダウンロードすることができます。
URL:https://www.steinberg.net/en/company/developers.html

 

 

2.zipファイルを解凍する

ASIO SDKをダウンロードしたら、zipファイルを解凍しておきましょう。
本記事では、”C:/SDKs/Steinberg SDK/ASIOSDK2.3″に解凍しておきます。
ASIO SDKにはソースコード、サンプルプロジェクト、ドキュメントが含まれています。

 

 

3.Projucerで新規プロジェクトを作成する

本記事では、Projucerの”Audio Application”テンプレートを元に作成したアプリケーションをASIOに対応させていきます。
“Audio Application”テンプレートを利用することで、オーディオデバイスからの音声入力・出力やMIDI入出力を行うためのコンポーネント群に簡単にアクセスすることができるようになります。
※他のテンプレートを利用した場合でもオーディオデバイスとの連携を実装することもできますが、特定のコンポーネント群を追加、継承する必要があるので、JUCEライブラリに慣れてから挑戦することをお勧めします。

 

 

4.JUCEモジュール設定でASIO対応を有効にする

①プロジェクト設定から[Modules]→[juce_audio_devices]を選択します。
②[JUCE_ASIO]を “Enabled”に変更します。

 

 

5.エクスポート設定でASIO SDKのヘッダーファイルパスを追加する

①[Exporters]から、ビルドを実行するIDEのビルド設定アイテムを選択します。
②[Header Search Paths]欄に、上記(2)の作業で配置したASIO SDKのソースコードへのパスを入力します。
本記事では “C:/SDKs/Steinberg SDK/ASIOSDK2.3/common” を入力しました。
※画像ではReleaseビルドの設定項目にパスを追加していますが、DebugビルドでもASIO対応させたい場合は、同様にDebugビルドの設定項目にパスを追加してください。

 

 

6.プロジェクトを保存したら、IDEでプロジェクトを開く

Projucerでの作業が完了したら、プロジェクトを保存し、IDE(Visual Studio)でプロジェクトを開きます。
“AppConfig.h”内の”JUCE_ASIO”定義が有効になっていること、プロジェクトからヘッダーファイル”asio.h”が参照できていることを確認しましょう。

 

 

7.コーディングの前に:プロジェクトの初期構成について

コーディングを始める前に、”Audio Application”テンプレートについて解説します。
本テンプレートを利用すると、”Main.cpp”と”MainComponent.cpp”のソースファイルが自動で作成されます。

◆ “Main.cpp ”
– クラス xxxApplication – 継承元 JUCEApplication (xxxはプロジェクト名)
アプリケーションのエントリー部となっており、JUCEライブラリにおけるmain()関数を持つ。このクラスがアプリケーションの初期化と終了を担当する。
また、内部にはMainWindowクラスが定義されており、新規作成の時点で既に、initialise関数内でMainWindowのインスタンスを生成する処理が実装されている。

class xxxApplication : public JUCEApplication
{
public:
  xxxApplication() {}
~~~中略~~~
void initialise (const String& commandLine) override
{
   mainWindow = new MainWindow (getApplicationName());
}
~~~中略~~~
  • クラス  MainWindow – 継承元 DocumentWindow
    アプリケーションのウインドウ(外枠)を描画するDocuementWindowコンポーネントを継承している。このウインドウ内に各種コンポーネントを配置することで、GUIを構築する。
    また、新規作成の時点で既にMainContentComponentクラスのインスタンスを生成し、ウインドウ内の主要な部品として配置する処理が実装されている。
    ※setContentOwned (createMainContentComponent(), true);がその処理に当たる。
class MainWindow : public DocumentWindow
{
public:
    MainWindow (String name) : DocumentWindow (~~~)
    {
        setUsingNativeTitleBar (true);
        setContentOwned (createMainContentComponent(), true);
        setResizable (true, true);

        centreWithSize (getWidth(), getHeight());
        setVisible (true);
    }
~~~中略~~~

◆ "MainComponent.cpp"
– クラス MainContentComponent – 継承元 AudioAppComponent
ウインドウ内に配置されるコンテンツの基礎となります。GUI部品の機能とオーディオデバイスと連携するインターフェースが定義されたAudioAppComponentクラスを継承しており、新規作成時点で既に、コンストラクタ内において大きさを決定する関数(初期=800pixel × 600pixel)と、オーディオ入出力のチャンネル数を設定する関数(初期=2入力:2出力)が実装されています。

<pre>
class MainContentComponent : public AudioAppComponent
{
public:
MainContentComponent()
{
setSize (800, 600);
setAudioChannels (2, 2);
}
~~~中略~~~
}

 
 

8.コーディングの前に:”AudioDeviceManager”クラスについて

MainContentComponentの継承元であるAudioAppComponent 内において、AudioDeviceManagerクラスのインスタンスdeviceManagerが予め定義されています。このAudioDeviceManagerクラスが、アプリケーションとオーディオデバイスとの橋渡し役として機能してくれます。
具体的には、AudioDeviceManagerクラスを介してオーディオデバイス・MIDIデバイスからのコールバックを受けて音声処理やMIDIデータ処理の関数を実行し、その結果をオーディオデバイス・MIDIデバイスに返します。詳細については、公式ドキュメントからも確認することができます。
また、このAudioDeviceManagerクラスは、オーディオデバイス・MIDIデバイスを設定する役割も担っています。つまり、AudioDeviceManagerクラスが持つオーディオドライバの設定項目をASIOに変更することによって、アプリケーションからASIOを介してオーディオデータをデバイスと送受信することができるようになります。

class JUCE_API AudioAppComponent : public Component, public AudioSource
{
public:
    AudioAppComponent();
    ~AudioAppComponent();
~~~中略~~~
  virtual void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) = 0;
  void shutdownAudio();

  AudioDeviceManager deviceManager;
~~~中略~~~

 

 

9.コーディングの前に:”AudioDeviceSelectorComponent”について

(8)で述べた通り、JUCEライブラリではAudioDeviceManagerクラスを介して、オーディオデバイスとのオーディオデータ・MIDIデータを送受信する仕組みとなっています。また、AudioDeviceManagerクラス自身も様々な設定項目を持っており、使用可能なオーディオドライバの一覧や、送受信可能なMIDIデバイスの一覧など様々な情報を扱います。そのため、各項目の状態を取得、操作するための処理や、そのためのGUIを毎回実装するのはかなりの手間となります。
そこで活躍するのが、”AudioDeviceSelectorComponent”クラスです。このクラスは、AudioDeviceManagerクラスを操作するために特化したGUIコンポーネントで、各種項目を取得、操作するための関数群やGUI部品が予め実装されており、このクラスを介してオーディオデバイス・MIDIデバイスの設定を行うことができます。

 

 

 

10.コーディング:”AudioDeviceSelectorComponent”を表示する

MainContentComponentクラスに、AudioDeviceSelectorComponent(以下、セレクター)を表示する関数”showDeviceSetting()”を追加実装します。
また、”showDeviceSetting()”の実行を、MainContentComponentクラスのコンストラクタ内に記述することで、アプリケーションの起動時にオーディオデバイス設定画面が立ち上がるようにしておきます。
“showDeviceSetting()”関数では、ダイアログウインドウが生成され、ウインドウ内にセレクターを配置する処理を実装します。この時、セレクターにはMainContentComponentクラスが持つAudioDeviceManager(以下、マネージャー)の参照を渡しておきます。参照を渡すことで、セレクターからマネージャーの持つ各関数を実行することができるようになります。

class MainContentComponent : public AudioAppComponent
{
public:
    MainContentComponent()
    {
        setSize (800, 600);
        setAudioChannels (2, 2);
        showDeviceSetting();
    }
~~~中略~~~
private:
    void showDeviceSetting() 
    {
        AudioDeviceSelectorComponent selector(deviceManager, 
                                              0, 256, 
                                              0, 256, 
                                              true, true, 
                                              true, false);

        selector.setSize(400, 600);

        DialogWindow::LaunchOptions dialog;
        dialog.content.setNonOwned(&selector);
        dialog.dialogTitle = "Audio/MIDI Device Settings";
        dialog.componentToCentreAround = this;
        dialog.dialogBackgroundColour = getLookAndFeel().findColour(ResizableWindow::backgroundColourId);
        dialog.escapeKeyTriggersCloseButton = true;
        dialog.useNativeTitleBar = false;
        dialog.resizable = false;
        dialog.useBottomRightCornerResizer = false;

        dialog.runModal();
    }
~~~中略~~~
}

 

11.コーディング:サイン波を出力する処理

目的のオーディオデバイスにオーディオデータが送られているかを確認するため、サイン波を出力する処理を実装してみましょう。以下のコードでは、全てのチャンネルに対して、毎回のオーディオバッファに1周分のサイン波を出力する処理が実行されます。
実際にスピーカーから流れる周波数は、オーディオバッファやサンプリングレートの設定値によって異なります。

class MainContentComponent : public AudioAppComponent 
{
~~~中略~~~
    void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
    {
        bufferToFill.clearActiveBufferRegion();

        #define M_PI 3.14159265358979323846
        const float level = 0.5f;  //音量はこの値で変更する

        auto buffer = bufferToFill.buffer;
        for (int channel = 0; channel < buffer->getNumChannels(); ++channel)
        {
            float* channelData = buffer->getWritePointer(channel);

            for (int sample = 0; sample < buffer->getNumSamples(); ++sample)
            {
                channelData[sample] = sinf(M_PI * 2 * sample / buffer->getNumSamples() * 2) * level;
            }
        }
    }
~~~中略~~~

 

12.プロジェクトをビルドして動作確認

▼ビルドしたアプリを起動する
アプリケーションを起動すると、MainContentComponentのコンストラクタ内に記述された”showDeviceSetting()”関数が実行されるため、ダイアログウインドウが表示されます。
また、初期状態でオーディオデバイスの出力チャンネルが有効になっている場合は、オーディオデバイスからサイン波が鳴ります。

 

▼オーディオドライバをASIOに切り替える
[Audio device type]のコンボボックスから”ASIO”を選択します。
[Device]のコンボボックス、[Control Panel]ボタン、[Reset Device]ボタンが追加されます。
[Device]のコンボボックスにASIO対応のデバイス一覧が表示されるので、使用するオーディオデバイスを選択します。
[Control Panel]ボタンをクリックすると、ASIOデバイス毎に設けられたコントロールパネルが開きます。

 

▼出力チャンネル数を変更してみる
以下のように、[Active output channels]を追加して、複数のチャンネルからサイン波を出力していることが確認できます。

 

▼ダイアログウインドウを閉じるとメインウインドウが開く
本記事紹介の実例では、ダイアログウインドウはモーダルウインドウとして実行しているため、ダイアログウインドウを閉じてからメインウインドウが表示される挙動となります。
また、説明を簡単にするため、ダイアログウインドウを表示する関数をコンストラクタ内で実行する実装としていましたが、決してこのような実装方針には限られません。メインウインドウ内に配置したボタンを押すことでダイアログウインドウを表示するという実装も可能ですし、ウインドウメニューからダイアログウインドウを表示するという実装も可能です。

 

補足説明:AudioAppComponentに実装された仕組みについて

本記事では、Projucerの”AudioApplication”テンプレートから自動生成されたMainContentComponentクラスに機能を追加することで、ASIO対応のアプリケーションを作成しました。ここでは、MainContentComponentクラスに関する解説をしていくことで、JUCEライブラリが提供する仕組みについてより深く理解することを目指します。
まず、(7)にある通り、MainContentComponentクラスは、AudioAppComponentクラスを継承してます。このAudioAppComponentクラスは、ComponentクラスとAudioSourceクラスを継承したクラスです。

  • クラス AudioAppComponent – 継承元 Component, AudioSource
class JUCE_API AudioAppComponent : public Component, public AudioSource
{
public:
    AudioAppComponent();
    ~AudioAppComponent();
~~~中略~~~
    virtual void prepareToPlay (int samplesPerBlockExpected, double sampleRate) = 0;
    virtual void releaseResources() = 0;
    virtual void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) = 0;
~~~中略~~~

この点について詳しく説明すると、先ず、ComponentクラスはGUI部品の機能を持つクラスです。GUIの部品として配置、描画、操作させられるため関数群を持っており、また、このクラスを継承することで、MainWindowクラス内に配置することができるようになります。

次に、AudioSourceクラスはオーディオデバイスに送るオーディオデータを生成する機能(音源の機能)を実装することができるクラスです。AudioSourceクラス自身は純粋仮想関数を持つインターフェースであり、継承先のクラスで実装を記述することで音源の機能としての役割を果たすことができます。
このAudioSourceクラスで定義された純粋仮想関数 getNextAudioBlock 他は、継承先のAudioAppComponentクラスにおいても実装がされていないため、更なる継承先のMainContentComponentクラスに実装コードを記述することになります。
また、AudioAppComponentの実装コードである”juce_AudioAppComponent.cpp”を見ると、以下の実装が既に行われていることが確認できます。

"juce_AudioAppComponent.cpp"

~~~中略~~~
void AudioAppComponent::setAudioChannels (int numInputChannels, int numOutputChannels, const XmlElement* const xml)
{
    String audioError = deviceManager.initialise (numInputChannels, numOutputChannels, xml, true);
    jassert (audioError.isEmpty());

    deviceManager.addAudioCallback (&audioSourcePlayer);
    audioSourcePlayer.setSource (this);
}

void AudioAppComponent::shutdownAudio()
{
    audioSourcePlayer.setSource (nullptr);
    deviceManager.removeAudioCallback (&audioSourcePlayer);
    deviceManager.closeAudioDevice();
}
~~~中略~~~
AudioDeviceManager deviceManager;
AudioSourcePlayer audioSourcePlayer;

 

上記実装のうち、setAudioChannels関数内に注目すると、AudioDeviceManagerクラスのインスタンスであるdeviceManagerに対して、AudioSourcePlayerクラスのインスタンスであるaudioSourcePlayerを引数としてAudioDeviceManager::addAudioCallback 関数を実行します。このaddAudioCallback (AudioIODeviceCallback *newCallback)関数は、AudioIODeviceCallback クラスを継承したオブジェクトのポインタを引数に渡すことができます。AudioSourcePlayerクラスはAudioIODeviceCallbackを継承しているため、引数として渡すことができるのです。

続いて、AudioSourcePlayerクラスのインスタンスであるaudioSourcePlayerに対してAudioAppComponent自身”this”を引数として、AudioSourcePlayer::setSource 関数を実行しています。このsetSource (AudioSource *newSource)関数は、AudioSourceクラスを継承したオブジェクトのポインタを引数に渡すことができます。AudioAppComponentクラスはAudioSourceクラスを継承しているため、自身”this”を引数として渡すことができるのです。

公式ドキュメント:AudioDeviceManager
公式ドキュメント:AudioSourcePlayer
公式ドキュメント:AudioSource

さて、ここでAudioSourcePlayerの実装を覗いてみます。注目すべきは、”audioDeviceIOCallback”関数です。この関数は、AudioDeviceManagerからのコールバック関数として用意されています。つまり、オーディオデバイスの都合に合わせて、このコールバック関数が実行させられます。
また更に、このコールバック関数内には、AudioSourcePlayerに登録されたAudioSourceクラスのオブジェクトに対して”getNextAudioBlock”関数を実行するという、コールバック処理も実装されています。

"juce_AudioSourcePlayer.cpp"

void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData,                                               
                                               int totalNumInputChannels,                                               
                                               float** outputChannelData,                                               
                                               int totalNumOutputChannels,                                               
                                               int numSamples)
{ 
        ~~~中略~~~        
        AudioSampleBuffer buffer (channels, numActiveChans, numSamples);
        AudioSourceChannelInfo info (&buffer, 0, numSamples);        
        
        source->getNextAudioBlock (info);     //AudioSource* source
        ~~~中略~~~
}

これらの実装によって、以下の一連の処理が実現されます。
1. AudioDeviceManagerからAudioSourcePlayer::audioDeviceIOCallbackが実行される
2. “audioDeviceIOCallback”内でAudioAppComponent::getNextAudioBlock が実行される
3. AudioAppComponent::getNextAudioBlock に実装した音声信号処理が実行される

JUCEで作るASIO対応アプリ -VSTプレイヤー編-

前回の記事に引き続き、JUCEからASIOドライバ対応のアプリケーションを作る手順を紹介します。本記事では、Projucerの”Audio Plug-In”テンプレートから作ることができるスタンドアローン形式のVSTプレイヤーを利用します。

■本記事の概要

1. ASIO SDKをダウンロードする
2. ASIO SDKのzipファイルを解凍する
3. Projucerで “Audio Plug-In”のテンプレートから新規プロジェクトを作成する
4. プロジェクト設定を開き、[Build Standalone Plug-In]を “Enabled”にする
5. [Modules] → [juce_audio_devices] → [JUCE_ASIO]を “Enabled”にする
6. [Exporters] → [Header Search paths]にASIO SDKのパスを入力する
7. プロジェクトを保存し、IDE (Visual Studio)でビルドする
8. ビルドしたアプリを起動し、オーディオ設定パネルからASIOを有効にする


1.ASIO SDKをダウンロードする

Steinbergの開発者向けページからダウンロードすることができます。
URL:https://www.steinberg.net/en/company/developers.html

 


2.zipファイルを解凍する

ASIO SDKをダウンロードしたら、zipファイルを解凍しておきましょう。
本記事では、”C:/SDKs/Steinberg SDK/ASIOSDK2.3″に解凍しておきます。
ASIO SDKにはソースコード、サンプルプロジェクト、ドキュメントが含まれています。

 


3.Projucerで新規プロジェクトを作成

本記事では、Projucerの”Audio Plug-In”テンプレートを元に作成したアプリケーションをASIOに対応させていきます。
“Audio Plug-In”テンプレートを利用することで、DAW等のホストアプリケーション上で実行することのできるVST/AUプラグインを簡単に作成することができます。
また、プラグイン形式だけでなく、スタンドアローン形式として、オーディオデバイスからの音声入力・出力やMIDI入出力を行うことができるアプリケーションを出力することができるのも大きな特徴です。

 


4.プロジェクト設定でスタンドアローン形式を有効にする

①[Projece Settings]ボタンをクリックし、プラグイン形式の選択画面を表示します。
②[Build Standalone Plug-In]を “Enabled”にしておきます。
※デフォルト値で”Enabled”になっています。

 


5.JUCEモジュール設定でASIO対応を有効にする

①プロジェクト設定から[Modules]→[juce_audio_devices]を選択します。
②[JUCE_ASIO]を “Enabled”に変更します。

 


6.エクスポート設定でASIO SDKのヘッダーファイルパスを追加する

①[Exporters]から、ビルドを実行するIDEのビルド設定アイテムを選択します。
②[Header Search Paths]欄に、上記(2)の作業で配置したASIO SDKのソースコードへのパスを入力します。
本記事では “C:/SDKs/Steinberg SDK/ASIOSDK2.3/common” を入力しました。
※画像ではReleaseビルドの設定項目にパスを追加していますが、DebugビルドでもASIO対応させたい場合は、同様にDebugビルドの設定項目にパスを追加してください。

 


7.プロジェクトの保存とIDE (Visual Studio)でビルド

Projucerでの作業が完了したら、プロジェクトを保存し、IDE(Visual Studio)でプロジェクトを開きます。
AppConfig.h内の”JUCE_ASIO”定義が有効になっていること、ASIO SDKのヘッダーファイルが参照されていることが確認できたら、ビルドを実行します。

 


8.ビルドしたアプリケーションの起動とASIOの有効化

アプリケーションの実装例として、JUCE JAPAN vol.2 で紹介したパラメトリックEQをスタンドアローン形式でビルドした場合を示します。
GitHub: https://github.com/COx2/JUCE_JAPAN_DEMO
VSTプラグイン版DL:  Windows (64bit+32bit) ,   macOS (64bit+32bit)

▼アプリケーションを起動する
ビルドしたアプリケーション “[プロジェクト名].exe” を起動すると、以下のような画面が 表示されます。

 

▼オーディオデバイス設定パネルを開く
ウインドウ左上の[Options]→ [Audio/MIDI Settings]をクリックしてオーディオデバイス設定パネルを開きます。

 

▼オーディオデバイス設定パネル
各項目の意味については、前回の記事の表の通りです。
なお、前回の記事には無かった[Feedback Loop]が追加されていますが、これはマイク入力とスピーカー出力との間でフィードバックループが発生してしまうのを防ぐために予めテンプレート側で実装されているスペシャルな機能です。

 

▼ASIOの設定を行う
[Audio device type]のコンボボックスから”ASIO”を選択すると、ドライバモードがASIOに切り替わり、GUIも変更されます。
[Device]のコンボボックスにASIO対応のデバイス一覧が表示されるので、使用するオーディオデバイスを選択します。
[Control Panel]ボタンをクリックすると、ASIOデバイス毎に設けられたコントロールパネルが開きます。

これで、ASIOに対応したスタンドアローンVSTプレイヤーが完成しました。
本記事で紹介した “Audio Plug-In”のテンプレートからASIO対応アプリを作ることで、低レイテンシーなオーディオ入出力機能を持ったアプリを作ることができます。

 

おまけ.

今回の記事を試してみたい…でもVSTプラグインのDSPとGUIの実装が面倒だ…、と思われた方には、JUCEのサンプルプロジェクト”JuceDemoPlugin”を改造するのがお勧めです。
“JUCE/examples/audio plugin demo”にある.jucerファイルを開き、上記手順の通りに設定を行うことで試すことができます。
VSTiの実装の参考にもなるでしょう。

JUCEでASIO対応アプリケーションを作る

JUCEの公式チュートリアルでもあまり触れられていませんが、JUCEの機能を使うことで、ASIOドライバ対応のアプリケーションを簡単に作ることができます。
本記事では、ASIO対応アプリケーションの作成におけるチュートリアルとしてサンプルプロジェクト”Plugin Host”をASIOに対応させる作業を紹介します。
新規プロジェクトから作成したアプリケーションをASIO対応させるまでの作業については後日投稿予定です。

■本記事の概要

1. ASIO SDKをダウンロードする
2. ASIO SDKのzipファイルを解凍する
3. Projucerでサンプルプロジェクト”Plugin Host”のjucerファイルを開く
4. [Modules] → [juce_audio_devices] → [JUCE_ASIO]を “Enabled”にする
5. [Exporters] → [Header Search paths]にASIO SDKのパスを入力する
6. プロジェクトを保存し、IDE (Visual Studio)でビルドする
7. ビルドしたアプリケーションを起動し、オーディオ設定パネルを開く
8. オーディオドライバ一覧から”ASIO”を選択する
9. ASIO対応デバイスのオーディオ入出力を設定する

■ASIOとは?


出典:https://www.steinberg.net/en/company/technologies.html
ASIO(Audio Stream Input Output)は、低遅延、高同期性、高いスループットを特徴とする、オーディオデバイスのドライバインタフェースです。
Windowsにも予めオーディオドライバ(DirectSound、WASAPI)は存在しますが、ASIOはそれよりもレイテンシー(データ送信から音声が出力されるまでの遅延時間)を低く抑えることができるので、例えばMIDI入力を受けてから実際に音が耳に届くまでの時間はOS標準のドライバよりも圧倒的に短くすることができます。
ASIOではマシンの処理速度が許す限りはあらゆるチャンネル数の入出力、標本化周波数、量子化ビット数のデータを扱うことができるので、マルチチャンネルシステムの構築を実現することができます。
つまり、DAWやハイレゾオーディオプレーヤーに代表されるASIO対応のアプリケーションの土台部分を簡単に作ることができます。そう、JUCEなら。

 


1.ASIO SDKをダウンロードする

Steinbergの開発者向けページからダウンロードすることができます。
URL:https://www.steinberg.net/en/company/developers.html

 


2.zipファイルを解凍する

ASIO SDKをダウンロードしたら、zipファイルを解凍しておきましょう。
本記事では、”C:/SDKs/Steinberg SDK/ASIOSDK2.3″に解凍しておきます。
ASIO SDKにはソースコード、サンプルプロジェクト、ドキュメントが含まれています。

 


3.Projucerでプロジェクトを開く

本記事では、JUCEフレームワークに付属されたサンプルプロジェクト”Plugin Host”を事例に、ASIOに対応させる手順を紹介します。
このサンプルプロジェクトは、DAWのようにオーディオデバイスからの音声入力・出力やMIDI入出力を行うことができるアプリケーションです。また、VSTプラグインをロードすることができるので、オーディオ入力にエフェクトを掛けたり、MIDI入力からプラグインを操作することができます。

◆今回使用するサンプルプロジェクト”JUCE/examples/audio plugin host”

◆ “Plugin Host.jucer”をProjucerで読み込みます

 


4.JUCEモジュール設定でASIO対応を有効にする

①プロジェクト設定から[Modules]→[juce_audio_devices]を選択します。
②[JUCE_ASIO]を “Enabled”に変更します。

 


5.エクスポート設定でASIO SDKのヘッダーファイルパスを追加する

①[Exporters]から、ビルドを実行するIDEのビルド設定アイテムを選択します。
②[Header Search Paths]欄に、上記(2)の作業で配置したASIO SDKのソースコードへのパスを入力します。
本記事では “C:/SDKs/Steinberg SDK/ASIOSDK2.3/common” を入力しました。
※画像ではReleaseビルドの設定項目にパスを追加していますが、DebugビルドでもASIO対応させたい場合は、同様にDebugビルドの設定項目にパスを追加してください。

 


6.プロジェクトの保存とIDE (Visual Studio)でビルド

Projucerでの作業が完了したら、プロジェクトを保存し、IDE(Visual Studio)でプロジェクトを開きます。
AppConfig.h内の”JUCE_ASIO”定義が有効になっていることと、ASIO SDKのヘッダーファイルが参照されていることが確認できたら、ビルドを実行します。

◆ AppConfig.h

◆ ASIO SDKへの参照

 


7.ビルドしたアプリケーションの起動とASIOの有効化

▼アプリケーションを起動する
ビルドした”Plugin Host.exe”を起動すると、以下のウインドウが表示されます。

▼オーディオデバイス設定パネルを開く
メニュー項目の[Options] → [Change the audio device settings]をクリックまたは、ショートカットキーの[Ctrl + A]から、オーディオデバイス設定パネルを開きます

▼オーディオデバイス設定パネル
オーディオデバイス設定パネルを開いた際に、Windows Audio(WASAPI)で動作している場合の例を以下に示します。

Audio device type オーディオドライバの形式(WASAPI/DirectSound/ASIO)を選択
Output オーディオ出力先の有効/無効
Input オーディオ入力元の有効/無効
[Test]ボタン [Output]デバイスから動作確認用のサイン波を鳴らします
Audio buffer size オーディオデバイスのバッファサイズ(サンプル数/バッファ)
Active MIDI Inputs .MIDI機器ごとの入力の有効/無効
MIDI Output MIDI信号を出力するデバイス・ポートを選択

 

▼オーディオデバイスドライバをASIOに切り替える
[Audio device type]のコンボボックスから”ASIO”を選択します。

▼ASIO対応デバイスの設定を行う
[Device]のコンボボックス、[Control Panel]ボタン、[Reset Device]ボタンが追加されます。
[Device]のコンボボックスにASIO対応のデバイス一覧が表示されるので、使用するオーディオデバイスを選択します。
[Control Panel]ボタンをクリックすると、ASIOデバイス毎に設けられたコントロールパネルが開きます。

▼ASIO対応で低レイテンシーの快適なVSTホストが完成しました。

 

今回はASIO対応アプリケーションを作成するために最低限必要な設定項目を説明するため、サンプルプロジェクトを改造する手順で紹介しました。新規プロジェクトを作成した場合は、これ以外に必要なコードの記述もありますので、別記事にて掲載予定です。

Raspberry Pi でJUCEをビルドする -Raspbian Stretch対応-

2017年8月にRaspbian Stretch がリリースされました。
7月に投稿したレシピからの変更点がありますので、別記事として投稿しました。

レシピ

ROLI社のFabianがJUCE Forumにレシピをポストしてくれました。
ターミナルから以下のコマンドを実行するとビルドします。
※cdコマンドのディレクトリは各自の環境に合わせて入力してください。

// JUCEリポジトリをクローン
$ git clone https://github.com/WeAreROLI/JUCE.git

// 依存パッケージをインストール
$ sudo apt-get install clang freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 ladspa-sdk

// 作業ディレクトリに移動
$ cd JUCE/extras/Projucer/Builds/LinuxMakefile

// GCCでコンパイルする場合
$ make CXX=g++ -j4

// LLVM-Clangでコンパイルする場合
$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a" -j4

 

1.準備

まずはJUCEをダウンロードしましょう。公式ダウンロードサイト、Windows/macOS/Linuxから選択することができますが、Linux向けを選択すれば良いです。
※Linux用のProjucerが同梱されていますがx86をターゲットにしているため、CPUアーキテクチャが異なるRaspberry Pi (ARM)上では動作しません。Raspberry Pi用のProjucerは上記レシピでビルドすることができます。

GitHubからクローンする場合は、ターミナルを開いて次のコマンドを実行します。

$ git clone https://github.com/WeAreROLI/JUCE.git

 

2.依存パッケージのインストール

$ sudo apt-get install clang freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 ladspa-sdk

JUCEライブラリのビルドに必要な依存パッケージをインストールします。ついでにLLVM-Clangコンパイラをインストールします。なお、GCCでもコンパイルできることを確認しています。

 

3.作業ディレクトリへの移動

$ cd JUCE/extras/Projucer/Builds/LinuxMakefile

LinuxMakefileが置かれたディレクトリに移動します。
※ディレクトリは各自の環境に合わせて入力してください。
※サンプルプロジェクトの一部に”LinuxMakefile”用プロジェクトが同梱されています。

 

4.コンパイル

4-1.GCCでコンパイルする場合

 $ make CXX=g++ -j4

LinuxMakefileからビルドを実行します。
ビルドモードは”Debug”がデフォルトです。
各種オプションについては以下の通りです。

CXX=g++
LinuxMakefileオプションです。C++コンパイラにg++を使用します

・ -j4
… makeコマンドのオプションです。同時に実行できるジョブの数(並行処理の数)を指定します。必須ではありませんが、CPUのコア数を有効利用できます。

※Raspbian Jessieの場合は、こちらのバグに当たるためアーキテクチャをコマンドから指定する必要がありましたが、Raspbian Stretchでは解消されたようです。

4-2.LLVM-Clangでコンパイルする場合

$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a" -j4

LinuxMakefileからビルドを実行します。
ビルドモードは”Debug”がデフォルトです。
各種オプションについては以下の通りです。

CXX=clang++
LinuxMakefileオプションです。C++コンパイラにclang++を使用します

“TARGET_ARCH=-march=armv7-a”
… LinuxMakefileオプションです。ターゲットアーキテクチャを明示的に指定します
※Raspbian Jessieでは不要でしたが、Raspbian Stretchでは指定が必要なようです。

・ -j4
… makeコマンドのオプションです。同時に実行できるジョブの数(並行処理の数)を指定します。必須ではありませんが、Raspberry Piのコア数を有効利用できます。

 

5.補足:Releaseビルド時のコマンド

// GCCでコンパイルする場合
$ make CXX=g++ -j4 CONFIG=Release

// LLVM-Clangでコンパイルする場合
$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a" -j4 CONFIG=Release

・CONFIG=Release
… LinuxMakefileオプションです。”Release”モードでビルドします。

筆者にて動作確認した環境

◆ハードウェア
・Raspberry Pi 3 Model B

◆OS
・Raspbian Stretch (Debian 9系)

◆JUCE バージョン
・JUCE 5.3.2

Raspberry Pi でJUCEをビルドする -Raspbian Jessie対応-

Raspberry Pi (Raspbian)でJUCEのプロジェクトをビルドしたので、レシピを紹介します。

同梱のVSTプラグイン/ ホストのサンプルプロジェクトをビルドしました。
PCと遜色なく快適に動きますし、MIDI入出力、音声入出力が可能なので、省電力なVSTシンセサイザ/エフェクトとして使用することができます。

レシピ

ROLI社のFabianがJUCE Forumにレシピをポストしてくれました。
ターミナルから以下のコマンドを実行するとビルドします。
※cdコマンドのディレクトリは各自の環境に合わせて入力してください。

// JUCEリポジトリをクローン 
$ git clone https://github.com/WeAreROLI/JUCE.git 

// 依存パッケージをインストール
$ sudo apt-get install clang freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 ladspa-sdk

// 作業ディレクトリに移動
$ cd JUCE/extras/Projucer/Builds/LinuxMakefile

// GCCでコンパイルする場合
$ make CXX=g++ "TARGET_ARCH=-march=armv7-a" -j4

// LLVM-Clangでコンパイルする場合
$ make CXX=clang++ -j4

1.準備

先ずはJUCEをダウンロードしましょう。公式ダウンロードサイト、Windows/macOS/Linuxから選択することができますが、Linux向けを選択すれば良いです。
※Linux用のProjucerが同梱されていますがx86をターゲットにしているため、CPUアーキテクチャが異なるRaspberry Pi (ARM)上では動作しません。Raspberry Pi用のProjucerは上記レシピでビルドすることができます。

GitHubからクローンする場合は、ターミナルを開いて次のコマンドを実行します。

$ git clone https://github.com/WeAreROLI/JUCE.git

 

2. 依存パッケージのインストール

$ sudo apt-get install clang freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 ladspa-sdk

JUCEライブラリのビルドに必要な依存パッケージをインストールします。ついでにLLVM-Clangコンパイラをインストールしています。なお、GCCでもコンパイルできることを確認しています。
※GCCでビルドする場合、こちらのバグに当たってしまうことを確認しています。GCCでビルドする際は”make”コマンドのオプションに “TARGET_ARCH=-march=armv7-a” を追加すると正常にビルドが実行されます。

 

 3.作業ディレクトリへの移動

$ cd JUCE/extras/Projucer/Builds/LinuxMakefile

LinuxMakefileが置かれたディレクトリに移動します。
※ディレクトリは各自の環境に合わせて入力してください。
※サンプルプロジェクトの一部に”LinuxMakefile”用プロジェクトが同梱されています。

 

4. コンパイル

4-1.GCCでコンパイルする場合

 $ make CXX=g++ "TARGET_ARCH=-march=armv7-a" -j4

LinuxMakefileからビルドを実行します。
ビルドモードは”Debug”がデフォルトです。
各種オプションについては以下の通りです。

CXX=g++
LinuxMakefileオプションです。C++コンパイラにg++を使用します

・ -j4
… makeコマンドのオプションです。同時に実行できるジョブの数(並行処理の数)を指定します。必須ではありませんが、CPUのコア数を有効利用できます。

※Raspbian Jessieの場合は、こちらのバグに当たるためアーキテクチャをコマンドから指定する必要があります。Raspbian Stretchでは解消されたようです。

4-2.LLVM-Clangでコンパイルする場合

$ make CXX=clang++ -j4

LinuxMakefileからビルドを実行します。
ビルドモードは”Debug”がデフォルトです。
各種オプションについては以下の通りです。

・CXX=clang++
… LinuxMakefileオプションです。C++コンパイラにclang++を使用します

・ -j4
… makeコマンドのオプションです。同時に実行できるジョブの数(並行処理の数)を指定します。必須ではありませんが、Raspberry Piのコア数を有効利用できます。

 

5.補足:Releaseビルド時のコマンド

// GCCでコンパイルする場合
$ make CXX=g++ -j4 CONFIG=Release

// LLVM-Clangでコンパイルする場合
$ make CXX=clang++ "TARGET_ARCH=-march=armv7-a" -j4 CONFIG=Release

・CONFIG=Release
… LinuxMakefileオプションです。”Release”モードでビルドします。

筆者にて動作確認した環境

◆ハードウェア
・Raspberry Pi 2 Model B
・Raspberry Pi 3 Model B

◆OS
・Raspbian Jessie With Pixel (Debian 8系)

◆JUCE バージョン
・JUCE 5.3.2