Noob c++ / JUCE question - display host BPM on plugin


#1

In my processBlock (PluginProcessor.cpp) I have this:

playHead = this->getPlayHead();
playHead->getCurrentPosition (currentPositionInfo);

And then in PluginProcessor.h I have this under public:

AudioPlayHead* playHead;
AudioPlayHead::CurrentPositionInfo currentPositionInfo;
float myBPM = currentPositionInfo.bpm;

So my question is, how do I display the value of myBPM on the screen? I’ve tried editing the “Hello World!” portion of the code in the PluginEditor.cpp file, but it doesn’t link to the value of myBPM…

void GetBpmAudioProcessorEditor::paint (Graphics& g)
{
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));

g.setColour (Colours::white);
g.setFont (15.0f);
g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1);

}

How do I just replace the “Hello World!” with the value of myBPM? Replacing “Hello World!” with myBPM gives an undeclared identifier error


#2

You need to do a bit more C++ learning I think. myBPM there is a local variable. You can’t access that from outside the function.

You want a member variable in your audio processor class. Probably a public member variable.

http://www.cplusplus.com/doc/tutorial/classes/

(You don’t want a global variable - whilst it may kind of work for this case you’ll run into trouble).

I’d do a day going through some C++ tutorials on classes and variables before going much further if I was you!


#3

First of all, to make your code more readable, please put a line of ``` above and below your block of code, so it is formatted properly.

Regarding your question, there are two things you should be aware of.
First one is multithreading. Your computer does a lot of stuff in parallel, so your paint function might be called by one thread while your processBlock will get called by another thread so that GUI and audio can run in parallel. So to sum it up, the worst case you should expect is that those two pieces of code might get executed in parallel and that both functions are called multiple times. The GUI related code will get called whenever something happens with your GUI (opening, closing the editor, moving it around, resizing it, moving another window above it…) or when you tell your editor to repaint your GUI. Process block will get called in a relatively regular interval. As mentioned here in the forum a lot of times you should never do anything in processBlock that waits for other events as this might interrupt your audio.
The second thing is the scope of an object in C++. You have
float myBPM = currentPositionInfo.bpm;
in your processBlock function. What you do is declaring the variable myBPM in that place, which means that it is created in that line and will be automatically deleted (go out of scope) at the end of your function. A scope is the space between a { and a }. You should have a read on this important C++ topic. That being said, it’s obvious that the compiler throws an error when you try to access this variable in a totally different place.

Now what you want is myBPM to be a member variable of your processor. So go over to the PluginProcessor.h file and add a public member variable float myBPM = -1.0;just below all member functions defined there and above the private: line. In the processBlock function body you now add something like

if (currentPositionInfo.bpm != myBPM)
    myBPM = currentPositionInfo.bpm;

Now you assign new value to the member variable myBPM when there is a new value. myBPM exists as long as your processor exists, in this case this means as long as your plugin is loaded.

Now to access this value from your editor, you can call processor.myBPM from within every pluginEditor function. This way you could now write something like

void GetBpmAudioProcessorEditor::paint (Graphics& g)
{
    g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));

    g.setColour (Colours::white);
    g.setFont (15.0f);
    float lastBPM = processor.myBPM;
    g.drawFittedText ("BPM is " + ((lastBPM == -1.0) ? "unknown" : String (lastBPM)), getLocalBounds(), Justification::centred, 1);
}

Let’s see what happens here.

  • A copy of the value currently assigned to processor.myBPM is made
  • (lastBPM == -1.0) evaluates to true if processor.myBPM still has the default value -1.0. This means processBlock has not been called yet
  • If it evaluates to true, you will see BPM is unknown in your editor window
  • If processBlock already assigned a value to myBPM, it will print something like BPM is 130

So as I said, you don’t really know in the first place, when the paint routine will be called. So in this setup, it could happen that it gets painted before the first call to processBlock was made. What you now want is your editor to check for updates on this value on a regular basis. For this, you will need to make your lastBPM a private member variable of your editor instead of declaring it locally in paint (similar to what was done in the processor) and then let the editor inherit from Timer. This means your class declaration in PluginEditor.h should look something like

class GetBpmAudioProcessorEditor  : public AudioProcessorEditor, public Timer
{
public:
    ...

    void timerCallback() override;

private:
    float lastBPM = -1.0;
    ...
}

Then add

void GetBpmAudioProcessorEditor::timerCallback()
{
   if (processor.myBPM != lastBPM)

    {
        lastBPM = processor.myBPM;
        repaint();
    }
}

to pluginEditor.cpp and add the line startTimer (1000) to your editors constructor so that the timer callback will be called once each second to check if there was an update of the BPM value. Of course you can chose another interval of choice.

All this code is untested and written on the go. It should demonstrate some concepts to help you looking at the right places. And I totally agree with jimc’s comment!

Hope that helps you!


#4

Thanks so much! I’m still trying to wrap my brain around it, but I get the general idea here.