とにかくチュートリアルのタイトルが長くなってきました。
内容も少しづつ難しくなってきたね~
JUCEプログラミング、Audioチュートリアルの項目「Looping audio using the AudioSampleBuffer class (advanced)」を勉強しています。まずは、サンプルプログラムである「LoopingAudioSampleBufferAdvancedTutorial_01.h」の内容を実装してみました。今回のサンプルプログラムは「01」と「02」があります。「01」が、「Thread-safe techniques」の項目までの実装例で、「02」が「Reading the audio on the background thread」の項目を実装するようになっているようです。まずは、「01」の実装からアプリが動くところまでをやってみます。
公式チュートリアルはこちらになります。
こんな人の役に立つかも
・JUCEフレームワークに入門したい人
・JUCEで音声プログラミングを学びたい人
・JUCEのAudioチュートリアルを進めている人
動作するアプリとチュートリアルの概要
一つ前のチュートリアルと同じ機能を持つアプリが作成できます。以前のアプリでは、いくつかエラーが起きる可能性がありましたが、その点を「スレッドセーフ」な方法を利用して、エラーなく動作させるような仕組みに変更するチュートリアルになります。
以前の方法の問題点としては、違うスレッドからオーディオスレッドのコントロールをしていたという点でした。getNextAudioBlock関数はオーディオスレッドというスレッドで、オーディオシステム開始時からひたすら繰り返し音声出力にデータを与え続けます。このストップするタイミングとスタートするタイミングをメッセージスレッド(アプリが動作しているスレッド)から直接操作してしまうと、そのタイミングによっては何かしら不具合が起きてしまうというものでした。
具体的な例がいくつかありましたが、いまいち理解しきれていません^^;
何となくですが、一番の問題点としては、openButtonClicked関数で音声ファイルを読みこみなおしたときに既にgetNextAudioBlock関数で確保したバッファーとのサイズが変わってしまったり、とメモリの確保的な部分でいろいろ問題が起きる、と雰囲気で感じています。
今回導入する「JUCE::ReferenceCountedObject」クラスを利用すると、「バックグラウンドに動作するスレッド」を作成してAudioBufferSampleを音声ファイル毎に追加していき、安全にバッファーを管理するような雰囲気のようです。プログラムを一度見た感じの感想です^^;
現状、問題点の把握があいまいで、チュートリアルに身を任せている状態なのですが、プログラムを見てより問題点と改善点を明確にしていきたいです。
今はまだ、イメージが明確ではないところも、プログラムを見て明確にしていきたいと思います。
MainComponent.h
ReferenceCountedBuffer
class MainComponent : public juce::AudioAppComponent,/*[1]スレッドクラスを継承します。*/ private juce::Thread
{
public:
//[2]以下の入れ子クラスを追加しました。
class ReferenceCountedBuffer : public juce::ReferenceCountedObject
{
public:
//[3]このtypedefが重要みたいです。
typedef juce::ReferenceCountedObjectPtr<ReferenceCountedBuffer> Ptr;
ReferenceCountedBuffer(const juce::String& nameToUse,
int numChannels,
int numSamples)
: name(nameToUse),
buffer(numChannels, numSamples)
{
DBG(juce::String("Buffer named '") + name + "' constructed. numChannels = " + juce::String(numChannels) + ", numSamples = " + juce::String(numSamples));
}
~ReferenceCountedBuffer()
{
DBG(juce::String("Buffer named '") + name + "' destroyed");
}
juce::AudioSampleBuffer* getAudioSampleBuffer()
{
return &buffer;
}
int position = 0;
private:
juce::String name;
juce::AudioSampleBuffer buffer;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ReferenceCountedBuffer)
};
//...略...
[1]で、「juce::Thread」クラスを継承しています。[2]では、juce::ReferenceCountedObjectクラスを継承したReferenceCountedBufferクラスを定義しています。これは、入れ子クラスというらしく、クラスの中にクラスを定義しています。
C++の入れ子クラスについては以下のサイトを参考にさせていただきました。
ReferenceCountedObjectを継承した「ReferenceCountedBuffer」は、[3]のように、typedefをして、「ReferenceCountedObjectPtr<ReferenceCountedBuffer>」の型「Ptr」を宣言しておきます。あとからこの型で変数を作成することで、参照された数を数えることができるようです。ここもなんだか高度なテクニックを使っているような感じがします。以下の公式サイトにも、typedefで定義して使ってね、とありました。
このあたりは、C++をもっと勉強しなければいけないな、と感じるところです^^;
とりあえずそうやって使うんだな、と思い込みました。
privateなメンバ
コンストラクタの最後には、privateなメンバを追加しておきます。
private:
//[4]プライベートな関数の定義と、JUCEクラスのメンバ変数を定義しています。
void run() override;
void checkForBuffersToFree();
void openButtonClicked();
void clearButtonClicked();
juce::TextButton openButton;
juce::TextButton clearButton;
juce::AudioFormatManager formatManager;
juce::ReferenceCountedArray<ReferenceCountedBuffer> buffers;
ReferenceCountedBuffer::Ptr currentBuffer;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
ヘッダーに使われている要素技術を調べていたら結構C++を知っていないと読み解くのが難し章な内容も多い印象です。今回はここまでとして、次回からcppの実装内容に行きたいと思います。
ヘッダーだけでも結構新しい要素がでてきて難しいですね。
このチュートリアル、advanceだね・・・