JUCEプログラミング、Draw audio waveform 5、描画の子コンポーネント化2

今度は生まれ変わったMainComponentクラスです。

昔よりシンプルになるね~

前回に引き続き、描画部分を子コンポーネントに移植した音声波形表示を行っていきます。今回は、MainComponentクラスを見ていきます。基本的に、子コンポーネント化する以前のプログラムと比較するように見ていきたいと思います。

本日はひたすらだらだらとMainComponentの中身を書くことになります^^;

※Draw audio waveformチュートリアルの最後のExerciseの内容となります、答えを知りたくない場合は見ないほうが良い点、ご了承ください。

こんな人の役に立つかも

・JUCEチュートリアル「Draw audio waveforms」のExerciseの実装例を知りたい人

目次

MainComponent.h

まずは、MainComponent.hから実装をしていきます。

[1]では、前回作成したDrawComponent.hをインクルードして「SimpleThumbnailComponent 」クラスと「SimplePositionOverlay」クラスを利用できるようにしています。

#include <JuceHeader.h>
#include "DrawComponent.h"
//[1]DrawComponentをincludeします。

class MainComponent : public juce::AudioAppComponent,
    public juce::ChangeListener//, private juce::Timer//del
{
public:
    MainComponent();
    ~MainComponent() override;

    void prepareToPlay(int samplesPerBlockExpected, double sampleRate) override;
    void getNextAudioBlock(const juce::AudioSourceChannelInfo& bufferToFill) override;
    void releaseResources() override;
    void changeListenerCallback(juce::ChangeBroadcaster* source) override;
    
    void paint(juce::Graphics& g) override;
    void resized() override;

    //void timerCallback() override;//[2]timerは不要になりました。

private:
    enum TransportState
    {
        Stopped,
        Starting,
        Playing,
        Stopping
    };

    juce::TextButton openButton;
    juce::TextButton playButton;
    juce::TextButton stopButton;

    juce::AudioFormatManager formatManager;
    std::unique_ptr<juce::AudioFormatReaderSource> readerSource;
    juce::AudioTransportSource transportSource;
    TransportState state;

    juce::AudioThumbnailCache thumbnailCache;
    //juce::AudioThumbnail thumbnail;//[3]AudioThumbnailクラスも不要となります。

    void changeState(TransportState newState);
    void openButtonClicked();
    void playButtonClicked();
    void stopButtonClicked();

    void transportSourceChanged();
    //void thumbnailChanged();//[4]これも、移植したので消します。


    //[5]以下の関数も、移植したので、取り除きます。
    //void paintIfNoFileLoaded(juce::Graphics& g, const juce::Rectangle<int>& thumbnailBounds);
    //void paintIfFileLoaded(juce::Graphics& g, const juce::Rectangle<int>& thumbnailBounds);

    //[6]DrawComponentファイルに記載したクラスを定義しました。
    SimpleThumbnailComponent thumbnailComp;
    SimplePositionOverlay positionOverlay;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent)
};

[2]~[5]については、DrawComponentファイルへ移植してしまった機能なので、MainComponentクラスから削る修正を行いました。一方で、[6]では、「SimpleThumbnailComponent 」クラスと「SimplePositionOverlay」クラスのprivateなメンバ変数を作成しています。

MainComponent.cpp

コンストラクタ

MainComponent::MainComponent()
    : state(Stopped),
    thumbnailCache(5),
    thumbnailComp(512, formatManager, thumbnailCache),
//[1]追加します。
    positionOverlay(transportSource)
//[1]追加します。
{
//...略...

    //[2]可視化します。
    addAndMakeVisible(&thumbnailComp);
    addAndMakeVisible(&positionOverlay);

    formatManager.registerBasicFormats();
    transportSource.addChangeListener(this);
    //thumbnail.addChangeListener(this);//[3]移植した機能です。消します。

    setAudioChannels(0, 2);

    setSize (800, 600);

    //startTimer(40);//[4]これも移植した機能です。
}

[1]では、メンバイニシャライザで先ほど定義したSimpleThumbnailComponentクラスの「thumbnailComp」と、SimplePositionOverlayクラスの「positionOverlay」を引数を与えて初期化します。

[2]では、これらを可視化しています。

[3]のコールバック関数については、SimpleThumbnailComponentクラスへと機能を移植しましたので、不要となりました。音声のサムネイルを再描画するタイミングを取得するコールバック関数です。

[4]については、SimplePositionOverlayクラスへ移植しました。

changeListenerCallback関数

次に、コールバック関数を次のように変更しました。以前は、サムネイルの変更時のコールバック処理も追加していましたが、この処理はSimpleThumbnailComponentクラスに引き継ぎました。

void  MainComponent::changeListenerCallback(juce::ChangeBroadcaster* source)
{
    if (source == &transportSource) transportSourceChanged();
    //if (source == &thumbnail)       thumbnailChanged();//ここを削除しました。
}

音声の再生停止などの状態変化のコールバック処理のみに戻りました。

thumbnailChanged関数

この機能も、移植したので、削除しておきます。

//削除しました。
/*void MainComponent::thumbnailChanged()
{
    repaint();
}*/

changeState関数

void MainComponent::changeState(MainComponent::TransportState newState)
{
//...略...

        //ここを追加しました。
        default:
            jassertfalse;
            break;
        }
    }
}

「jassertfalse」は、デバッグビルドの時、assertionを出力してくれます。defaultは、他の条件に当てはまらない場合の処理です。普段はTransportStateが何かしらの状態にあると思うので、この条件に入ることはないはずなのですが、ここに入るとデバッグモードのときはエラーが出力されるような処理となっています。

あわせて読みたい

ここにきて追加されているということは、前のプログラムから改善されている、ということでしょうか・・・ほんとは前のプログラムからあるべきなのですが^^;

openButtonClicked関数

void MainComponent::openButtonClicked()
{
    juce::FileChooser chooser("Select a Wave file to play...",
        {},
        "*.wav");

//以下のコメントアウトは、以前のプログラムです。
/*
    if (chooser.browseForFileToOpen())
    {
        auto file = chooser.getResult();
        auto* reader = formatManager.createReaderFor(file);

        if (reader != nullptr)
        {
            std::unique_ptr<juce::AudioFormatReaderSource> newSource(new juce::AudioFormatReaderSource(reader, true));
            transportSource.setSource(newSource.get(), 0, nullptr, reader->sampleRate);
            playButton.setEnabled(true);
            thumbnail.setSource(new juce::FileInputSource(file));
            readerSource.reset(newSource.release());
        }
    }
*/
//上記のコメントアウトが以下のように変更されました。
    if (chooser.browseForFileToOpen())
    {
        juce::File file(chooser.getResult());

        if (auto* reader = formatManager.createReaderFor(file))
        {
            std::unique_ptr<juce::AudioFormatReaderSource> newSource(new juce::AudioFormatReaderSource(reader, true));
            transportSource.setSource(newSource.get(), 0, nullptr, reader->sampleRate);
            playButton.setEnabled(true);
            thumbnailComp.setFile(file);//[1]ここを変更しています。
            readerSource.reset(newSource.release());
        }
    }
}

コメントアウトで以前のプログラムを残してみました。やっていることは同じなのですが、書き方が代わり、若干スマートになった印象を受けます。chooser.getResultをfileにそのまま入れて、ローカル変数としてFileオブジェクトを作成、そのままnullptrの判定の条件の中にformatManager.createReaderSourceを入れてしまう点、より熟練のプログラマがリファクタリングをしたのですかね〜。

機能的に重要な点は、[1]thumbnailCompのsetFile関数で音声ファイルをセットするように変更された点となります。

paint関数

MainComponentで描画するものはなくなったので、中身を全てコメントアウトしました。(以前のものがどのような状態かわかりやすいようにコメントアウトしました。)

void MainComponent::paint (juce::Graphics& g)
{
    /*
    juce::Rectangle<int> thumbnailBounds(10, 100, getWidth() - 20, getHeight() - 120);

    if (thumbnail.getNumChannels() == 0)
        paintIfNoFileLoaded(g, thumbnailBounds);
    else
        paintIfFileLoaded(g, thumbnailBounds);
    */
}

resized関数

resize関数に、DrawComponentファイルに定義したクラスを配置します。

void MainComponent::resized()
{
//...略...

    //波形描画部分を配置します。
    juce::Rectangle<int> thumbnailBounds(10, 100, getWidth() - 20, getHeight() - 120);//[1]MainComponentの配置場所の矩形をセットします。
    thumbnailComp.setBounds(thumbnailBounds);//[2]波形描画クラスです。
    positionOverlay.setBounds(thumbnailBounds);//[3]垂直線描画クラスです。
}

[1]で、MainComponentクラスの波形描画する場所を矩形で設定しておきます。この矩形を引数に、「thumbnailComp」と「positionOverLay」をsetBoundsで位置指定することで配置します。これらのオブジェクトはComponentクラスを継承していますので、setBoundsで位置指定ができるのです。

最後に、さらにこれらの下に定義している、MainComponent.cppの「paintIfNoFileLoaded関数」「paintIfFileLoaded関数」「timerCallback関数」はMainComponentでは呼び出さなくなりましたので、削除(コメントアウト)しておきます。

結構変更点が多くて迷いました。

よかったらシェアしてね!
目次
閉じる