Browse Source

Fixed a very very subtle component bug. Added peak level detection to AudioThumbnail. Tidied up some old demo plugin UI code.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
098abe4a2f
11 changed files with 201 additions and 134 deletions
  1. +40
    -35
      extras/audio plugins/demo/Source/PluginEditor.cpp
  2. +5
    -5
      extras/audio plugins/demo/Source/PluginEditor.h
  3. +61
    -37
      juce_amalgamated.cpp
  4. +17
    -10
      juce_amalgamated.h
  5. +40
    -0
      src/audio/audio_file_formats/juce_AudioThumbnail.cpp
  6. +6
    -0
      src/audio/audio_file_formats/juce_AudioThumbnail.h
  7. +6
    -6
      src/audio/processors/juce_AudioProcessorGraph.cpp
  8. +1
    -1
      src/core/juce_StandardHeader.h
  9. +8
    -7
      src/events/juce_Timer.h
  10. +15
    -31
      src/gui/components/juce_Component.cpp
  11. +2
    -2
      src/gui/components/juce_Component.h

+ 40
- 35
extras/audio plugins/demo/Source/PluginEditor.cpp View File

@@ -13,32 +13,38 @@
//==============================================================================
JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemoPluginAudioProcessor* ownerFilter)
: AudioProcessorEditor (ownerFilter)
: AudioProcessorEditor (ownerFilter),
midiKeyboard (ownerFilter->keyboardState, MidiKeyboardComponent::horizontalKeyboard),
infoLabel (String::empty),
gainLabel ("", "Throughput level:"),
delayLabel ("", "Delay:"),
gainSlider ("gain"),
delaySlider ("delay")
{
addAndMakeVisible (gainSlider = new Slider ("gain"));
gainSlider->setSliderStyle (Slider::Rotary);
gainSlider->addListener (this);
gainSlider->setRange (0.0, 1.0, 0.01);
Label* l = new Label ("", "Throughput level:");
l->attachToComponent (gainSlider, false);
l->setFont (Font (11.0f));
addAndMakeVisible (delaySlider = new Slider ("delay"));
delaySlider->setSliderStyle (Slider::Rotary);
delaySlider->addListener (this);
delaySlider->setRange (0.0, 1.0, 0.01);
l = new Label ("", "Delay:");
l->attachToComponent (delaySlider, false);
l->setFont (Font (11.0f));
// create and add the midi keyboard component..
addAndMakeVisible (midiKeyboard
= new MidiKeyboardComponent (ownerFilter->keyboardState,
MidiKeyboardComponent::horizontalKeyboard));
// add some sliders..
addAndMakeVisible (&gainSlider);
gainSlider.setSliderStyle (Slider::Rotary);
gainSlider.addListener (this);
gainSlider.setRange (0.0, 1.0, 0.01);
addAndMakeVisible (&delaySlider);
delaySlider.setSliderStyle (Slider::Rotary);
delaySlider.addListener (this);
delaySlider.setRange (0.0, 1.0, 0.01);
// add some labels for the sliders..
gainLabel.attachToComponent (&gainSlider, false);
gainLabel.setFont (Font (11.0f));
delayLabel.attachToComponent (&delaySlider, false);
delayLabel.setFont (Font (11.0f));
// add the midi keyboard component..
addAndMakeVisible (&midiKeyboard);
// add a label that will display the current timecode and status..
addAndMakeVisible (infoLabel = new Label (String::empty, String::empty));
infoLabel->setColour (Label::textColourId, Colours::blue);
addAndMakeVisible (&infoLabel);
infoLabel.setColour (Label::textColourId, Colours::blue);
// add the triangular resizer component for the bottom-right of the UI
addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits));
@@ -53,7 +59,6 @@ JuceDemoPluginAudioProcessorEditor::JuceDemoPluginAudioProcessorEditor (JuceDemo
JuceDemoPluginAudioProcessorEditor::~JuceDemoPluginAudioProcessorEditor()
{
deleteAllChildren();
}
//==============================================================================
@@ -65,12 +70,12 @@ void JuceDemoPluginAudioProcessorEditor::paint (Graphics& g)
void JuceDemoPluginAudioProcessorEditor::resized()
{
infoLabel->setBounds (10, 4, 400, 25);
gainSlider->setBounds (20, 60, 150, 40);
delaySlider->setBounds (200, 60, 150, 40);
infoLabel.setBounds (10, 4, 400, 25);
gainSlider.setBounds (20, 60, 150, 40);
delaySlider.setBounds (200, 60, 150, 40);
const int keyboardHeight = 70;
midiKeyboard->setBounds (4, getHeight() - keyboardHeight - 4, getWidth() - 8, keyboardHeight);
midiKeyboard.setBounds (4, getHeight() - keyboardHeight - 4, getWidth() - 8, keyboardHeight);
resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16);
@@ -89,25 +94,25 @@ void JuceDemoPluginAudioProcessorEditor::timerCallback()
if (lastDisplayedPosition != newPos)
displayPositionInfo (newPos);
gainSlider->setValue (ourProcessor->gain, false);
delaySlider->setValue (ourProcessor->delay, false);
gainSlider.setValue (ourProcessor->gain, false);
delaySlider.setValue (ourProcessor->delay, false);
}
// This is our Slider::Listener callback, when the user drags a slider.
void JuceDemoPluginAudioProcessorEditor::sliderValueChanged (Slider* slider)
{
if (slider == gainSlider)
if (slider == &gainSlider)
{
// It's vital to use setParameterNotifyingHost to change any parameters that are automatable
// by the host, rather than just modifying them directly, otherwise the host won't know
// that they've changed.
getProcessor()->setParameterNotifyingHost (JuceDemoPluginAudioProcessor::gainParam,
(float) gainSlider->getValue());
(float) gainSlider.getValue());
}
else if (slider == delaySlider)
else if (slider == &delaySlider)
{
getProcessor()->setParameterNotifyingHost (JuceDemoPluginAudioProcessor::delayParam,
(float) delaySlider->getValue());
(float) delaySlider.getValue());
}
}
@@ -169,5 +174,5 @@ void JuceDemoPluginAudioProcessorEditor::displayPositionInfo (const AudioPlayHea
else if (pos.isPlaying)
displayText << " (playing)";
infoLabel->setText (displayText, false);
infoLabel.setText (displayText, false);
}

+ 5
- 5
extras/audio plugins/demo/Source/PluginEditor.h View File

@@ -34,11 +34,11 @@ public:
void sliderValueChanged (Slider*);
private:
MidiKeyboardComponent* midiKeyboard;
Label* infoLabel;
Slider* gainSlider;
Slider* delaySlider;
ResizableCornerComponent* resizer;
MidiKeyboardComponent midiKeyboard;
Label infoLabel, gainLabel, delayLabel;
Slider gainSlider;
Slider delaySlider;
ScopedPointer<ResizableCornerComponent> resizer;
ComponentBoundsConstrainer resizeLimits;
AudioPlayHead::CurrentPositionInfo lastDisplayedPosition;


+ 61
- 37
juce_amalgamated.cpp View File

@@ -21988,6 +21988,12 @@ struct AudioThumbnail::MinMaxValue
return maxValue > minValue;
}

inline int getPeak() const throw()
{
return jmax (std::abs ((int) minValue),
std::abs ((int) maxValue));
}

inline void read (InputStream& input)
{
minValue = input.readByte();
@@ -22186,6 +22192,7 @@ class AudioThumbnail::ThumbData
{
public:
ThumbData (const int numThumbSamples)
: peakLevel (-1)
{
ensureSize (numThumbSamples);
}
@@ -22232,6 +22239,8 @@ public:

void write (const MinMaxValue* const source, const int startIndex, const int numValues)
{
resetPeak();

if (startIndex + numValues > data.size())
ensureSize (startIndex + numValues);

@@ -22241,8 +22250,29 @@ public:
dest[i] = source[i];
}

void resetPeak()
{
peakLevel = -1;
}

int getPeak()
{
if (peakLevel < 0)
{
for (int i = 0; i < data.size(); ++i)
{
const int peak = data[i].getPeak();
if (peak > peakLevel)
peakLevel = peak;
}
}

return peakLevel;
}

private:
Array <MinMaxValue> data;
int peakLevel;

void ensureSize (const int thumbSamples)
{
@@ -22619,6 +22649,16 @@ bool AudioThumbnail::isFullyLoaded() const throw()
return numSamplesFinished >= totalSamples - samplesPerThumbSample;
}

float AudioThumbnail::getApproximatePeak() const
{
int peak = 0;

for (int i = channels.size(); --i >= 0;)
peak = jmax (peak, channels.getUnchecked(i)->getPeak());

return jlimit (0, 127, peak) / 127.0f;
}

void AudioThumbnail::drawChannel (Graphics& g, const Rectangle<int>& area, double startTime,
double endTime, int channelNum, float verticalZoomFactor)
{
@@ -36682,10 +36722,10 @@ public:
: graph (graph_),
orderedNodes (orderedNodes_)
{
nodeIds.add (zeroNodeID); // first buffer is read-only zeros
nodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros
channels.add (0);

midiNodeIds.add (zeroNodeID);
midiNodeIds.add ((uint32) zeroNodeID);

for (int i = 0; i < orderedNodes.size(); ++i)
{
@@ -36969,7 +37009,7 @@ private:
if (midiNodeIds.getUnchecked(i) == freeNodeID)
return i;

midiNodeIds.add (freeNodeID);
midiNodeIds.add ((uint32) freeNodeID);
return midiNodeIds.size() - 1;
}
else
@@ -36978,7 +37018,7 @@ private:
if (nodeIds.getUnchecked(i) == freeNodeID)
return i;

nodeIds.add (freeNodeID);
nodeIds.add ((uint32) freeNodeID);
channels.add (0);
return nodeIds.size() - 1;
}
@@ -37018,7 +37058,7 @@ private:
nodeIds.getUnchecked(i),
channels.getUnchecked(i)))
{
nodeIds.set (i, freeNodeID);
nodeIds.set (i, (uint32) freeNodeID);
}
}

@@ -37029,7 +37069,7 @@ private:
midiNodeIds.getUnchecked(i),
AudioProcessorGraph::midiChannelIndex))
{
midiNodeIds.set (i, freeNodeID);
midiNodeIds.set (i, (uint32) freeNodeID);
}
}
}
@@ -40198,8 +40238,7 @@ void Component::setVisible (bool shouldBeVisible)

if (! shouldBeVisible)
{
if (currentlyFocusedComponent == this
|| isParentOf (currentlyFocusedComponent))
if (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent))
{
if (parentComponent_ != 0)
parentComponent_->grabKeyboardFocus();
@@ -41074,32 +41113,18 @@ Component* Component::removeChildComponent (const int index)
childComponentList_.remove (index);
child->parentComponent_ = 0;

if (childShowing)
// (NB: there are obscure situations where a childShowing = false, but it still has the focus)
if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent))
{
JUCE_TRY
{
if ((currentlyFocusedComponent == child)
|| child->isParentOf (currentlyFocusedComponent))
{
// get rid first to force the grabKeyboardFocus to change to us.
giveAwayFocus();
grabKeyboardFocus();
}
}
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
catch (const std::exception& e)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
}
catch (...)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
}
#endif
SafePointer<Component> thisPointer (this);

giveAwayFocus();

if (thisPointer == 0)
return child;

if (childShowing)
grabKeyboardFocus();
}

child->internalHierarchyChanged();
@@ -42545,14 +42570,13 @@ Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() throw()

void Component::giveAwayFocus()
{
// use a copy so we can clear the value before the call
SafePointer<Component> componentLosingFocus (currentlyFocusedComponent);

Component* const componentLosingFocus = currentlyFocusedComponent;
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();

if (componentLosingFocus != 0)
componentLosingFocus->internalFocusLoss (focusChangedDirectly);

Desktop::getInstance().triggerFocusCallback();
}

bool Component::isMouseOver (const bool includeChildren) const


+ 17
- 10
juce_amalgamated.h View File

@@ -64,7 +64,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 105
#define JUCE_BUILDNUMBER 106

/** Current Juce version number.

@@ -28178,9 +28178,9 @@ private:
bool isDisabledFlag : 1;
bool childCompFocusedFlag : 1;
bool dontClipGraphicsFlag : 1;
#if JUCE_DEBUG
#if JUCE_DEBUG
bool isInsidePaintCall : 1;
#endif
#endif
};

union
@@ -29126,18 +29126,19 @@ private:
class InternalTimerThread;

/**
Repeatedly calls a user-defined method at a specified time interval.
Makes repeated callbacks to a virtual method at a specified time interval.

A Timer's timerCallback() method will be repeatedly called at a given
interval. Initially when a Timer object is created, they will do nothing
until the startTimer() method is called, then the message thread will
start calling it back until stopTimer() is called.
interval. When you create a Timer object, it will do nothing until the
startTimer() method is called, which will cause the message thread to
start making callbacks at the specified interval, until stopTimer() is called
or the object is deleted.

The time interval isn't guaranteed to be precise to any more than maybe
10-20ms, and the intervals may end up being much longer than requested if the
system is busy. Because it's the message thread that is doing the callbacks,
any messages that take a significant amount of time to process will block
all the timers for that period.
system is busy. Because the callbacks are made by the main message thread,
anything that blocks the message queue for a period of time will also prevent
any timers from running until it can carry on.

If you need to have a single callback that is shared by multiple timers with
different frequencies, then the MultiTimer class allows you to do that - its
@@ -32741,6 +32742,12 @@ public:
/** Returns true if the low res preview is fully generated. */
bool isFullyLoaded() const throw();

/** Returns the highest level in the thumbnail.
Note that because the thumb only stores low-resolution data, this isn't
an accurate representation of the highest value, it's only a rough approximation.
*/
float getApproximatePeak() const;

/** Returns the hash code that was set by setSource() or setReader(). */
int64 getHashCode() const;



+ 40
- 0
src/audio/audio_file_formats/juce_AudioThumbnail.cpp View File

@@ -62,6 +62,12 @@ struct AudioThumbnail::MinMaxValue
return maxValue > minValue;
}
inline int getPeak() const throw()
{
return jmax (std::abs ((int) minValue),
std::abs ((int) maxValue));
}
inline void read (InputStream& input)
{
minValue = input.readByte();
@@ -262,6 +268,7 @@ class AudioThumbnail::ThumbData
{
public:
ThumbData (const int numThumbSamples)
: peakLevel (-1)
{
ensureSize (numThumbSamples);
}
@@ -308,6 +315,8 @@ public:
void write (const MinMaxValue* const source, const int startIndex, const int numValues)
{
resetPeak();
if (startIndex + numValues > data.size())
ensureSize (startIndex + numValues);
@@ -317,8 +326,29 @@ public:
dest[i] = source[i];
}
void resetPeak()
{
peakLevel = -1;
}
int getPeak()
{
if (peakLevel < 0)
{
for (int i = 0; i < data.size(); ++i)
{
const int peak = data[i].getPeak();
if (peak > peakLevel)
peakLevel = peak;
}
}
return peakLevel;
}
private:
Array <MinMaxValue> data;
int peakLevel;
void ensureSize (const int thumbSamples)
{
@@ -700,6 +730,16 @@ bool AudioThumbnail::isFullyLoaded() const throw()
return numSamplesFinished >= totalSamples - samplesPerThumbSample;
}
float AudioThumbnail::getApproximatePeak() const
{
int peak = 0;
for (int i = channels.size(); --i >= 0;)
peak = jmax (peak, channels.getUnchecked(i)->getPeak());
return jlimit (0, 127, peak) / 127.0f;
}
void AudioThumbnail::drawChannel (Graphics& g, const Rectangle<int>& area, double startTime,
double endTime, int channelNum, float verticalZoomFactor)
{


+ 6
- 0
src/audio/audio_file_formats/juce_AudioThumbnail.h View File

@@ -177,6 +177,12 @@ public:
/** Returns true if the low res preview is fully generated. */
bool isFullyLoaded() const throw();
/** Returns the highest level in the thumbnail.
Note that because the thumb only stores low-resolution data, this isn't
an accurate representation of the highest value, it's only a rough approximation.
*/
float getApproximatePeak() const;
/** Returns the hash code that was set by setSource() or setReader(). */
int64 getHashCode() const;


+ 6
- 6
src/audio/processors/juce_AudioProcessorGraph.cpp View File

@@ -546,10 +546,10 @@ public:
: graph (graph_),
orderedNodes (orderedNodes_)
{
nodeIds.add (zeroNodeID); // first buffer is read-only zeros
nodeIds.add ((uint32) zeroNodeID); // first buffer is read-only zeros
channels.add (0);
midiNodeIds.add (zeroNodeID);
midiNodeIds.add ((uint32) zeroNodeID);
for (int i = 0; i < orderedNodes.size(); ++i)
{
@@ -835,7 +835,7 @@ private:
if (midiNodeIds.getUnchecked(i) == freeNodeID)
return i;
midiNodeIds.add (freeNodeID);
midiNodeIds.add ((uint32) freeNodeID);
return midiNodeIds.size() - 1;
}
else
@@ -844,7 +844,7 @@ private:
if (nodeIds.getUnchecked(i) == freeNodeID)
return i;
nodeIds.add (freeNodeID);
nodeIds.add ((uint32) freeNodeID);
channels.add (0);
return nodeIds.size() - 1;
}
@@ -884,7 +884,7 @@ private:
nodeIds.getUnchecked(i),
channels.getUnchecked(i)))
{
nodeIds.set (i, freeNodeID);
nodeIds.set (i, (uint32) freeNodeID);
}
}
@@ -895,7 +895,7 @@ private:
midiNodeIds.getUnchecked(i),
AudioProcessorGraph::midiChannelIndex))
{
midiNodeIds.set (i, freeNodeID);
midiNodeIds.set (i, (uint32) freeNodeID);
}
}
}


+ 1
- 1
src/core/juce_StandardHeader.h View File

@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 52
#define JUCE_BUILDNUMBER 105
#define JUCE_BUILDNUMBER 106
/** Current Juce version number.


+ 8
- 7
src/events/juce_Timer.h View File

@@ -31,18 +31,19 @@ class InternalTimerThread;
//==============================================================================
/**
Repeatedly calls a user-defined method at a specified time interval.
Makes repeated callbacks to a virtual method at a specified time interval.
A Timer's timerCallback() method will be repeatedly called at a given
interval. Initially when a Timer object is created, they will do nothing
until the startTimer() method is called, then the message thread will
start calling it back until stopTimer() is called.
interval. When you create a Timer object, it will do nothing until the
startTimer() method is called, which will cause the message thread to
start making callbacks at the specified interval, until stopTimer() is called
or the object is deleted.
The time interval isn't guaranteed to be precise to any more than maybe
10-20ms, and the intervals may end up being much longer than requested if the
system is busy. Because it's the message thread that is doing the callbacks,
any messages that take a significant amount of time to process will block
all the timers for that period.
system is busy. Because the callbacks are made by the main message thread,
anything that blocks the message queue for a period of time will also prevent
any timers from running until it can carry on.
If you need to have a single callback that is shared by multiple timers with
different frequencies, then the MultiTimer class allows you to do that - its


+ 15
- 31
src/gui/components/juce_Component.cpp View File

@@ -470,8 +470,7 @@ void Component::setVisible (bool shouldBeVisible)
if (! shouldBeVisible)
{
if (currentlyFocusedComponent == this
|| isParentOf (currentlyFocusedComponent))
if (currentlyFocusedComponent == this || isParentOf (currentlyFocusedComponent))
{
if (parentComponent_ != 0)
parentComponent_->grabKeyboardFocus();
@@ -1358,32 +1357,18 @@ Component* Component::removeChildComponent (const int index)
childComponentList_.remove (index);
child->parentComponent_ = 0;
if (childShowing)
// (NB: there are obscure situations where a childShowing = false, but it still has the focus)
if (currentlyFocusedComponent == child || child->isParentOf (currentlyFocusedComponent))
{
JUCE_TRY
{
if ((currentlyFocusedComponent == child)
|| child->isParentOf (currentlyFocusedComponent))
{
// get rid first to force the grabKeyboardFocus to change to us.
giveAwayFocus();
grabKeyboardFocus();
}
}
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS
catch (const std::exception& e)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__);
}
catch (...)
{
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
JUCEApplication::sendUnhandledException (0, __FILE__, __LINE__);
}
#endif
SafePointer<Component> thisPointer (this);
giveAwayFocus();
if (thisPointer == 0)
return child;
if (childShowing)
grabKeyboardFocus();
}
child->internalHierarchyChanged();
@@ -2854,14 +2839,13 @@ Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() throw()
void Component::giveAwayFocus()
{
// use a copy so we can clear the value before the call
SafePointer<Component> componentLosingFocus (currentlyFocusedComponent);
Component* const componentLosingFocus = currentlyFocusedComponent;
currentlyFocusedComponent = 0;
Desktop::getInstance().triggerFocusCallback();
if (componentLosingFocus != 0)
componentLosingFocus->internalFocusLoss (focusChangedDirectly);
Desktop::getInstance().triggerFocusCallback();
}
//==============================================================================


+ 2
- 2
src/gui/components/juce_Component.h View File

@@ -2171,9 +2171,9 @@ private:
bool isDisabledFlag : 1;
bool childCompFocusedFlag : 1;
bool dontClipGraphicsFlag : 1;
#if JUCE_DEBUG
#if JUCE_DEBUG
bool isInsidePaintCall : 1;
#endif
#endif
};
union


Loading…
Cancel
Save