| @@ -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) | |||
| }; | |||
| @@ -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) | |||
| }; | |||
| @@ -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) | |||
| }; | |||
| @@ -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) | |||
| }; | |||
| @@ -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) | |||
| }; | |||
| @@ -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; | |||
| } | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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; | |||
| } | |||
| @@ -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; | |||
| } | |||