次はcppに機能を実装していきます。
ようやく中身のプログラミングまできたね〜
前回の記事で、ヘッダーファイルを記載しました。ヘッダーファイルは目次のようになっているので、全体的にどんな関数がどのように動作しているのかを俯瞰で見やすく、掴みやすいと思いました。今回から、コンストラクタから順次実装していきます。
チュートリアルページはこちらになります。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・JUCEプログラミングでオーディオアプリを作成したい人
・JUCEで音声再生を行いたい人
コンストラクタ
各種の初期化
「MainComponent.cpp」のコンストラクタで次のようにプログラムを変更しました。
MainComponent::MainComponent()
: state(Stopped)//[1]メンバイニシャライザでstateを初期化します。
{
[1]では、C++の機能であるメンバイニシャライザを利用して、列挙型で定義したTransportState型のstate変数を「Stopped」に設定しています。初期状態で音声の再生状態を「停止中」に設定しています。
引き続き、コンストラクタ内に、以下[2]のプログラムを追記します。今回のアプリでは3つあるボタンの初期化を行っています。
//[2]ボタンの初期化
addAndMakeVisible(&openButton);
openButton.setButtonText("Open...");
openButton.onClick = [this] { openButtonClicked(); };
addAndMakeVisible(&playButton);
playButton.setButtonText("Play");
playButton.onClick = [this] { playButtonClicked(); };
playButton.setColour(juce::TextButton::buttonColourId, juce::Colours::green);
playButton.setEnabled(false);
addAndMakeVisible(&stopButton);
stopButton.setButtonText("Stop");
stopButton.onClick = [this] { stopButtonClicked(); };
stopButton.setColour(juce::TextButton::buttonColourId, juce::Colours::red);
stopButton.setEnabled(false);
共通したボタンの処理
1.アプリのGUIへの反映をさせる「addAndMakeVisible」関数
2.setButtonTextでボタンのテキストを変更
3.onClickに「ラムダ式」でクリックされたときの処理を登録
Buttonクラスでは、このようにonClicktという「std::function」クラスのメンバ変数がpublicに準備されています。ここにクリックしたときのコールバック関数をラムダ式で与えることで、クリック時の処理が登録できるみたいです。ラムダ式で実行される内容としては、これから記載するそれぞれの「●●Clicked」という関数を実行するようにしています。
意外と出てくるラムダ式です。
再生、停止ボタンの初期状態
playButtonとstopButtonに関しては、ボタンの色を変更するsetColour関数を実行しています。第2引数に「JUCE::Colours」名前空間で、色の指定ができるようです。
また、この2つのボタンについては、openButtonを押して音声ファイルが読み込まれるまでは押すことができないように、初期状態は「setEnabled」関数で押すことができないようにしています。
formatManagerの取り扱い音声一覧
コンストラクタの最後には、次のプログラムを追記しています。
formatManager.registerBasicFormats();//[3]音声の拡張子の定義です。
transportSource.addChangeListener(this);//[4]changeListnerCallbackをtransportSourceに紐づけます。
setAudioChannels(0, 2);//バスレイアウトを簡易的に出力2チャンネル(ステレオ)としました。
setSize (800, 600);
}
[3]で、formatManagerの「registerBasicFormats」関数を実行することで、標準的な音声データである「wav,aiff,aif」が利用できるようになります。また、ProjucerのModulesの「juce_audio_formats」の設定項目を変更することで「oggやmp3」など、他のフォーマットを取り扱うようにできるらしいのです。
[4]のプログラムで、AudioTransportSourceクラスのTransportSourceの音声の状態が変化したときのコールバック関数を紐づけます。
以前の記事で作成した音声処理関連のクラスの関係図を改めて貼り付けてみます。
AudioTransportSourceクラスが音声の操作を行うというイメージから、ここにコールバック関数が紐づくのも何となくイメージができます。
AudioTransportSourceの変化を検知
「MainConponent.cpp」に音声の再生状態の変化をとらえる「changeListenerCallback」関数を実装していきます。また、この中で、「changeState」関数を呼び出しますので、続いて「changeState」関数も定義します。
void MainComponent::changeListenerCallback(juce::ChangeBroadcaster* source)
{
if (source == &transportSource)//[1]コールバックされたものがtransportSourceの場合です。
{
if (transportSource.isPlaying())//[2]transportSourceの状態を確認します。
changeState(Playing);
else
changeState(Stopped);
}
}
[1]の条件としては、changeListenerCallbackでは、transportSourceのアドレスとsource(ポインタなので、格納されている値は、何かしらのアドレスとなります。)が一致していた場合、transportSourceの音声プレイバックの状態変化で起きたコールバックとみなす条件になります。
この条件が入っているのを見ると、changeListenerCallbackは他のタイミングにも紐づいていて(または紐づけることができる)、実行されるようなコールバック関数ということができると思います。
[2]の条件で、改めて、AudioTransportSourceが再生中の状態かどうかを確認しています。「isPlaying」関数は、再生中であればTrueを返すような関数です。
再生中であれば、stateをPlayingにしたいので、changeState関数で「音声の再生中としてアプリの状態を変更」します。再生中かどうかをここで確認するのは、再生準備中からユーザー操作などによって停止状態に戻ったりする可能性もあるからだと思います。
そして、先ほど利用したchangeState関数を実装していきます。この関数は、オーディオの再生状態や、再生状態と連携させてアプリの状態と連携するような機能を兼ね備えます。主にボタンの状態と、transportSourceの再生停止を実際に行なってtransportSourceを次の状態に遷移させるような役割を果たしています。他にも今後実装する、playボタンとstopボタンからも呼び出されて、アプリの状態(ボタンと音声再生状態)を変化させます。
void MainComponent::changeState(MainComponent::TransportState newState)
{
if (state != newState)
{
state = newState;
switch (state)
{
//[3]音声再生が完全に停止状態、playボタンを有効にしてstopボタンを無効にし、
//音声の再生開始ポイントを最初に持ってきます。
case Stopped:
stopButton.setEnabled(false);
playButton.setEnabled(true);
transportSource.setPosition(0.0);
break;
//[4]再生開始待ち状態です。playボタンを押せなくします。また、音声再生を開始させます。
case Starting:
playButton.setEnabled(false);
transportSource.start();
break;
//[5]音声の再生中です。stopボタンを押すことができるようにします。
case Playing:
stopButton.setEnabled(true);
break;
//[6]停止待ち状態です。音声の停止処理を行います。
case Stopping:
transportSource.stop();
break;
}
}
}
今回は少し長くなってしまいましたので、ボタンのコールバック関数などについてはまた次の記事で書き連ねていきたいと思います。