エクササイズとして、ラベルに時間表示をしてみよう、みたいな課題がありました。
再生時間取得もできるんだね!
JUCEプログラミング、音声再生アプリで一時停止機能を実装しましたが、さらにエクササイズとして、音声の再生時間を表示する機能を追加するという項目がありました。今回は、音声の再生時間をラベルオブジェクトに表示してみることにします。
公式チュートリアルはこちらですが、文章としては表記されておらず、zipファイルをダウンロードして「PlayingSoundFilesTutorial_03.h」の内容を実装して確認する必要がありました。本記事では、一時停止機能を備えた音声再生アプリに追記した部分を記載していきます。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・JUCEプログラミングでオーディオアプリを作成したい人
・JUCEで音声再生を行いたい人
機能追加の概要
JUCEには、タイマーという機能があります。タイマーは、一定時間経過で実行されるコールバック関数を定義することができます。今回、音声の再生時間をタイマーの一定時間間隔でtransportSourceオブジェクトから取得して、GUIに反映させて現在の音声再生時間を表示する、ということを行います。
JUCEのタイマーオブジェクトは、10msec~20msec程度の正確さと記載があります。メインメッセージスレッドで実行されるので、処理が後回しになることもあるようですね。
※マイコンなどの、組み込み系プログラミングを経験している場合、JUCEのタイマーは組み込みの考え方に近いのような感じがします。マイコンのタイマーは、きっちりその時間に割り込まれるように厳密にプログラミングできますが、JUCEはソフトウェアレベルでのタイマーなので、もっとゆるい精度のようですね。
MainComponent.hへの追加事項
次のようにヘッダーファイルに追記しました。
class MainComponent : public juce::AudioAppComponent,
public juce::ChangeListener,
public juce::[1]Timer//←タイマーオブジェクトというものを利用します。
{
public:
//...略...
void changeListenerCallback(juce::ChangeBroadcaster* source) override;//add
void timerCallback() override;//[2]タイマーのコールバック関数をオーバーライドします。
//...略...
juce::TextButton openButton;
juce::TextButton playButton;
juce::TextButton stopButton;
//↓[3]ラベルオブジェクトを追加します。
juce::Label currentPositionLabel;
//...略...
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
[1]でjuce::Timerクラスを継承します。そして、[2]でtimerCallback関数をオーバーライドしています。このtimerCallback関数は、一定時間毎に呼び出される処理内容となります。
[3]では、音声ファイルの時間をGUIに表示するたのラベルオブジェクト「currentPositionLabel」を定義しています。
MainComponent.cppのプログラミング
コンストラクタの初期化
タイマーとラベルを初期化します。
MainComponent::MainComponent()
: state(Stopped)
{
//...略...
//[1]ラベルオブジェクトをGUIで可視化して、初期状態のテキストを設定します。
addAndMakeVisible(¤tPositionLabel);
currentPositionLabel.setText("Stopped", juce::dontSendNotification);
formatManager.registerBasicFormats();
transportSource.addChangeListener(this);
setAudioChannels(0, 2);
setSize (800, 600);
startTimer(20);//[2]タイマーのコールバック関数の実行間隔を指定してタイマーをスタートさせます。
}
[1]では、ラベルオブジェクトの可視化などの初期化を行います。
[2]で、タイマーをスタートさせます。startTimer関数の引数は、msec単位の数値を与えます。今回は20msec毎のコールバックとしてタイマーを起動させています。
resize関数で配置
resize関数にラベルの位置を指定して、配置しましょう。
void MainComponent::resized()
{
openButton.setBounds(10, 10, getWidth() - 20, 20);
playButton.setBounds(10, 40, getWidth() - 20, 20);
stopButton.setBounds(10, 70, getWidth() - 20, 20);
//↓これが、追加したラベルの配置になります。
currentPositionLabel.setBounds(10, 130, getWidth() - 20, 20);
}
timerCallback関数の追加
新たに追記するコールバック関数になります。
//以下、コールバック関数を追記しました。
void MainComponent::timerCallback()
{
//[1]条件で、音声が再生中の場合のみ、時間を表示します。
if (transportSource.isPlaying())
{
juce::RelativeTime position(transportSource.getCurrentPosition());
\\[2]時間の取得を行います。
//[3]分、秒、ミリ秒の取得への変換を行います。
auto minutes = ((int)position.inMinutes()) % 60;
auto seconds = ((int)position.inSeconds()) % 60;
auto millis = ((int)position.inMilliseconds()) % 1000;
auto positionString = juce::String::formatted("%02d:%02d:%03d", minutes, seconds, millis);
currentPositionLabel.setText(positionString, juce::dontSendNotification);
}
else
{
currentPositionLabel.setText("Stopped", juce::dontSendNotification);
}
}
JUCEのRelativeTimeクラスは、秒を使いやすく変換してくれるクラスのようです。
[2]では、「position」というRelativeTimeクラスにtransportSourceの「getCurrentPosition」関数で現在再生中の秒数を取得して、初期化しています。[3]のように、int型にキャストして剰余演算を行うことで、分、秒、ミリ秒を取得できるようです。
このように、再生中の音声ファイルの時間がラベルオブジェクトに表示されるようになれば成功です。