I have code that uses g.strokePath() and it works, until I change the data. Redrawing it has no effect (perhaps it’s drawing the old path, or perhaps the background isn’t being cleared.)
Here’s the code:
std::vector<float> waveformValues;
waveformValues.clear();
juce::Path waveformPath;
waveformPath.clear();
waveformPath.startNewSubPath(15, waveformHeight + 60);
auto buffer = audioProcessor.getIR();
const float waveformResolution = 1024.0f;
const int ratio =
static_cast<int>(buffer.getNumSamples() / waveformResolution);
auto bufferPointer = buffer.getReadPointer(0);
for (int sample = 0; sample < buffer.getNumSamples(); sample += ratio) {
waveformValues.push_back(juce::Decibels::gainToDecibels<float>(
std::fabsf(bufferPointer[sample]), -72.0f));
}
for (int xPos = 0; xPos < waveformValues.size(); ++xPos) {
auto yPos = juce::jmap<float>(waveformValues[xPos], -72.0f, 0.0f,
waveformHeight + 60, 75); // FIXME magic numbers
waveformPath.lineTo(15 + xPos / waveformResolution * waveformWidth, yPos);
}
g.strokePath(waveformPath, juce::PathStrokeType(1.0f));
DBG("======== waveform path: " << waveformPath.getLength());
The color is definitely set. It draws the path. But when the IR changes (getIr()), the same path appears. Debugs show that the waveformPath length has changed (but not just grown, so I’m not appending data – it’s always the same for a given IR.)
The first one is drawn at startup; the second one after loading a file via the file chooser. Regardless, the paint() routine gets called, the debugs show what I expect, but the graph doesn’t change. Other changes do appear, like a label.setText() just above this code.
No doubt I’m missing something. Any clues?
If buffer.getNumSamples() < 1024, ratio == 0, so the loop never advances. Try debug and see if that’s the case?
Perhaps you shall make the path as a class member and ONLY update it if the IR has changed (of course you also need to add a method to check whether the IR has changed). It might also help you find the problem faster.
BTW, I would assume that audioProcessor.getIR() is completely thread-safe, right?
Thanks, but nope. Whatever IR file I use, it gets properly displayed the first time (e.g., loaded when restoring last setup.) It’s when I load a new file the display doesn’t change despite the same code running.
Regardless, that’s a good tip to look into, could happen for some future file.
I only redraw if the IR has changed. The IR is a class member. So, I don’t see why storing the path as a class member would help. I’ll try it in case I’m missing something.
Regarding thread-safe: good point, I’ll check. But (as a retired real-time/embedded-systems programmer of 45 years) I’m confident that’s not the issue in this case. I won’t go into all the details on why I’m confident, but the main one is that the IR is being updated correctly, which I can tell by listening plus stats like length.
BTW, redrawing was working earlier, so it’s a change in the context where the code changing the IR runs rather than the code per-se. At least, I think.
Earlier the code to change the IR was in the editor, but I had to move it to the plugin processor so it could load a new IR in setStateInformation(). Important detail I omitted! So, might be a thread-safe issue, but not regarding the data I’m handling but something higher up in the food chain.
When the code works, it’s being caused by a call to setStateInformation(), if that helps. That is, there’s no obvious difference I can see in context from working and non-working, other than, well, the framework calling setStateInformation initially vs from responding to the file chooser.
I only redraw if the IR has changed.
Got it. That’s great
No problem. Something like setBufferedToImage is totally fine & great for performance.
I don’t see why storing the path as a class member would help.
Well, it may not help the current issue, but it could avoid memory allocation when you re-draw the path each time.
1 Like
I haven’t looked at the code, but I’d assume that clear() and adding a new set of points would churn the majority of the memory. But if there’s significant overhead, sure.
Thanks for pointing out setBufferedToImage, which looks like it’d be useful. Currently it redraws whenever the mouse enters or leaves the frame, and no doubt lots of other times. When I said I only “redraw” when the IR changes, I meant that in paint(), I skip the code in the OP.
I switched to using AudioThumbnail instead of drawing and get the same results, so it’s not about strokePath().
It seems that it depends on whether the IR is set from the initial call to setState() to load the previous file or from the file chooser button’s onClick(). The latter doesn’t repaint the waveform, even though the code in paint() is being called.
Doh! Just a missing repaint() call from the file chooser button’s onClick(). Thanks for the help!
I have to admit that I’m surprised that the paint() routine was called despite no repaint(), and that the results were different. Since paint() was called, I didn’t think about a missing repaint(). Live and learn!