|
|
@@ -1,194 +1,194 @@ |
|
|
|
/* |
|
|
|
============================================================================== |
|
|
|
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions" |
|
|
|
Copyright 2004-7 by Raw Material Software ltd. |
|
|
|
|
|
|
|
------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the |
|
|
|
GNU General Public License, as published by the Free Software Foundation; |
|
|
|
either version 2 of the License, or (at your option) any later version. |
|
|
|
|
|
|
|
JUCE is distributed in the hope that it will be useful, |
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
GNU General Public License for more details. |
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License |
|
|
|
along with JUCE; if not, visit www.gnu.org/licenses or write to the |
|
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
|
|
|
Boston, MA 02111-1307 USA |
|
|
|
|
|
|
|
------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
If you'd like to release a closed-source product which uses JUCE, commercial |
|
|
|
licenses are also available: visit www.rawmaterialsoftware.com/juce for |
|
|
|
more information. |
|
|
|
|
|
|
|
============================================================================== |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "DemoEditorComponent.h" |
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
// quick-and-dirty function to format a timecode string |
|
|
|
static const String timeToTimecodeString (const double seconds) |
|
|
|
{ |
|
|
|
const double absSecs = fabs (seconds); |
|
|
|
const tchar* const sign = (seconds < 0) ? T("-") : T(""); |
|
|
|
|
|
|
|
const int hours = (int) (absSecs / (60.0 * 60.0)); |
|
|
|
const int mins = ((int) (absSecs / 60.0)) % 60; |
|
|
|
const int secs = ((int) absSecs) % 60; |
|
|
|
|
|
|
|
return String::formatted (T("%s%02d:%02d:%02d:%03d"), |
|
|
|
sign, hours, mins, secs, |
|
|
|
roundDoubleToInt (absSecs * 1000) % 1000); |
|
|
|
} |
|
|
|
|
|
|
|
// quick-and-dirty function to format a bars/beats string |
|
|
|
static const String ppqToBarsBeatsString (const double ppq, |
|
|
|
const double lastBarPPQ, |
|
|
|
const int numerator, |
|
|
|
const int denominator) |
|
|
|
{ |
|
|
|
if (numerator == 0 || denominator == 0) |
|
|
|
return T("1|1|0"); |
|
|
|
|
|
|
|
const int ppqPerBar = (numerator * 4 / denominator); |
|
|
|
const double beats = (fmod (ppq, ppqPerBar) / ppqPerBar) * numerator; |
|
|
|
|
|
|
|
const int bar = ((int) ppq) / ppqPerBar + 1; |
|
|
|
const int beat = ((int) beats) + 1; |
|
|
|
const int ticks = ((int) (fmod (beats, 1.0) * 960.0)); |
|
|
|
|
|
|
|
String s; |
|
|
|
s << bar << T('|') << beat << T('|') << ticks; |
|
|
|
return s; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
DemoEditorComponent::DemoEditorComponent (DemoJuceFilter* const ownerFilter) |
|
|
|
: AudioFilterEditor (ownerFilter) |
|
|
|
{ |
|
|
|
// create our gain slider.. |
|
|
|
addAndMakeVisible (gainSlider = new Slider (T("gain"))); |
|
|
|
gainSlider->addListener (this); |
|
|
|
gainSlider->setRange (0.0, 1.0, 0.01); |
|
|
|
gainSlider->setTooltip (T("changes the volume of the audio that runs through the plugin..")); |
|
|
|
|
|
|
|
// get the gain parameter from the filter and use it to set up our slider |
|
|
|
gainSlider->setValue (ownerFilter->getParameter (0), false); |
|
|
|
|
|
|
|
// create and add the midi keyboard component.. |
|
|
|
addAndMakeVisible (midiKeyboard |
|
|
|
= new MidiKeyboardComponent (ownerFilter->keyboardState, |
|
|
|
MidiKeyboardComponent::horizontalKeyboard)); |
|
|
|
|
|
|
|
// add a label that will display the current timecode and status.. |
|
|
|
addAndMakeVisible (infoLabel = new Label (String::empty, String::empty)); |
|
|
|
|
|
|
|
// add the triangular resizer component for the bottom-right of the UI |
|
|
|
addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits)); |
|
|
|
resizeLimits.setSizeLimits (150, 150, 800, 300); |
|
|
|
|
|
|
|
// set our component's initial size to be the last one that was stored in the filter's settings |
|
|
|
setSize (ownerFilter->lastUIWidth, |
|
|
|
ownerFilter->lastUIHeight); |
|
|
|
|
|
|
|
// register ourselves with the filter - it will use its ChangeBroadcaster base |
|
|
|
// class to tell us when something has changed, and this will call our changeListenerCallback() |
|
|
|
// method. |
|
|
|
ownerFilter->addChangeListener (this); |
|
|
|
} |
|
|
|
|
|
|
|
DemoEditorComponent::~DemoEditorComponent() |
|
|
|
{ |
|
|
|
getFilter()->removeChangeListener (this); |
|
|
|
|
|
|
|
deleteAllChildren(); |
|
|
|
} |
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
void DemoEditorComponent::paint (Graphics& g) |
|
|
|
{ |
|
|
|
// just clear the window |
|
|
|
g.fillAll (Colour::greyLevel (0.9f)); |
|
|
|
} |
|
|
|
|
|
|
|
void DemoEditorComponent::resized() |
|
|
|
{ |
|
|
|
gainSlider->setBounds (10, 10, 200, 22); |
|
|
|
infoLabel->setBounds (10, 35, 450, 20); |
|
|
|
|
|
|
|
const int keyboardHeight = 70; |
|
|
|
midiKeyboard->setBounds (4, getHeight() - keyboardHeight - 4, |
|
|
|
getWidth() - 8, keyboardHeight); |
|
|
|
|
|
|
|
resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16); |
|
|
|
|
|
|
|
// if we've been resized, tell the filter so that it can store the new size |
|
|
|
// in its settings |
|
|
|
getFilter()->lastUIWidth = getWidth(); |
|
|
|
getFilter()->lastUIHeight = getHeight(); |
|
|
|
} |
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
void DemoEditorComponent::changeListenerCallback (void* source) |
|
|
|
{ |
|
|
|
// this is the filter telling us that it's changed, so we'll update our |
|
|
|
// display of the time, midi message, etc. |
|
|
|
updateParametersFromFilter(); |
|
|
|
} |
|
|
|
|
|
|
|
void DemoEditorComponent::sliderValueChanged (Slider*) |
|
|
|
{ |
|
|
|
getFilter()->setParameterNotifyingHost (0, (float) gainSlider->getValue()); |
|
|
|
} |
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
void DemoEditorComponent::updateParametersFromFilter() |
|
|
|
{ |
|
|
|
DemoJuceFilter* const filter = getFilter(); |
|
|
|
|
|
|
|
// we use this lock to make sure the processBlock() method isn't writing to the |
|
|
|
// lastMidiMessage variable while we're trying to read it, but be extra-careful to |
|
|
|
// only hold the lock for a minimum amount of time.. |
|
|
|
filter->getCallbackLock().enter(); |
|
|
|
|
|
|
|
// take a local copy of the info we need while we've got the lock.. |
|
|
|
const AudioFilterBase::CurrentPositionInfo positionInfo (filter->lastPosInfo); |
|
|
|
const float newGain = filter->getParameter (0); |
|
|
|
|
|
|
|
// ..release the lock ASAP |
|
|
|
filter->getCallbackLock().exit(); |
|
|
|
|
|
|
|
|
|
|
|
// ..and after releasing the lock, we're free to do the time-consuming UI stuff.. |
|
|
|
String infoText; |
|
|
|
infoText << String (positionInfo.bpm, 2) << T(" bpm, ") |
|
|
|
<< positionInfo.timeSigNumerator << T("/") << positionInfo.timeSigDenominator |
|
|
|
<< T(" - ") << timeToTimecodeString (positionInfo.timeInSeconds) |
|
|
|
<< T(" - ") << ppqToBarsBeatsString (positionInfo.ppqPosition, |
|
|
|
positionInfo.ppqPositionOfLastBarStart, |
|
|
|
positionInfo.timeSigNumerator, |
|
|
|
positionInfo.timeSigDenominator); |
|
|
|
|
|
|
|
if (positionInfo.isPlaying) |
|
|
|
infoText << T(" (playing)"); |
|
|
|
|
|
|
|
infoLabel->setText (infoText, false); |
|
|
|
|
|
|
|
/* Update our slider. |
|
|
|
|
|
|
|
(note that it's important here to tell the slider not to send a change |
|
|
|
message, because that would cause it to call the filter with a parameter |
|
|
|
change message again, and the values would drift out. |
|
|
|
*/ |
|
|
|
gainSlider->setValue (newGain, false); |
|
|
|
|
|
|
|
setSize (filter->lastUIWidth, |
|
|
|
filter->lastUIHeight); |
|
|
|
} |
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
|
|
Copyright 2004-7 by Raw Material Software ltd.
|
|
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the
|
|
|
|
GNU General Public License, as published by the Free Software Foundation;
|
|
|
|
either version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
JUCE is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with JUCE; if not, visit www.gnu.org/licenses or write to the
|
|
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
|
|
Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
If you'd like to release a closed-source product which uses JUCE, commercial
|
|
|
|
licenses are also available: visit www.rawmaterialsoftware.com/juce for
|
|
|
|
more information.
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "DemoEditorComponent.h"
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
// quick-and-dirty function to format a timecode string
|
|
|
|
static const String timeToTimecodeString (const double seconds)
|
|
|
|
{
|
|
|
|
const double absSecs = fabs (seconds);
|
|
|
|
const tchar* const sign = (seconds < 0) ? T("-") : T("");
|
|
|
|
|
|
|
|
const int hours = (int) (absSecs / (60.0 * 60.0));
|
|
|
|
const int mins = ((int) (absSecs / 60.0)) % 60;
|
|
|
|
const int secs = ((int) absSecs) % 60;
|
|
|
|
|
|
|
|
return String::formatted (T("%s%02d:%02d:%02d:%03d"),
|
|
|
|
sign, hours, mins, secs,
|
|
|
|
roundDoubleToInt (absSecs * 1000) % 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// quick-and-dirty function to format a bars/beats string
|
|
|
|
static const String ppqToBarsBeatsString (const double ppq,
|
|
|
|
const double lastBarPPQ,
|
|
|
|
const int numerator,
|
|
|
|
const int denominator)
|
|
|
|
{
|
|
|
|
if (numerator == 0 || denominator == 0)
|
|
|
|
return T("1|1|0");
|
|
|
|
|
|
|
|
const int ppqPerBar = (numerator * 4 / denominator);
|
|
|
|
const double beats = (fmod (ppq, ppqPerBar) / ppqPerBar) * numerator;
|
|
|
|
|
|
|
|
const int bar = ((int) ppq) / ppqPerBar + 1;
|
|
|
|
const int beat = ((int) beats) + 1;
|
|
|
|
const int ticks = ((int) (fmod (beats, 1.0) * 960.0));
|
|
|
|
|
|
|
|
String s;
|
|
|
|
s << bar << T('|') << beat << T('|') << ticks;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
DemoEditorComponent::DemoEditorComponent (DemoJuceFilter* const ownerFilter)
|
|
|
|
: AudioFilterEditor (ownerFilter)
|
|
|
|
{
|
|
|
|
// create our gain slider..
|
|
|
|
addAndMakeVisible (gainSlider = new Slider (T("gain")));
|
|
|
|
gainSlider->addListener (this);
|
|
|
|
gainSlider->setRange (0.0, 1.0, 0.01);
|
|
|
|
gainSlider->setTooltip (T("changes the volume of the audio that runs through the plugin.."));
|
|
|
|
|
|
|
|
// get the gain parameter from the filter and use it to set up our slider
|
|
|
|
gainSlider->setValue (ownerFilter->getParameter (0), false);
|
|
|
|
|
|
|
|
// create and add the midi keyboard component..
|
|
|
|
addAndMakeVisible (midiKeyboard
|
|
|
|
= new MidiKeyboardComponent (ownerFilter->keyboardState,
|
|
|
|
MidiKeyboardComponent::horizontalKeyboard));
|
|
|
|
|
|
|
|
// add a label that will display the current timecode and status..
|
|
|
|
addAndMakeVisible (infoLabel = new Label (String::empty, String::empty));
|
|
|
|
|
|
|
|
// add the triangular resizer component for the bottom-right of the UI
|
|
|
|
addAndMakeVisible (resizer = new ResizableCornerComponent (this, &resizeLimits));
|
|
|
|
resizeLimits.setSizeLimits (150, 150, 800, 300);
|
|
|
|
|
|
|
|
// set our component's initial size to be the last one that was stored in the filter's settings
|
|
|
|
setSize (ownerFilter->lastUIWidth,
|
|
|
|
ownerFilter->lastUIHeight);
|
|
|
|
|
|
|
|
// register ourselves with the filter - it will use its ChangeBroadcaster base
|
|
|
|
// class to tell us when something has changed, and this will call our changeListenerCallback()
|
|
|
|
// method.
|
|
|
|
ownerFilter->addChangeListener (this);
|
|
|
|
}
|
|
|
|
|
|
|
|
DemoEditorComponent::~DemoEditorComponent()
|
|
|
|
{
|
|
|
|
getFilter()->removeChangeListener (this);
|
|
|
|
|
|
|
|
deleteAllChildren();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void DemoEditorComponent::paint (Graphics& g)
|
|
|
|
{
|
|
|
|
// just clear the window
|
|
|
|
g.fillAll (Colour::greyLevel (0.9f));
|
|
|
|
}
|
|
|
|
|
|
|
|
void DemoEditorComponent::resized()
|
|
|
|
{
|
|
|
|
gainSlider->setBounds (10, 10, 200, 22);
|
|
|
|
infoLabel->setBounds (10, 35, 450, 20);
|
|
|
|
|
|
|
|
const int keyboardHeight = 70;
|
|
|
|
midiKeyboard->setBounds (4, getHeight() - keyboardHeight - 4,
|
|
|
|
getWidth() - 8, keyboardHeight);
|
|
|
|
|
|
|
|
resizer->setBounds (getWidth() - 16, getHeight() - 16, 16, 16);
|
|
|
|
|
|
|
|
// if we've been resized, tell the filter so that it can store the new size
|
|
|
|
// in its settings
|
|
|
|
getFilter()->lastUIWidth = getWidth();
|
|
|
|
getFilter()->lastUIHeight = getHeight();
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void DemoEditorComponent::changeListenerCallback (void* source)
|
|
|
|
{
|
|
|
|
// this is the filter telling us that it's changed, so we'll update our
|
|
|
|
// display of the time, midi message, etc.
|
|
|
|
updateParametersFromFilter();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DemoEditorComponent::sliderValueChanged (Slider*)
|
|
|
|
{
|
|
|
|
getFilter()->setParameterNotifyingHost (0, (float) gainSlider->getValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void DemoEditorComponent::updateParametersFromFilter()
|
|
|
|
{
|
|
|
|
DemoJuceFilter* const filter = getFilter();
|
|
|
|
|
|
|
|
// we use this lock to make sure the processBlock() method isn't writing to the
|
|
|
|
// lastMidiMessage variable while we're trying to read it, but be extra-careful to
|
|
|
|
// only hold the lock for a minimum amount of time..
|
|
|
|
filter->getCallbackLock().enter();
|
|
|
|
|
|
|
|
// take a local copy of the info we need while we've got the lock..
|
|
|
|
const AudioFilterBase::CurrentPositionInfo positionInfo (filter->lastPosInfo);
|
|
|
|
const float newGain = filter->getParameter (0);
|
|
|
|
|
|
|
|
// ..release the lock ASAP
|
|
|
|
filter->getCallbackLock().exit();
|
|
|
|
|
|
|
|
|
|
|
|
// ..and after releasing the lock, we're free to do the time-consuming UI stuff..
|
|
|
|
String infoText;
|
|
|
|
infoText << String (positionInfo.bpm, 2) << T(" bpm, ")
|
|
|
|
<< positionInfo.timeSigNumerator << T("/") << positionInfo.timeSigDenominator
|
|
|
|
<< T(" - ") << timeToTimecodeString (positionInfo.timeInSeconds)
|
|
|
|
<< T(" - ") << ppqToBarsBeatsString (positionInfo.ppqPosition,
|
|
|
|
positionInfo.ppqPositionOfLastBarStart,
|
|
|
|
positionInfo.timeSigNumerator,
|
|
|
|
positionInfo.timeSigDenominator);
|
|
|
|
|
|
|
|
if (positionInfo.isPlaying)
|
|
|
|
infoText << T(" (playing)");
|
|
|
|
|
|
|
|
infoLabel->setText (infoText, false);
|
|
|
|
|
|
|
|
/* Update our slider.
|
|
|
|
|
|
|
|
(note that it's important here to tell the slider not to send a change
|
|
|
|
message, because that would cause it to call the filter with a parameter
|
|
|
|
change message again, and the values would drift out.
|
|
|
|
*/
|
|
|
|
gainSlider->setValue (newGain, false);
|
|
|
|
|
|
|
|
setSize (filter->lastUIWidth,
|
|
|
|
filter->lastUIHeight);
|
|
|
|
}
|