Browse Source

Add new juce files

tags/1.9.7
falkTX 10 years ago
parent
commit
aa150270e7
10 changed files with 2163 additions and 0 deletions
  1. +63
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h
  2. +78
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h
  3. +79
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h
  4. +77
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h
  5. +55
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h
  6. +157
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameters.cpp
  7. +512
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp
  8. +226
    -0
      source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h
  9. +835
    -0
      source/modules/juce_core/native/java/AndroidMidi.java
  10. +81
    -0
      source/modules/juce_core/native/java/AndroidMidiFallback.java

+ 63
- 0
source/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h View File

@@ -0,0 +1,63 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
/**
Provides a class of AudioProcessorParameter that can be used as a boolean value.
@see AudioParameterFloat, AudioParameterInt, AudioParameterChoice
*/
class JUCE_API AudioParameterBool : public AudioProcessorParameterWithID
{
public:
/** Creates a AudioParameterBool with an ID and name.
On creation, its value is set to the default value.
*/
AudioParameterBool (String parameterID, String name, bool defaultValue);
/** Destructor. */
~AudioParameterBool();
/** Returns the parameter's current boolean value. */
bool get() const noexcept { return value >= 0.5f; }
/** Returns the parameter's current boolean value. */
operator bool() const noexcept { return get(); }
/** Changes the parameter's current value to a new boolean. */
AudioParameterBool& operator= (bool newValue);
private:
//==============================================================================
float value, defaultValue;
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterBool)
};

+ 78
- 0
source/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h View File

@@ -0,0 +1,78 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
/**
Provides a class of AudioProcessorParameter that can be used to select
an indexed, named choice from a list.
@see AudioParameterFloat, AudioParameterInt, AudioParameterBool
*/
class JUCE_API AudioParameterChoice : public AudioProcessorParameterWithID
{
public:
/** Creates a AudioParameterChoice with an ID, name, and list of items.
On creation, its value is set to the default index.
*/
AudioParameterChoice (String parameterID, String name,
const StringArray& choices,
int defaultItemIndex);
/** Destructor. */
~AudioParameterChoice();
/** Returns the current index of the selected item. */
int getIndex() const noexcept { return roundToInt (value); }
/** Returns the current index of the selected item. */
operator int() const noexcept { return getIndex(); }
/** Returns the name of the currently selected item. */
String getCurrentChoiceName() const noexcept { return choices[getIndex()]; }
/** Returns the name of the currently selected item. */
operator String() const noexcept { return getCurrentChoiceName(); }
/** Changes the selected item to a new index. */
AudioParameterChoice& operator= (int newValue);
/** Provides access to the list of choices that this parameter is working with. */
const StringArray choices;
private:
//==============================================================================
float value, defaultValue;
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
int limitRange (int) const noexcept;
float convertTo0to1 (int) const noexcept;
int convertFrom0to1 (float) const noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterChoice)
};

+ 79
- 0
source/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h View File

@@ -0,0 +1,79 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
/**
A subclass of AudioProcessorParameter that provides an easy way to create a
parameter which maps onto a given NormalisableRange.
@see AudioParameterInt, AudioParameterBool, AudioParameterChoice
*/
class JUCE_API AudioParameterFloat : public AudioProcessorParameterWithID
{
public:
/** Creates a AudioParameterFloat with an ID, name, and range.
On creation, its value is set to the default value.
*/
AudioParameterFloat (String parameterID, String name,
NormalisableRange<float> normalisableRange,
float defaultValue);
/** Creates a AudioParameterFloat with an ID, name, and range.
On creation, its value is set to the default value.
For control over skew factors, you can use the other
constructor and provide a NormalisableRange.
*/
AudioParameterFloat (String parameterID, String name,
float minValue,
float maxValue,
float defaultValue);
/** Destructor. */
~AudioParameterFloat();
/** Returns the parameter's current value. */
float get() const noexcept { return value; }
/** Returns the parameter's current value. */
operator float() const noexcept { return value; }
/** Changes the parameter's current value. */
AudioParameterFloat& operator= (float newValue);
/** Provides access to the parameter's range. */
NormalisableRange<float> range;
private:
//==============================================================================
float value, defaultValue;
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterFloat)
};

+ 77
- 0
source/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h View File

@@ -0,0 +1,77 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
/**
Provides a class of AudioProcessorParameter that can be used as an
integer value with a given range.
@see AudioParameterFloat, AudioParameterBool, AudioParameterChoice
*/
class JUCE_API AudioParameterInt : public AudioProcessorParameterWithID
{
public:
/** Creates an AudioParameterInt with an ID, name, and range.
Note that the min and max range values are inclusive.
On creation, its value is set to the default value.
*/
AudioParameterInt (String parameterID, String name,
int minValue, int maxValue,
int defaultValue);
/** Destructor. */
~AudioParameterInt();
/** Returns the parameter's current value as an integer. */
int get() const noexcept { return roundToInt (value); }
/** Returns the parameter's current value as an integer. */
operator int() const noexcept { return get(); }
/** Changes the parameter's current value to a new integer.
The value passed in will be snapped to the permitted range before being used.
*/
AudioParameterInt& operator= (int newValue);
/** Returns the parameter's range. */
Range<int> getRange() const noexcept { return Range<int> (minValue, maxValue); }
private:
//==============================================================================
int minValue, maxValue;
float value, defaultValue;
float getValue() const override;
void setValue (float newValue) override;
float getDefaultValue() const override;
int getNumSteps() const override;
String getText (float, int) const override;
float getValueForText (const String&) const override;
int limitRange (int) const noexcept;
float convertTo0to1 (int) const noexcept;
int convertFrom0to1 (float) const noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioParameterInt)
};

+ 55
- 0
source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h View File

@@ -0,0 +1,55 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
/**
This abstract base class is used by some AudioProcessorParameter helper classes.
@see AudioParameterFloat, AudioParameterInt, AudioParameterBool, AudioParameterChoice
*/
class JUCE_API AudioProcessorParameterWithID : public AudioProcessorParameter
{
public:
/** Creation of this object requires providing a name and ID which will be
constant for its lifetime.
*/
AudioProcessorParameterWithID (String parameterID, String name);
/** Destructor. */
~AudioProcessorParameterWithID();
/** Provides access to the parameter's ID string. */
const String paramID;
/** Provides access to the parameter's name. */
const String name;
private:
String label;
String getName (int) const override;
String getLabel() const override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorParameterWithID)
};

+ 157
- 0
source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameters.cpp View File

@@ -0,0 +1,157 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
// This file contains the implementations of the various AudioParameter[XYZ] classes..
AudioProcessorParameterWithID::AudioProcessorParameterWithID (String pid, String nm) : paramID (pid), name (nm) {}
AudioProcessorParameterWithID::~AudioProcessorParameterWithID() {}
String AudioProcessorParameterWithID::getName (int maximumStringLength) const { return name.substring (0, maximumStringLength); }
String AudioProcessorParameterWithID::getLabel() const { return label; }
//==============================================================================
AudioParameterFloat::AudioParameterFloat (String pid, String nm, NormalisableRange<float> r, float def)
: AudioProcessorParameterWithID (pid, nm), range (r), value (def), defaultValue (def)
{
}
AudioParameterFloat::AudioParameterFloat (String pid, String nm, float minValue, float maxValue, float def)
: AudioProcessorParameterWithID (pid, nm), range (minValue, maxValue), value (def), defaultValue (def)
{
}
AudioParameterFloat::~AudioParameterFloat() {}
float AudioParameterFloat::getValue() const { return range.convertTo0to1 (value); }
void AudioParameterFloat::setValue (float newValue) { value = range.convertFrom0to1 (newValue); }
float AudioParameterFloat::getDefaultValue() const { return range.convertTo0to1 (defaultValue); }
int AudioParameterFloat::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); }
float AudioParameterFloat::getValueForText (const String& text) const { return range.convertTo0to1 (text.getFloatValue()); }
String AudioParameterFloat::getText (float v, int length) const { return String (range.convertFrom0to1 (v), 2).substring (0, length); }
AudioParameterFloat& AudioParameterFloat::operator= (float newValue)
{
const float normalisedValue = range.convertTo0to1 (newValue);
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
return *this;
}
//==============================================================================
AudioParameterInt::AudioParameterInt (String pid, String nm, int mn, int mx, int def)
: AudioProcessorParameterWithID (pid, nm),
minValue (mn), maxValue (mx),
value ((float) def),
defaultValue (convertTo0to1 (def))
{
jassert (minValue < maxValue); // must have a non-zero range of values!
}
AudioParameterInt::~AudioParameterInt() {}
int AudioParameterInt::limitRange (int v) const noexcept { return jlimit (minValue, maxValue, v); }
float AudioParameterInt::convertTo0to1 (int v) const noexcept { return (limitRange (v) - minValue) / (float) (maxValue - minValue); }
int AudioParameterInt::convertFrom0to1 (float v) const noexcept { return limitRange (roundToInt ((v * (float) (maxValue - minValue)) + minValue)); }
float AudioParameterInt::getValue() const { return convertTo0to1 (roundToInt (value)); }
void AudioParameterInt::setValue (float newValue) { value = (float) convertFrom0to1 (newValue); }
float AudioParameterInt::getDefaultValue() const { return defaultValue; }
int AudioParameterInt::getNumSteps() const { return AudioProcessorParameterWithID::getNumSteps(); }
float AudioParameterInt::getValueForText (const String& text) const { return convertTo0to1 (text.getIntValue()); }
String AudioParameterInt::getText (float v, int /*length*/) const { return String (convertFrom0to1 (v)); }
AudioParameterInt& AudioParameterInt::operator= (int newValue)
{
const float normalisedValue = convertTo0to1 (newValue);
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
return *this;
}
//==============================================================================
AudioParameterBool::AudioParameterBool (String pid, String nm, bool def)
: AudioProcessorParameterWithID (pid, nm),
value (def ? 1.0f : 0.0f),
defaultValue (value)
{
}
AudioParameterBool::~AudioParameterBool() {}
float AudioParameterBool::getValue() const { return value; }
void AudioParameterBool::setValue (float newValue) { value = newValue; }
float AudioParameterBool::getDefaultValue() const { return defaultValue; }
int AudioParameterBool::getNumSteps() const { return 2; }
float AudioParameterBool::getValueForText (const String& text) const { return text.getIntValue() != 0 ? 1.0f : 0.0f; }
String AudioParameterBool::getText (float v, int /*length*/) const { return String ((int) (v > 0.5f ? 1 : 0)); }
AudioParameterBool& AudioParameterBool::operator= (bool newValue)
{
const float normalisedValue = newValue ? 1.0f : 0.0f;
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
return *this;
}
//==============================================================================
AudioParameterChoice::AudioParameterChoice (String pid, String nm, const StringArray& c, int def)
: AudioProcessorParameterWithID (pid, nm), choices (c),
value ((float) def),
defaultValue (convertTo0to1 (def))
{
jassert (choices.size() > 0); // you must supply an actual set of items to choose from!
}
AudioParameterChoice::~AudioParameterChoice() {}
int AudioParameterChoice::limitRange (int v) const noexcept { return jlimit (0, choices.size() - 1, v); }
float AudioParameterChoice::convertTo0to1 (int v) const noexcept { return jlimit (0.0f, 1.0f, (v + 0.5f) / (float) choices.size()); }
int AudioParameterChoice::convertFrom0to1 (float v) const noexcept { return limitRange ((int) (v * (float) choices.size())); }
float AudioParameterChoice::getValue() const { return convertTo0to1 (roundToInt (value)); }
void AudioParameterChoice::setValue (float newValue) { value = (float) convertFrom0to1 (newValue); }
float AudioParameterChoice::getDefaultValue() const { return defaultValue; }
int AudioParameterChoice::getNumSteps() const { return choices.size(); }
float AudioParameterChoice::getValueForText (const String& text) const { return convertTo0to1 (choices.indexOf (text)); }
String AudioParameterChoice::getText (float v, int /*length*/) const { return choices [convertFrom0to1 (v)]; }
AudioParameterChoice& AudioParameterChoice::operator= (int newValue)
{
const float normalisedValue = convertTo0to1 (newValue);
if (value != normalisedValue)
setValueNotifyingHost (normalisedValue);
return *this;
}

+ 512
- 0
source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp View File

@@ -0,0 +1,512 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#if JUCE_COMPILER_SUPPORTS_LAMBDAS
//==============================================================================
struct AudioProcessorValueTreeState::Parameter : public AudioProcessorParameter,
private ValueTree::Listener
{
Parameter (AudioProcessorValueTreeState& s,
String parameterID, String paramName, String labelText,
NormalisableRange<float> r, float defaultVal,
std::function<String (float)> valueToText,
std::function<float (const String&)> textToValue)
: owner (s), paramID (parameterID), name (paramName), label (labelText),
valueToTextFunction (valueToText),
textToValueFunction (textToValue),
range (r), value (defaultVal), defaultValue (defaultVal),
listenersNeedCalling (true)
{
state.addListener (this);
needsUpdate.set (1);
}
~Parameter()
{
// should have detached all callbacks before destroying the parameters!
jassert (listeners.size() <= 1);
}
float getValue() const override { return range.convertTo0to1 (value); }
float getDefaultValue() const override { return range.convertTo0to1 (defaultValue); }
String getName (int maximumStringLength) const override { return name.substring (0, maximumStringLength); }
String getLabel() const override { return label; }
float getValueForText (const String& text) const override
{
return range.convertTo0to1 (textToValueFunction != nullptr ? textToValueFunction (text)
: text.getFloatValue());
}
String getText (float v, int length) const override
{
return valueToTextFunction != nullptr ? valueToTextFunction (range.convertFrom0to1 (v))
: AudioProcessorParameter::getText (v, length);
}
void setValue (float newValue) override
{
newValue = range.snapToLegalValue (range.convertFrom0to1 (newValue));
if (value != newValue || listenersNeedCalling)
{
value = newValue;
listeners.call (&AudioProcessorValueTreeState::Listener::parameterChanged, paramID, value);
listenersNeedCalling = false;
needsUpdate.set (1);
}
}
void setNewState (const ValueTree& v)
{
state = v;
updateFromValueTree();
}
void setUnnormalisedValue (float newUnnormalisedValue)
{
if (value != newUnnormalisedValue)
{
const float newValue = range.convertTo0to1 (newUnnormalisedValue);
setValueNotifyingHost (newValue);
}
}
void updateFromValueTree()
{
setUnnormalisedValue (state.getProperty (owner.valuePropertyID, defaultValue));
}
void copyValueToValueTree()
{
if (state.isValid())
state.setProperty (owner.valuePropertyID, value, owner.undoManager);
}
void valueTreePropertyChanged (ValueTree&, const Identifier& property) override
{
if (property == owner.valuePropertyID)
updateFromValueTree();
}
void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
void valueTreeParentChanged (ValueTree&) override {}
static Parameter* getParameterForID (AudioProcessor& processor, StringRef paramID) noexcept
{
const int numParams = processor.getParameters().size();
for (int i = 0; i < numParams; ++i)
{
AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i);
// When using this class, you must allow it to manage all the parameters in your AudioProcessor, and
// not add any parameter objects of other types!
jassert (dynamic_cast<Parameter*> (ap) != nullptr);
Parameter* const p = static_cast<Parameter*> (ap);
if (paramID == p->paramID)
return p;
}
return nullptr;
}
AudioProcessorValueTreeState& owner;
ValueTree state;
String paramID, name, label;
ListenerList<AudioProcessorValueTreeState::Listener> listeners;
std::function<String (float)> valueToTextFunction;
std::function<float (const String&)> textToValueFunction;
NormalisableRange<float> range;
float value, defaultValue;
Atomic<int> needsUpdate;
bool listenersNeedCalling;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Parameter)
};
//==============================================================================
AudioProcessorValueTreeState::AudioProcessorValueTreeState (AudioProcessor& p, UndoManager* um)
: processor (p),
undoManager (um),
valueType ("PARAM"),
valuePropertyID ("value"),
idPropertyID ("id"),
updatingConnections (false)
{
startTimerHz (10);
state.addListener (this);
}
AudioProcessorValueTreeState::~AudioProcessorValueTreeState() {}
AudioProcessorParameter* AudioProcessorValueTreeState::createAndAddParameter (String paramID, String paramName, String labelText,
NormalisableRange<float> r, float defaultVal,
std::function<String (float)> valueToTextFunction,
std::function<float (const String&)> textToValueFunction)
{
// All parameters must be created before giving this manager a ValueTree state!
jassert (! state.isValid());
jassert (MessageManager::getInstance()->isThisTheMessageThread());
Parameter* p = new Parameter (*this, paramID, paramName, labelText, r,
defaultVal, valueToTextFunction, textToValueFunction);
processor.addParameter (p);
return p;
}
void AudioProcessorValueTreeState::addParameterListener (StringRef paramID, Listener* listener)
{
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
p->listeners.add (listener);
}
void AudioProcessorValueTreeState::removeParameterListener (StringRef paramID, Listener* listener)
{
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
p->listeners.remove (listener);
}
Value AudioProcessorValueTreeState::getParameterAsValue (StringRef paramID) const
{
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
return p->state.getPropertyAsValue (valuePropertyID, undoManager);
return Value();
}
NormalisableRange<float> AudioProcessorValueTreeState::getParameterRange (StringRef paramID) const noexcept
{
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
return p->range;
return NormalisableRange<float>();
}
AudioProcessorParameter* AudioProcessorValueTreeState::getParameter (StringRef paramID) const noexcept
{
return Parameter::getParameterForID (processor, paramID);
}
float* AudioProcessorValueTreeState::getRawParameterValue (StringRef paramID) const noexcept
{
if (Parameter* p = Parameter::getParameterForID (processor, paramID))
return &(p->value);
return nullptr;
}
ValueTree AudioProcessorValueTreeState::getOrCreateChildValueTree (const String& paramID)
{
ValueTree v (state.getChildWithProperty (idPropertyID, paramID));
if (! v.isValid())
{
v = ValueTree (valueType);
v.setProperty (idPropertyID, paramID, undoManager);
state.addChild (v, -1, undoManager);
}
return v;
}
void AudioProcessorValueTreeState::updateParameterConnectionsToChildTrees()
{
if (! updatingConnections)
{
ScopedValueSetter<bool> svs (updatingConnections, true, false);
const int numParams = processor.getParameters().size();
for (int i = 0; i < numParams; ++i)
{
AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i);
jassert (dynamic_cast<Parameter*> (ap) != nullptr);
Parameter* p = static_cast<Parameter*> (ap);
p->setNewState (getOrCreateChildValueTree (p->paramID));
}
}
}
void AudioProcessorValueTreeState::valueTreePropertyChanged (ValueTree&, const Identifier& property)
{
if (property == idPropertyID)
updateParameterConnectionsToChildTrees();
}
void AudioProcessorValueTreeState::valueTreeChildAdded (ValueTree& parent, ValueTree&)
{
if (parent == state)
updateParameterConnectionsToChildTrees();
}
void AudioProcessorValueTreeState::valueTreeChildRemoved (ValueTree& parent, ValueTree&, int)
{
if (parent == state)
updateParameterConnectionsToChildTrees();
}
void AudioProcessorValueTreeState::valueTreeRedirected (ValueTree& v)
{
if (v == state)
updateParameterConnectionsToChildTrees();
}
void AudioProcessorValueTreeState::valueTreeChildOrderChanged (ValueTree&, int, int) {}
void AudioProcessorValueTreeState::valueTreeParentChanged (ValueTree&) {}
void AudioProcessorValueTreeState::timerCallback()
{
const int numParams = processor.getParameters().size();
bool anythingUpdated = false;
for (int i = 0; i < numParams; ++i)
{
AudioProcessorParameter* const ap = processor.getParameters().getUnchecked(i);
jassert (dynamic_cast<Parameter*> (ap) != nullptr);
Parameter* p = static_cast<Parameter*> (ap);
if (p->needsUpdate.compareAndSetBool (0, 1))
{
p->copyValueToValueTree();
anythingUpdated = true;
}
}
startTimer (anythingUpdated ? 1000 / 50
: jlimit (50, 500, getTimerInterval() + 20));
}
AudioProcessorValueTreeState::Listener::Listener() {}
AudioProcessorValueTreeState::Listener::~Listener() {}
//==============================================================================
struct AttachedControlBase : public AudioProcessorValueTreeState::Listener,
public AsyncUpdater
{
AttachedControlBase (AudioProcessorValueTreeState& s, const String& p)
: state (s), paramID (p), lastValue (0)
{
state.addParameterListener (paramID, this);
}
void removeListener()
{
state.removeParameterListener (paramID, this);
}
void setNewUnnormalisedValue (float newUnnormalisedValue)
{
if (AudioProcessorParameter* p = state.getParameter (paramID))
{
const float newValue = state.getParameterRange (paramID)
.convertTo0to1 (newUnnormalisedValue);
if (p->getValue() != newValue)
p->setValueNotifyingHost (newValue);
}
}
void sendInitialUpdate()
{
if (float* v = state.getRawParameterValue (paramID))
parameterChanged (paramID, *v);
}
void parameterChanged (const String&, float newValue) override
{
lastValue = newValue;
if (MessageManager::getInstance()->isThisTheMessageThread())
{
cancelPendingUpdate();
setValue (newValue);
}
else
{
triggerAsyncUpdate();
}
}
void handleAsyncUpdate() override
{
setValue (lastValue);
}
virtual void setValue (float) = 0;
AudioProcessorValueTreeState& state;
String paramID;
float lastValue;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttachedControlBase)
};
//==============================================================================
struct AudioProcessorValueTreeState::SliderAttachment::Pimpl : private AttachedControlBase,
private Slider::Listener
{
Pimpl (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
: AttachedControlBase (s, p), slider (sl)
{
NormalisableRange<float> range (s.getParameterRange (paramID));
slider.setRange (range.start, range.end, range.interval);
if (AudioProcessorParameter* param = state.getParameter (paramID))
slider.setDoubleClickReturnValue (true, range.convertFrom0to1 (param->getDefaultValue()));
sendInitialUpdate();
slider.addListener (this);
}
~Pimpl()
{
slider.removeListener (this);
removeListener();
}
void setValue (float newValue) override
{
slider.setValue (newValue, sendNotificationSync);
}
void sliderValueChanged (Slider* s) override
{
if (! ModifierKeys::getCurrentModifiers().isRightButtonDown())
setNewUnnormalisedValue ((float) s->getValue());
}
void sliderDragStarted (Slider*) override
{
if (AudioProcessorParameter* p = state.getParameter (paramID))
p->beginChangeGesture();
}
void sliderDragEnded (Slider*) override
{
if (AudioProcessorParameter* p = state.getParameter (paramID))
p->endChangeGesture();
}
Slider& slider;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
};
AudioProcessorValueTreeState::SliderAttachment::SliderAttachment (AudioProcessorValueTreeState& s, const String& p, Slider& sl)
: pimpl (new Pimpl (s, p, sl))
{
}
AudioProcessorValueTreeState::SliderAttachment::~SliderAttachment() {}
//==============================================================================
struct AudioProcessorValueTreeState::ComboBoxAttachment::Pimpl : private AttachedControlBase,
private ComboBox::Listener
{
Pimpl (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
: AttachedControlBase (s, p), combo (c)
{
sendInitialUpdate();
combo.addListener (this);
}
~Pimpl()
{
combo.removeListener (this);
removeListener();
}
void setValue (float newValue) override
{
combo.setSelectedItemIndex (roundToInt (newValue), sendNotificationSync);
}
void comboBoxChanged (ComboBox* comboBox) override
{
setNewUnnormalisedValue ((float) comboBox->getSelectedId() - 1.0f);
}
ComboBox& combo;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
};
AudioProcessorValueTreeState::ComboBoxAttachment::ComboBoxAttachment (AudioProcessorValueTreeState& s, const String& p, ComboBox& c)
: pimpl (new Pimpl (s, p, c))
{
}
AudioProcessorValueTreeState::ComboBoxAttachment::~ComboBoxAttachment() {}
//==============================================================================
struct AudioProcessorValueTreeState::ButtonAttachment::Pimpl : private AttachedControlBase,
private Button::Listener
{
Pimpl (AudioProcessorValueTreeState& s, const String& p, Button& b)
: AttachedControlBase (s, p), button (b)
{
sendInitialUpdate();
button.addListener (this);
}
~Pimpl()
{
button.removeListener (this);
removeListener();
}
void setValue (float newValue) override
{
button.setToggleState (newValue >= 0.5f, sendNotificationSync);
}
void buttonClicked (Button* b) override
{
setNewUnnormalisedValue (b->getToggleState() ? 1.0f : 0.0f);
}
Button& button;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
};
AudioProcessorValueTreeState::ButtonAttachment::ButtonAttachment (AudioProcessorValueTreeState& s, const String& p, Button& b)
: pimpl (new Pimpl (s, p, b))
{
}
AudioProcessorValueTreeState::ButtonAttachment::~ButtonAttachment() {}
#endif

+ 226
- 0
source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h View File

@@ -0,0 +1,226 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2013 - Raw Material Software Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
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.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOPROCESSORVALUETREESTATE_H_INCLUDED
#define JUCE_AUDIOPROCESSORVALUETREESTATE_H_INCLUDED
#if JUCE_COMPILER_SUPPORTS_LAMBDAS || defined (DOXYGEN)
/**
This class contains a ValueTree which is used to manage an AudioProcessor's entire state.
It has its own internal class of parameter object which are linked to values
within its ValueTree, and which are each identified by a string ID.
To use: Create a AudioProcessorValueTreeState, and give it some parameters
using createParameter().
You can get access to the underlying ValueTree object via the state member variable,
so you can add extra properties to it as necessary.
It also provides some utility child classes for connecting parameters directly to
GUI controls like sliders.
*/
class JUCE_API AudioProcessorValueTreeState : private Timer,
private ValueTree::Listener
{
public:
/** Creates a state object for a given processor.
The UndoManager is optional and can be a nullptr.
After creating your state object, you should add parameters with the
createAndAddParameter() method. Note that each AudioProcessorValueTreeState
should be attached to only one processor, and must have the same lifetime as the
processor, as they will have dependencies on each other.
*/
AudioProcessorValueTreeState (AudioProcessor& processorToConnectTo,
UndoManager* undoManagerToUse);
/** Destructor. */
~AudioProcessorValueTreeState();
/** Creates and returns a new parameter object for controlling a parameter
with the given ID.
Calling this will create and add a special type of AudioProcessorParameter to the
AudioProcessor to which this state is attached.
@param parameterID A unique string ID for the new parameter
@param parameterName The name that the parameter will return from AudioProcessorParameter::getName()
@param labelText The label that the parameter will return from AudioProcessorParameter::getLabel()
@param valueRange A mapping that will be used to determine the value range which this parameter uses
@param defaultValue A default value for the parameter (in non-normalised units)
@param valueToTextFunction A function that will convert a non-normalised value to a string for the
AudioProcessorParameter::getText() method. This can be nullptr to use the
default implementation
@param textToValueFunction The inverse of valueToTextFunction
@returns the parameter object that was created
*/
AudioProcessorParameter* createAndAddParameter (String parameterID,
String parameterName,
String labelText,
NormalisableRange<float> valueRange,
float defaultValue,
std::function<String (float)> valueToTextFunction,
std::function<float (const String&)> textToValueFunction);
/** Returns a parameter by its ID string. */
AudioProcessorParameter* getParameter (StringRef parameterID) const noexcept;
/** Returns a pointer to a floating point representation of a particular
parameter which a realtime process can read to find out its current value.
*/
float* getRawParameterValue (StringRef parameterID) const noexcept;
/** A listener class that can be attached to an AudioProcessorValueTreeState.
Use AudioProcessorValueTreeState::addParameterListener() to register a callback.
*/
struct JUCE_API Listener
{
Listener();
virtual ~Listener();
/** This callback method is called by the AudioProcessorValueTreeState when a parameter changes. */
virtual void parameterChanged (const String& parameterID, float newValue) = 0;
};
/** Attaches a callback to one of the parameters, which will be called when the parameter changes. */
void addParameterListener (StringRef parameterID, Listener* listener);
/** Removes a callback that was previously added with addParameterCallback(). */
void removeParameterListener (StringRef parameterID, Listener* listener);
/** Returns a Value object that can be used to control a particular parameter. */
Value getParameterAsValue (StringRef parameterID) const;
/** Returns the range that was set when the given parameter was created. */
NormalisableRange<float> getParameterRange (StringRef parameterID) const noexcept;
/** A reference to the processor with which this state is associated. */
AudioProcessor& processor;
/** The state of the whole processor.
You can replace this with your own ValueTree object, and can add properties and
children to the tree. This class will automatically add children for each of the
parameter objects that are created by createParameter().
*/
ValueTree state;
/** Provides access to the undo manager that this object is using. */
UndoManager* const undoManager;
//==============================================================================
/** An object of this class maintains a connection between a Slider and a parameter
in an AudioProcessorValueTreeState.
During the lifetime of this SliderAttachment object, it keeps the two things in
sync, making it easy to connect a slider to a parameter. When this object is
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState
and Slider aren't deleted before this object!
*/
class JUCE_API SliderAttachment
{
public:
SliderAttachment (AudioProcessorValueTreeState& stateToControl,
const String& parameterID,
Slider& sliderToControl);
~SliderAttachment();
private:
struct Pimpl;
ScopedPointer<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderAttachment)
};
//==============================================================================
/** An object of this class maintains a connection between a ComboBox and a parameter
in an AudioProcessorValueTreeState.
During the lifetime of this SliderAttachment object, it keeps the two things in
sync, making it easy to connect a combo box to a parameter. When this object is
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState
and ComboBox aren't deleted before this object!
*/
class JUCE_API ComboBoxAttachment
{
public:
ComboBoxAttachment (AudioProcessorValueTreeState& stateToControl,
const String& parameterID,
ComboBox& comboBoxToControl);
~ComboBoxAttachment();
private:
struct Pimpl;
ScopedPointer<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComboBoxAttachment)
};
//==============================================================================
/** An object of this class maintains a connection between a Button and a parameter
in an AudioProcessorValueTreeState.
During the lifetime of this SliderAttachment object, it keeps the two things in
sync, making it easy to connect a button to a parameter. When this object is
deleted, the connection is broken. Make sure that your AudioProcessorValueTreeState
and Button aren't deleted before this object!
*/
class JUCE_API ButtonAttachment
{
public:
ButtonAttachment (AudioProcessorValueTreeState& stateToControl,
const String& parameterID,
Button& buttonToControl);
~ButtonAttachment();
private:
struct Pimpl;
ScopedPointer<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ButtonAttachment)
};
private:
//==============================================================================
struct Parameter;
friend struct Parameter;
ValueTree getOrCreateChildValueTree (const String&);
void timerCallback() override;
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
void valueTreeParentChanged (ValueTree&) override;
void valueTreeRedirected (ValueTree&) override;
void updateParameterConnectionsToChildTrees();
Identifier valueType, valuePropertyID, idPropertyID;
bool updatingConnections;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorValueTreeState)
};
#endif
#endif // JUCE_AUDIOPROCESSORVALUETREESTATE_H_INCLUDED

+ 835
- 0
source/modules/juce_core/native/java/AndroidMidi.java View File

@@ -0,0 +1,835 @@
//==============================================================================
public class BluetoothManager extends ScanCallback
{
BluetoothManager()
{
ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
.setScanMode (ScanSettings.MATCH_MODE_STICKY);
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
return;
}
BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
if (bluetoothLeScanner == null)
{
Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
return;
}
bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
scanSettingsBuilder.build(),
this);
}
public String[] getMidiBluetoothAddresses()
{
return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
return btDevice.getName();
}
public boolean isBluetoothDevicePaired (String address)
{
return getAndroidMidiDeviceManager().isBluetoothDevicePaired (address);
}
public boolean pairBluetoothMidiDevice(String address)
{
BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
if (btDevice == null)
{
Log.d ("JUCE", "failed to create buletooth device from address");
return false;
}
MidiManager mm = (MidiManager) getSystemService (MIDI_SERVICE);
PhysicalMidiDevice midiDevice = PhysicalMidiDevice.fromBluetoothLeDevice (btDevice, mm);
if (midiDevice != null)
{
getAndroidMidiDeviceManager().addDeviceToList (midiDevice);
return true;
}
return false;
}
public void unpairBluetoothMidiDevice (String address)
{
getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
}
public void onScanFailed (int errorCode)
{
}
public void onScanResult (int callbackType, ScanResult result)
{
if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
|| callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
{
BluetoothDevice device = result.getDevice();
if (device != null)
bluetoothMidiDevices.add (device.getAddress());
}
if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
{
Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
BluetoothDevice device = result.getDevice();
if (device != null)
{
bluetoothMidiDevices.remove (device.getAddress());
unpairBluetoothMidiDevice (device.getAddress());
}
}
}
public void onBatchScanResults (List<ScanResult> results)
{
for (ScanResult result : results)
onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
}
private BluetoothLeScanner scanner;
private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
}
public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
{
private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
public JuceMidiInputPort (PhysicalMidiDevice device, long host, MidiOutputPort midiPort)
{
parent = device;
juceHost = host;
port = midiPort;
}
@Override
public boolean isInputPort()
{
return true;
}
@Override
public void start()
{
port.connect (this);
}
@Override
public void stop()
{
port.disconnect (this);
}
@Override
public void close()
{
stop();
try
{
port.close();
}
catch (IOException e)
{
Log.d ("JUCE", "JuceMidiInputPort::close: IOException = " + e.toString());
}
if (parent != null)
{
parent.removePort (port.getPortNumber(), true);
parent = null;
}
}
public void onSend (byte[] msg, int offset, int count, long timestamp)
{
if (count > 0)
handleReceive (juceHost, msg, offset, count, timestamp);
}
@Override
public MidiPortID getPortId()
{
return new MidiPortID (port.getPortNumber(), true);
}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
}
private PhysicalMidiDevice parent = null;
private long juceHost = 0;
private MidiOutputPort port;
}
public static class JuceMidiOutputPort implements JuceMidiPort
{
public JuceMidiOutputPort (PhysicalMidiDevice device, MidiInputPort midiPort)
{
parent = device;
port = midiPort;
}
@Override
public boolean isInputPort()
{
return false;
}
@Override
public void start()
{
}
@Override
public void stop()
{
}
@Override
public void sendMidi (byte[] msg, int offset, int count)
{
try
{
port.send(msg, offset, count);
}
catch (IOException e)
{
Log.d ("JUCE", "JuceMidiOutputPort::sendMidi: IOException = " + e.toString());
}
}
@Override
public void close()
{
try
{
port.close();
}
catch (IOException e)
{
Log.d ("JUCE", "JuceMidiOutputPort::close: IOException = " + e.toString());
}
if (parent != null)
{
parent.removePort (port.getPortNumber(), false);
parent = null;
}
}
@Override
public MidiPortID getPortId()
{
return new MidiPortID (port.getPortNumber(), false);
}
private PhysicalMidiDevice parent = null;
private MidiInputPort port;
}
public static class PhysicalMidiDevice
{
private static class MidiDeviceThread extends Thread
{
public Handler handler = null;
public Object sync = null;
public MidiDeviceThread (Object syncrhonization)
{
sync = syncrhonization;
}
public void run()
{
Looper.prepare();
synchronized (sync)
{
handler = new Handler();
sync.notifyAll();
}
Looper.loop();
}
}
private static class MidiDeviceOpenCallback implements MidiManager.OnDeviceOpenedListener
{
public Object sync = null;
public boolean isWaiting = true;
public android.media.midi.MidiDevice theDevice = null;
public MidiDeviceOpenCallback (Object waiter)
{
sync = waiter;
}
public void onDeviceOpened (MidiDevice device)
{
synchronized (sync)
{
theDevice = device;
isWaiting = false;
sync.notifyAll();
}
}
}
public static PhysicalMidiDevice fromBluetoothLeDevice (BluetoothDevice bluetoothDevice, MidiManager mm)
{
Object waitForCreation = new Object();
MidiDeviceThread thread = new MidiDeviceThread (waitForCreation);
thread.start();
synchronized (waitForCreation)
{
while (thread.handler == null)
{
try
{
waitForCreation.wait();
}
catch (InterruptedException e)
{
Log.d ("JUCE", "Wait was interrupted but we don't care");
}
}
}
Object waitForDevice = new Object();
MidiDeviceOpenCallback openCallback = new MidiDeviceOpenCallback (waitForDevice);
synchronized (waitForDevice)
{
mm.openBluetoothDevice (bluetoothDevice, openCallback, thread.handler);
while (openCallback.isWaiting)
{
try
{
waitForDevice.wait();
}
catch (InterruptedException e)
{
Log.d ("JUCE", "Wait was interrupted but we don't care");
}
}
}
if (openCallback.theDevice == null)
{
Log.d ("JUCE", "openBluetoothDevice failed");
return null;
}
PhysicalMidiDevice device = new PhysicalMidiDevice();
device.handle = openCallback.theDevice;
device.info = device.handle.getInfo();
device.bluetoothAddress = bluetoothDevice.getAddress();
device.midiManager = mm;
return device;
}
public void unpair()
{
if (! bluetoothAddress.equals ("") && handle != null)
{
JuceMidiPort ports[] = new JuceMidiPort[0];
ports = juceOpenedPorts.values().toArray(ports);
for (int i = 0; i < ports.length; ++i)
ports[i].close();
juceOpenedPorts.clear();
try
{
handle.close();
}
catch (IOException e)
{
Log.d ("JUCE", "handle.close(): IOException = " + e.toString());
}
handle = null;
}
}
public static PhysicalMidiDevice fromMidiDeviceInfo (MidiDeviceInfo info, MidiManager mm)
{
PhysicalMidiDevice device = new PhysicalMidiDevice();
device.info = info;
device.midiManager = mm;
return device;
}
public PhysicalMidiDevice()
{
bluetoothAddress = "";
juceOpenedPorts = new Hashtable<MidiPortID, JuceMidiPort>();
handle = null;
}
public MidiDeviceInfo.PortInfo[] getPorts()
{
return info.getPorts();
}
public String getHumanReadableNameForPort (MidiDeviceInfo.PortInfo port, int portIndexToUseInName)
{
String portName = port.getName();
if (portName.equals (""))
portName = ((port.getType() == MidiDeviceInfo.PortInfo.TYPE_OUTPUT) ? "Out " : "In ")
+ Integer.toString (portIndexToUseInName);
return getHumanReadableDeviceName() + " " + portName;
}
public String getHumanReadableNameForPort (int portType, int androidPortID, int portIndexToUseInName)
{
MidiDeviceInfo.PortInfo[] ports = info.getPorts();
for (MidiDeviceInfo.PortInfo port : ports)
{
if (port.getType() == portType)
{
if (port.getPortNumber() == androidPortID)
return getHumanReadableNameForPort (port, portIndexToUseInName);
}
}
return "Unknown";
}
public String getHumanReadableDeviceName()
{
Bundle bundle = info.getProperties();
return bundle.getString (MidiDeviceInfo.PROPERTY_NAME , "Unknown device");
}
public void checkIfDeviceCanBeClosed()
{
if (juceOpenedPorts.size() == 0)
{
// never close bluetooth LE devices, otherwise they unpair and we have
// no idea how many ports they have.
// Only remove bluetooth devices when we specifically unpair
if (bluetoothAddress.equals (""))
{
try
{
handle.close();
handle = null;
}
catch (IOException e)
{
Log.d ("JUCE", "PhysicalMidiDevice::checkIfDeviceCanBeClosed: IOException = " + e.toString());
}
}
}
}
public void removePort (int portIdx, boolean isInput)
{
MidiPortID portID = new MidiPortID (portIdx, isInput);
JuceMidiPort port = juceOpenedPorts.get (portID);
if (port != null)
{
juceOpenedPorts.remove (portID);
checkIfDeviceCanBeClosed();
return;
}
// tried to remove a port that was never added
assert false;
}
public JuceMidiPort openPort (int portIdx, boolean isInput, long host)
{
open();
if (handle == null)
{
Log.d ("JUCE", "PhysicalMidiDevice::openPort: handle = null, device not open");
return null;
}
// make sure that the port is not already open
if (findPortForIdx (portIdx, isInput) != null)
{
Log.d ("JUCE", "PhysicalMidiDevice::openInputPort: port already open, not opening again!");
return null;
}
JuceMidiPort retval = null;
if (isInput)
{
MidiOutputPort androidPort = handle.openOutputPort (portIdx);
if (androidPort == null)
{
Log.d ("JUCE", "PhysicalMidiDevice::openPort: MidiDevice::openOutputPort (portIdx = "
+ Integer.toString (portIdx) + ") failed!");
return null;
}
retval = new JuceMidiInputPort (this, host, androidPort);
}
else
{
MidiInputPort androidPort = handle.openInputPort (portIdx);
if (androidPort == null)
{
Log.d ("JUCE", "PhysicalMidiDevice::openPort: MidiDevice::openInputPort (portIdx = "
+ Integer.toString (portIdx) + ") failed!");
return null;
}
retval = new JuceMidiOutputPort (this, androidPort);
}
juceOpenedPorts.put (new MidiPortID (portIdx, isInput), retval);
return retval;
}
private JuceMidiPort findPortForIdx (int idx, boolean isInput)
{
return juceOpenedPorts.get (new MidiPortID (idx, isInput));
}
// opens the device
private synchronized void open()
{
if (handle != null)
return;
Object waitForCreation = new Object();
MidiDeviceThread thread = new MidiDeviceThread (waitForCreation);
thread.start();
synchronized(waitForCreation)
{
while (thread.handler == null)
{
try
{
waitForCreation.wait();
}
catch (InterruptedException e)
{
Log.d ("JUCE", "wait was interrupted but we don't care");
}
}
}
Object waitForDevice = new Object();
MidiDeviceOpenCallback openCallback = new MidiDeviceOpenCallback (waitForDevice);
synchronized (waitForDevice)
{
midiManager.openDevice (info, openCallback, thread.handler);
while (openCallback.isWaiting)
{
try
{
waitForDevice.wait();
}
catch (InterruptedException e)
{
Log.d ("JUCE", "wait was interrupted but we don't care");
}
}
}
handle = openCallback.theDevice;
}
private MidiDeviceInfo info;
private Hashtable<MidiPortID, JuceMidiPort> juceOpenedPorts;
public MidiDevice handle;
public String bluetoothAddress;
private MidiManager midiManager;
}
//==============================================================================
public class MidiDeviceManager extends MidiManager.DeviceCallback
{
public class MidiPortPath
{
public PhysicalMidiDevice midiDevice;
public int androidMidiPortID;
public int portIndexToUseInName;
}
public class JuceDeviceList
{
public ArrayList<MidiPortPath> inPorts = new ArrayList<MidiPortPath>();
public ArrayList<MidiPortPath> outPorts = new ArrayList<MidiPortPath>();
}
// We need to keep a thread local copy of the devices
// which we returned the last time
// getJuceAndroidMidiIn/OutputDevices() was called
private final ThreadLocal<JuceDeviceList> lastDevicesReturned =
new ThreadLocal<JuceDeviceList>()
{
@Override protected JuceDeviceList initialValue()
{
return new JuceDeviceList();
}
};
public MidiDeviceManager()
{
physicalMidiDevices = new ArrayList<PhysicalMidiDevice>();
manager = (MidiManager) getSystemService (MIDI_SERVICE);
if (manager == null)
{
Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
return;
}
manager.registerDeviceCallback (this, null);
MidiDeviceInfo[] foundDevices = manager.getDevices();
for (MidiDeviceInfo info : foundDevices)
physicalMidiDevices.add (PhysicalMidiDevice.fromMidiDeviceInfo (info, manager));
}
// specifically add a device to the list
public void addDeviceToList (PhysicalMidiDevice device)
{
physicalMidiDevices.add (device);
}
public void unpairBluetoothDevice (String address)
{
for (int i = 0; i < physicalMidiDevices.size(); ++i)
{
PhysicalMidiDevice device = physicalMidiDevices.get(i);
if (device.bluetoothAddress.equals (address))
{
physicalMidiDevices.remove (i);
device.unpair();
return;
}
}
}
public boolean isBluetoothDevicePaired (String address)
{
for (int i = 0; i < physicalMidiDevices.size(); ++i)
{
PhysicalMidiDevice device = physicalMidiDevices.get(i);
if (device.bluetoothAddress.equals (address))
return true;
}
return false;
}
public String[] getJuceAndroidMidiInputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
}
public String[] getJuceAndroidMidiOutputDevices()
{
return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
}
private String[] getJuceAndroidMidiDevices (int portType)
{
ArrayList<MidiPortPath> listOfReturnedDevices = new ArrayList<MidiPortPath>();
List<String> deviceNames = new ArrayList<String>();
for (PhysicalMidiDevice physicalMidiDevice : physicalMidiDevices)
{
int portIdx = 0;
MidiDeviceInfo.PortInfo[] ports = physicalMidiDevice.getPorts();
for (MidiDeviceInfo.PortInfo port : ports)
{
if (port.getType() == portType)
{
MidiPortPath path = new MidiPortPath();
path.midiDevice = physicalMidiDevice;
path.androidMidiPortID = port.getPortNumber();
path.portIndexToUseInName = ++portIdx;
listOfReturnedDevices.add (path);
deviceNames.add (physicalMidiDevice.getHumanReadableNameForPort (port,
path.portIndexToUseInName));
}
}
}
String[] deviceNamesArray = new String[deviceNames.size()];
if (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT)
{
lastDevicesReturned.get().inPorts.clear();
lastDevicesReturned.get().inPorts.addAll (listOfReturnedDevices);
}
else
{
lastDevicesReturned.get().outPorts.clear();
lastDevicesReturned.get().outPorts.addAll (listOfReturnedDevices);
}
return deviceNames.toArray(deviceNamesArray);
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().inPorts;
if (index >= lastDevices.size() || index < 0)
return null;
MidiPortPath path = lastDevices.get (index);
return path.midiDevice.openPort (path.androidMidiPortID, true, host);
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().outPorts;
if (index >= lastDevices.size() || index < 0)
return null;
MidiPortPath path = lastDevices.get (index);
return path.midiDevice.openPort (path.androidMidiPortID, false, 0);
}
public String getInputPortNameForJuceIndex (int index)
{
ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().inPorts;
if (index >= lastDevices.size() || index < 0)
return "";
MidiPortPath path = lastDevices.get (index);
return path.midiDevice.getHumanReadableNameForPort (MidiDeviceInfo.PortInfo.TYPE_INPUT,
path.androidMidiPortID,
path.portIndexToUseInName);
}
public String getOutputPortNameForJuceIndex (int index)
{
ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().outPorts;
if (index >= lastDevices.size() || index < 0)
return "";
MidiPortPath path = lastDevices.get (index);
return path.midiDevice.getHumanReadableNameForPort (MidiDeviceInfo.PortInfo.TYPE_OUTPUT,
path.androidMidiPortID,
path.portIndexToUseInName);
}
public void onDeviceAdded (MidiDeviceInfo info)
{
PhysicalMidiDevice device = PhysicalMidiDevice.fromMidiDeviceInfo (info, manager);
// Do not add bluetooth devices as they are already added by the native bluetooth dialog
if (info.getType() != MidiDeviceInfo.TYPE_BLUETOOTH)
physicalMidiDevices.add (device);
}
public void onDeviceRemoved (MidiDeviceInfo info)
{
for (int i = 0; i < physicalMidiDevices.size(); ++i)
{
if (physicalMidiDevices.get(i).info.getId() == info.getId())
{
physicalMidiDevices.remove (i);
return;
}
}
// Don't assert here as this may be called again after a bluetooth device is unpaired
}
public void onDeviceStatusChanged (MidiDeviceStatus status)
{
}
private ArrayList<PhysicalMidiDevice> physicalMidiDevices;
private MidiManager manager;
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
if (getSystemService (MIDI_SERVICE) == null)
return null;
synchronized (JuceAppActivity.class)
{
if (midiDeviceManager == null)
midiDeviceManager = new MidiDeviceManager();
}
return midiDeviceManager;
}
public BluetoothManager getAndroidBluetoothManager()
{
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null)
return null;
if (adapter.getBluetoothLeScanner() == null)
return null;
synchronized (JuceAppActivity.class)
{
if (bluetoothManager == null)
bluetoothManager = new BluetoothManager();
}
return bluetoothManager;
}

+ 81
- 0
source/modules/juce_core/native/java/AndroidMidiFallback.java View File

@@ -0,0 +1,81 @@
//==============================================================================
public class BluetoothManager
{
BluetoothManager()
{
}
public String[] getMidiBluetoothAddresses()
{
String[] bluetoothAddresses = new String[0];
return bluetoothAddresses;
}
public String getHumanReadableStringForBluetoothAddress (String address)
{
return address;
}
public boolean isBluetoothDevicePaired (String address)
{
return false;
}
public boolean pairBluetoothMidiDevice(String address)
{
return false;
}
public void unpairBluetoothMidiDevice (String address)
{
}
}
//==============================================================================
public class MidiDeviceManager
{
public MidiDeviceManager()
{
}
public String[] getJuceAndroidMidiInputDevices()
{
return new String[0];
}
public String[] getJuceAndroidMidiOutputDevices()
{
return new String[0];
}
public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
{
return null;
}
public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
{
return null;
}
public String getInputPortNameForJuceIndex (int index)
{
return "";
}
public String getOutputPortNameForJuceIndex (int index)
{
return "";
}
}
public MidiDeviceManager getAndroidMidiDeviceManager()
{
return null;
}
public BluetoothManager getAndroidBluetoothManager()
{
return null;
}

Loading…
Cancel
Save