カテゴリー
C++ JUCE

juce::AudioSource関連クラス

  1. juce::File – ディスク上のファイル
    ・パスを表現している
    ・オーディオに関しては何も知らない
    ・AudioFormatReader作成時に使用される
    juce::File audioFile("/Users/username/audio/kick.wav");

    2. juce::AudioFormatReader – decoder/parser
     ・ファイルからオーディオを読む
     ・オーディオをバッファしない – 都度ディスクから読む
     ・thread-safeではない

    auto* reader = formatManager.createReaderFor(audioFile);

    3. juce::AudioFormatReaderSource – readerをstreamable sourceに変換する
     ・AudioFormatReaderをwrapしAudioSourceを作成する
     ・オーディオをバッファし、ポジションをトラッキングする
     ・real-timeプレイバックに使う
     ・コンストラクト時にreaderのownershipを保持する

    auto readerSource = std::make_unique<juce::AudioFormatReaderSource>(reader, true);
    
    カテゴリー
    C++

    command failed with a nonzero exit code

    XCodeのProduct -> Clean Build Folderでは上手くいかない場合

    xcodebuild clean -project YourApp.xcodeproj

    とすると、
    ** CLEAN FAILED **

    とログを吐き出すと思います。

    $ rm -rf "/Users/YourName/YourPath/YourApp/Builds/MacOSX/build"

    一旦builフォルダごと削除して、再度Build Cleanをすると上手く行きました。


    ** CLEAN SUCCEEDED **

    カテゴリー
    C++ JUCE

    JUCEでフォントを使う

    JUCEでフォントを使うには

    // フォントファイルをプロジェクトに読み込み
    // juce::FontOptions オブジェクトを定義
    juce::FontOptions mainFont = juce::FontOptions(juce::Typeface::createSystemTypefaceFor(BinaryData::RobotoCondensedVariableFont_wght_ttf, BinaryData::RobotoCondensedVariableFont_wght_ttfSize));
    
    // 使用する場合はjuce::FontオブジェクトにmainFontをアサイン
    juce::Font font = mainFont;
    font.setHeight(height);
    label.setFont(font);
    カテゴリー
    C++ JUCE

    JUCEでSimplePianoVisualizerを作る

    MIDI Pianoの入力や演奏の様子を鍵盤上で「見る」だけの手頃でシンプルなVisualizer Pluginがなかったので、久しぶりにC++の復習も兼ねて作ってみた。

    【C++の復習】juce::MidiKeyboardStateはPluginProcessor.hでインスタンス化し、その他のComponentではその参照を利用する。

    // PluginProcessor.h
    
    class SimplePianoVisualizerAudioProcessor  : public juce::AudioProcessor
    
    {
    public:
    
    // 省略
      
      juce::MidiKeyboardState& getKeyboardState() {
        return keyboardState;
      } //インスタンスへの参照をゲットするpublic関数
    
    private:
        //==============================================================================
      juce::MidiKeyboardState keyboardState; // ここでインスタンス化
      JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimplePianoVisualizerAudioProcessor)
    };
    // PluginEditor.cpp
    
    SimplePianoVisualizerAudioProcessorEditor::SimplePianoVisualizerAudioProcessorEditor (SimplePianoVisualizerAudioProcessor& p)
        : AudioProcessorEditor (&p), audioProcessor (p), keyboardState(p.getKeyboardState())
    {
        setSize (400, 300);
    }

    【C++の復習】keyboardComponentはPluginEditor.hでインスタンス化し、initialization listでイニシャライズする。

    //PluginEditor.h
    
    class SimplePianoVisualizerAudioProcessorEditor  :  public juce::AudioProcessorEditor
    
    {
    public:
        SimplePianoVisualizerAudioProcessorEditor (SimplePianoVisualizerAudioProcessor&);
        ~SimplePianoVisualizerAudioProcessorEditor() override;
    
        //==============================================================================
        void paint (juce::Graphics&) override;
        void resized() override;
    
    private:
      SimplePianoVisualizerAudioProcessor& audioProcessor;
      juce::MidiKeyboardState& keyboardState;
      juce::MidiKeyboardComponent keyboardComponent;
    
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimplePianoVisualizerAudioProcessorEditor)
    };
    
    //PluginEditor.cpp
    
    SimplePianoVisualizerAudioProcessorEditor::SimplePianoVisualizerAudioProcessorEditor (SimplePianoVisualizerAudioProcessor& p)
        : AudioProcessorEditor (&p), audioProcessor (p), keyboardState(p.getKeyboardState()), keyboardComponent(p.getKeyboardState(), juce::KeyboardComponentBase::horizontalKeyboard)
    {
    
      setSize (400, 300);
    }

    【JUCEの復習】PluginEditor.cppのコンストラクタとresized()に記述し、画面上に表示する。

    // Constructor
    {
      addAndMakeVisible(keyboardComponent);
    
      setSize (400, 100);
      setResizable(true, false);
    }
    
    // resized()
    void SimplePianoVisualizerAudioProcessorEditor::resized()
    {
    
      auto area = getLocalBounds();
      keyboardComponent.setBounds(area);
    }

    これを実行すると、以下のように表示されます。縦・横を自由に表示されるようにするには、Sliderなどでサイズを可変にすると良いです。

    void SimplePianoVisualizerAudioProcessorEditor::sliderValueChanged(juce::Slider *slider)
    {
      horizontalSliderValue = horizontalSlider.getValue();
      verticalSliderValue = verticalSlider.getValue();
      // making sure the resized() (UI changes) are made on the UI thread
      (new SliderValueChangedCallback(this))->post();
    }
    
    void SimplePianoVisualizerAudioProcessorEditor::resized()
    {
      auto area = getLocalBounds();
    
      auto verticalMargin = area.getHeight()/2.8;
      auto keyboardArea = area.removeFromBottom(verticalMargin).reduced(30, 0);
      keyWidth = 32 * (horizontalSliderValue/10);
      keyboardComponent.setKeyWidth(keyWidth);
      keyboardComponent.setBounds(keyboardArea.getX(), keyboardArea.getY(), keyboardArea.getWidth(), keyboardArea.getHeight()*(verticalSliderValue/10));
    
    }

    PluginProcessorのprocessBlockで下のように処理をすると、キーボードを弾くとスクリーン上のキーボードが反応するようになります。

    void SimplePianoVisualizerAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
    {
     
      for(const auto m: midiMessages) {
        keyboardState.processNextMidiEvent(m.getMessage());
      }
    }
    カテゴリー
    C++ JUCE

    JUCE ComboBoxの挙動

    これは僕が知らなかったのでメモしておきます。ComboBoxはcomboBox.addItem(“string”, counter);でアイテムを追加出来ますが、このcounterはアイテムが追加される順番だと思っていましたが、intであればなんでもいいようです。

    つまり、何かのデータのindexなどをこれにあてることで、comboBox.getSelectedId()で値をゲットできるので、とても便利です。

    カテゴリー
    C++ JUCE

    Custom ComponentにTooltipをセットする

    カスタムComponentにTooltipをセットしたい場合は、public juce::SettableTooltipClientを継承する必要があります。

    class CustomComponent : public juce::Component, public juce::SettableTooltipClient
    {
    
    };

    カテゴリー
    C++ JUCE

    JUCE Forwarding MouseEvent

    Child ComponentがParent Componentのエリアを全て覆ってしまっている場合、Parent ComponentのMouseEventが実行されませんが、以下のようにoverrideすると実行されるようになります。

    class ForwardingLabel  : public juce::Label
    {
    public:
      ForwardingLabel()
      {
    
      }
      
      void mouseDown(const juce::MouseEvent &event) override {
        if(auto parent = getParentComponent()) {
          parent->mouseDown(event);
        }
      }
      
      void mouseExit(const juce::MouseEvent &event) override {
        if(auto parent = getParentComponent()) {
          parent->mouseExit(event);
        }
      }
    
    private:
      JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ForwardingLabel)
    };
    カテゴリー
    C++ JUCE

    JUCE APVTS Parameter

    JUCEのAudioProcessorValueTreeStateのパラメターを使う場合、以下のようなシナリオがあると思います。

    1. パラメターをゲットして、値をget/setする場合
    auto param = valueTreeState.getParameterAsValue(ParamId);
    // getting value
    int paramValue = param.getValue();
    // setting value
    param.setValue(juce::var(newValue));

    2. パラメターのリアルタイム値をゲットしたい場合

    int value = (int)*valueTreeState.getRawParameterValue(ParamId);

    Host(DAW)に保存されているパラメターをPlug-In再起動時に読み込む場合などは、getRawParameterValueを使わないといけないようです。

    カテゴリー
    C++ JUCE

    JUCE ThreadID

    JUCEでThreadIDを確認するには以下の方法があります。

    // get threadID
    void* currentThreadId = juce::Thread::getCurrentThreadId();
    DBG("setSelectorId thread ID: " + juce::String(reinterpret_cast<uintptr_t>(currentThreadId)));
    
    // check if the thread is the Message Thread
    bool isMessageThread = juce::MessageManager::getInstance()->isThisTheMessageThread();
    DBG("Is this message thread? " + juce::String(isMessageThread ? "YES" : "NO"));
    
    カテゴリー
    C++ JUCE

    JUCE ComboBoxの操作を区別する

    ComboBoxの操作において、プログラム的に操作をする場合とユーザーが手動で操作をする場合において、異なるcallback処理をしたい場合は、以下のようにCustomComboBoxクラスのmouseDown()をoverrideするといいと思います。

    class CustomComboBox  : public juce::ComboBox
    {
    public:
      CustomComboBox() : juce::ComboBox()
      {
    
      }
    
      ~CustomComboBox() override
      {
      }
    
      void mouseDown(const juce::MouseEvent &event) override {
        DBG("Mouse down");
        userInteracted = true;
        juce::ComboBox::mouseDown(event);
      }
      
      bool getUserInteracted() const {
        return userInteracted;
      }
      
      void setUserInteractedToFalse() {
        userInteracted = false;
      }
      
    private:
      bool userInteracted = false;
      JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomComboBox)
    };
    
    void comboBoxChanged(juce::ComboBox *comboBoxThatHasChanged) override {
      if (comboBoxThatHasChanged == &selector) {
        if(selector.getUserInteracted()) {
          DBG("user selected ...");
          // do something
          selector.setUserInteractedToFalse();
        } else {
          DBG("program selected ...");
          // do something
        }
      }
    }