ボタンに反応して行われる処理を追加していきます。
だんだんと完成がみえてきたね~
JUCEプログラミング、AudioPlayerアプリを作成するチュートリアルを進めていきます。ボタンのコールバック関数をプログラミングしてくところから進めていきます。
公式チュートリアルの「Opening a file」の項目です。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・JUCEで音声プログラミングを学びたい人
・JUCEのAudioチュートリアルを進めている人
ボタンのコールバック関数
今回のアプリでは、3つのボタンがありますので、それぞれ次のように対応するコールバック関数を定義します。
1.openボタン:openButtonClicked関数
2.playボタン:playButtonClicked関数
3.stopボタン:sropButtonClicked関数
これらの関数は、「コールバック関数」として、コンストラクタでButtonオブジェクトのonClickにラムダ式で記載されているもので、ボタンを押したら実行される関数になっています。
リスナー関数、コールバック関数、イベントハンドラ、基本的に用語の違いですね、混乱しますね。
これらの言葉が出てきたら、「任意のタイミングで実行される関数」みたいな共通のイメージをもつといいね~
openButtonClicked関数
openボタンが押されたら、音声ファイルの場所を指定するウィンドウを出す必要があります。
juceのFileChooserクラスがこの動作を担ってくれます。
void MainComponent::openButtonClicked()
{
//[1]ファイルの選択画面機能を持つFileChooserクラスのchooseを作成します。
juce::FileChooser chooser("Select a Wave file to play...",
{},
"*.wav");
//[2]このようにすることで選択画面ループ(ファイル選択画面を表示)となるようです。
if (chooser.browseForFileToOpen())
{
//[3]ファイルを取得します。
auto file = chooser.getResult();
auto* reader = formatManager.createReaderFor(file);
if (reader != nullptr)
{
//[4]一時的にnewSoureというAudioFormatReaderSourceを準備します。
std::unique_ptr<juce::AudioFormatReaderSource> newSource(new juce::AudioFormatReaderSource(reader, true));
//[5]
transportSource.setSource(newSource.get(), 0, nullptr, reader->sampleRate);
playButton.setEnabled(true);
//[6]readerSourceにnewSourceのリソースを設定します。
readerSource.reset(newSource.release());
}
}
}
[1]では、ファイルを選択するための機能、JUCE::FileChooserクラスの変数を作成します。引数として、選択画面のタイトル、選択可能なファイル形式を設定しています。今回は「.wav」ファイルのみ指定できるようになっています。
※第三引数の「”*.wav”」を「”*.wav; *.aif; *.aiff”」とすることで、選択できるファイルにaif形式を追加することができます。選択できるファイルというだけで、AudioFormatManager(formatManager変数)が対応していないと実際のプログラムで扱うことはできないので、注意です。
上のようなファイル選択画面を出現するために[2]のように記載するようです。
[3]では、音声ファイルを読み取る処理を行います。AudioFormatManagerクラスのformatManagerから「createReaderFor」関数を利用してAudioFormatReaderクラスのオブジェクトを取得します。AudioFormatReaderクラスは、音声の生データをなんやかんやうまく読み取れるようなクラス、程度の理解ですが、reader変数に音声データへの参照が入る、というイメージでしょうか。
以前の図を少し更新しました。[4]では、AudioFormatReaderSourceのnewSourceをnewするときに、readerを引数に与えていますので、次の図のように、AudioFormatManagerがAudioFormatReaderを返すようなイメージを追加しました。
そして、[5]の手順で、上の図の一番左のAudioTransportSourceの監視先をnewSourceで得た音声ファイルの場所に変更しています。
[6]では、一時的に取得したnewSourceをreaderSourceに設定します。スマートポインタの使い方になりますので、参考としたページをリンクしておきます。
スマートポインタのreset関数は、リソースの所有権を放棄し、新たなリソースの所有権を設定するとのことです。
また、スマートポインタのrelease関数は、リソースの所有権を放棄し、放棄したリソースを返り値として返します。
ということで、newSourceに設定した音声ファイルをreaderSourceへ設定しなおしている処理ということができます。
playButtonClickedとstopButtonClicked関数
playとstopに関しては、changeState関数で音声の状態を変更させます。
ここで簡潔に記載できるのは、changeStateという関数に音声の再生状態を変更する処理を一括して記載したおかげです。
void MainComponent::playButtonClicked()
{
changeState(Starting);
}
void MainComponent::stopButtonClicked()
{
changeState(Stopping);
}
changeState関数内でそれぞれの音声の状態に変更され、音声の状態が変更されると「changeListenerCallback 」関数が呼び出されますので、一連の再生状態の変更が可能となります。
GUI関連の処理
サンプルプログラムを見ると記載されているプログラムです。「paint」関数と「resized」関数に、以下のようにプログラムを追記することで、アプリのGUIが表示されます。
paint関数では、背景を塗りつぶす処理を行っています。
resized関数では、ButtonオブジェクトのsetBounds関数を利用して、ボタンを配置する位置とサイズを決めています。
void MainComponent::paint (juce::Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
}
void MainComponent::resized()
{
openButton.setBounds(10, 10, getWidth() - 20, 20);
playButton.setBounds(10, 40, getWidth() - 20, 20);
stopButton.setBounds(10, 70, getWidth() - 20, 20);
}
ここまで実装すると、音声ファイルを読み込んで再生できるアプリができたかと思います。
再生するwavがない場合、このスネア音をお使いください。
ソースコード
以下のリモートリポジトリにここまでのプログラムを配置しましたので、もしよろしければ、ご利用ください。