Browse Source

Update to new JUCE4

tags/2018-04-16
falkTX 9 years ago
parent
commit
a987743a27
100 changed files with 6592 additions and 1668 deletions
  1. +1
    -0
      .gitignore
  2. +1
    -1
      libs/drowaudio/source/dRowAudio/audio/soundtouch/SoundTouch_Source.cpp
  3. +1
    -1
      libs/drowaudio/source/dRowAudio/audio/soundtouch/cpu_detect_x64_win.cpp
  4. +1
    -1
      libs/drowaudio/source/dRowAudio/utility/dRowAudio_DebugObject.h
  5. +0
    -673
      libs/juce/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp
  6. +639
    -92
      libs/juce/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
  7. +0
    -1
      libs/juce/source/modules/juce_audio_basics/juce_audio_basics.cpp
  8. +1
    -1
      libs/juce/source/modules/juce_audio_basics/juce_audio_basics.h
  9. +1
    -1
      libs/juce/source/modules/juce_audio_basics/juce_module_info
  10. +37
    -6
      libs/juce/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
  11. +24
    -4
      libs/juce/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h
  12. +288
    -36
      libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  13. +45
    -2
      libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
  14. +1
    -1
      libs/juce/source/modules/juce_audio_devices/juce_module_info
  15. +294
    -18
      libs/juce/source/modules/juce_audio_devices/native/juce_android_Midi.cpp
  16. +218
    -99
      libs/juce/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  17. +2
    -2
      libs/juce/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  18. +1
    -1
      libs/juce/source/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
  19. +1
    -1
      libs/juce/source/modules/juce_audio_formats/juce_module_info
  20. +9
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp
  21. +5
    -7
      libs/juce/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
  22. +97
    -26
      libs/juce/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  23. +122
    -54
      libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
  24. +1
    -1
      libs/juce/source/modules/juce_audio_plugin_client/juce_module_info
  25. +2
    -1
      libs/juce/source/modules/juce_audio_plugin_client/utility/juce_FakeMouseMoveGenerator.h
  26. +1
    -1
      libs/juce/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  27. +43
    -19
      libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3Common.h
  28. +47
    -31
      libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  29. +147
    -104
      libs/juce/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  30. +2
    -0
      libs/juce/source/modules/juce_audio_processors/juce_audio_processors.cpp
  31. +6
    -0
      libs/juce/source/modules/juce_audio_processors/juce_audio_processors.h
  32. +3
    -2
      libs/juce/source/modules/juce_audio_processors/juce_module_info
  33. +29
    -2
      libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  34. +113
    -2
      libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  35. +235
    -54
      libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  36. +18
    -7
      libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
  37. +63
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioParameterBool.h
  38. +78
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioParameterChoice.h
  39. +79
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioParameterFloat.h
  40. +77
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioParameterInt.h
  41. +55
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameterWithID.h
  42. +157
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioProcessorParameters.cpp
  43. +512
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.cpp
  44. +226
    -0
      libs/juce/source/modules/juce_audio_processors/utilities/juce_AudioProcessorValueTreeState.h
  45. +38
    -2
      libs/juce/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp
  46. +9
    -1
      libs/juce/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.h
  47. +76
    -0
      libs/juce/source/modules/juce_audio_utils/gui/juce_BluetoothMidiDevicePairingDialogue.h
  48. +17
    -0
      libs/juce/source/modules/juce_audio_utils/juce_audio_utils.cpp
  49. +1
    -0
      libs/juce/source/modules/juce_audio_utils/juce_audio_utils.h
  50. +5
    -2
      libs/juce/source/modules/juce_audio_utils/juce_module_info
  51. +438
    -0
      libs/juce/source/modules/juce_audio_utils/native/juce_android_BluetoothMidiDevicePairingDialogue.cpp
  52. +138
    -0
      libs/juce/source/modules/juce_audio_utils/native/juce_ios_BluetoothMidiDevicePairingDialogue.mm
  53. +37
    -0
      libs/juce/source/modules/juce_audio_utils/native/juce_linux_BluetoothMidiDevicePairingDialogue.cpp
  54. +36
    -0
      libs/juce/source/modules/juce_audio_utils/native/juce_mac_BluetoothMidiDevicePairingDialogue.mm
  55. +37
    -0
      libs/juce/source/modules/juce_audio_utils/native/juce_win_BluetoothMidiDevicePairingDialogue.cpp
  56. +41
    -2
      libs/juce/source/modules/juce_audio_utils/players/juce_AudioProcessorPlayer.cpp
  57. +18
    -4
      libs/juce/source/modules/juce_audio_utils/players/juce_AudioProcessorPlayer.h
  58. +7
    -2
      libs/juce/source/modules/juce_core/containers/juce_Array.h
  59. +35
    -0
      libs/juce/source/modules/juce_core/files/juce_File.cpp
  60. +21
    -15
      libs/juce/source/modules/juce_core/files/juce_File.h
  61. +1
    -1
      libs/juce/source/modules/juce_core/juce_module_info
  62. +2
    -0
      libs/juce/source/modules/juce_core/maths/juce_NormalisableRange.h
  63. +60
    -6
      libs/juce/source/modules/juce_core/memory/juce_ByteOrder.h
  64. +835
    -0
      libs/juce/source/modules/juce_core/native/java/AndroidMidi.java
  65. +81
    -0
      libs/juce/source/modules/juce_core/native/java/AndroidMidiFallback.java
  66. +222
    -72
      libs/juce/source/modules/juce_core/native/java/JuceAppActivity.java
  67. +28
    -133
      libs/juce/source/modules/juce_core/native/juce_android_JNIHelpers.h
  68. +11
    -25
      libs/juce/source/modules/juce_core/native/juce_android_SystemStats.cpp
  69. +232
    -0
      libs/juce/source/modules/juce_core/native/juce_android_Threads.cpp
  70. +1
    -1
      libs/juce/source/modules/juce_core/native/juce_linux_CommonFile.cpp
  71. +1
    -1
      libs/juce/source/modules/juce_core/native/juce_linux_Files.cpp
  72. +7
    -2
      libs/juce/source/modules/juce_core/native/juce_mac_Files.mm
  73. +5
    -18
      libs/juce/source/modules/juce_core/native/juce_posix_SharedCode.h
  74. +34
    -2
      libs/juce/source/modules/juce_core/native/juce_win32_Files.cpp
  75. +6
    -0
      libs/juce/source/modules/juce_core/system/juce_CompilerSupport.h
  76. +35
    -29
      libs/juce/source/modules/juce_core/system/juce_PlatformDefs.h
  77. +3
    -3
      libs/juce/source/modules/juce_core/system/juce_StandardHeader.h
  78. +10
    -0
      libs/juce/source/modules/juce_core/text/juce_CharacterFunctions.cpp
  79. +10
    -0
      libs/juce/source/modules/juce_core/text/juce_CharacterFunctions.h
  80. +152
    -27
      libs/juce/source/modules/juce_core/text/juce_String.cpp
  81. +14
    -0
      libs/juce/source/modules/juce_core/text/juce_String.h
  82. +6
    -0
      libs/juce/source/modules/juce_core/text/juce_StringArray.cpp
  83. +9
    -0
      libs/juce/source/modules/juce_core/text/juce_StringArray.h
  84. +41
    -0
      libs/juce/source/modules/juce_core/unit_tests/juce_UnitTest.h
  85. +1
    -1
      libs/juce/source/modules/juce_cryptography/juce_module_info
  86. +1
    -1
      libs/juce/source/modules/juce_data_structures/juce_module_info
  87. +1
    -1
      libs/juce/source/modules/juce_events/juce_module_info
  88. +2
    -0
      libs/juce/source/modules/juce_events/native/juce_android_Messaging.cpp
  89. +1
    -1
      libs/juce/source/modules/juce_graphics/juce_module_info
  90. +12
    -12
      libs/juce/source/modules/juce_gui_basics/components/juce_Component.cpp
  91. +2
    -2
      libs/juce/source/modules/juce_gui_basics/components/juce_Component.h
  92. +2
    -2
      libs/juce/source/modules/juce_gui_basics/components/juce_Desktop.cpp
  93. +1
    -1
      libs/juce/source/modules/juce_gui_basics/juce_module_info
  94. +9
    -5
      libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseEvent.cpp
  95. +14
    -0
      libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseEvent.h
  96. +30
    -19
      libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp
  97. +24
    -9
      libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.h
  98. +17
    -11
      libs/juce/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp
  99. +20
    -3
      libs/juce/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm
  100. +12
    -0
      libs/juce/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm

+ 1
- 0
.gitignore View File

@@ -52,4 +52,5 @@ libs/juce/source/juce_amalgamated.mm
libs/juce/source/modules/juce_box2d/
libs/juce/source/modules/juce_browser_plugin_client/
libs/juce/source/modules/juce_opengl/
libs/juce/source/modules/juce_osc/
libs/juce/source/modules/juce_video/

+ 1
- 1
libs/drowaudio/source/dRowAudio/audio/soundtouch/SoundTouch_Source.cpp View File

@@ -91,7 +91,7 @@
#include "TDStretch.cpp"
#if JUCE_64BIT
#if JUCE_WIN
#if JUCE_WINDOWS
#include "cpu_detect_x64_win.cpp"
#elif JUCE_MAC || JUCE_IOS || JUCE_LINUX
#include "cpu_detect_x64_gcc.cpp"


+ 1
- 1
libs/drowaudio/source/dRowAudio/audio/soundtouch/cpu_detect_x64_win.cpp View File

@@ -42,7 +42,7 @@
#include "cpu_detect.h"
#ifndef WIN64
#ifndef _WIN64
#error wrong platform - this source code file is exclusively for Win64 platform
#endif


+ 1
- 1
libs/drowaudio/source/dRowAudio/utility/dRowAudio_DebugObject.h View File

@@ -156,7 +156,7 @@ private:
if (xml == nullptr)
return "invalid XmlElement";
return newLine + xml->createDocument (String::empty, false, includeXmlHeader);
return String(NewLine::getDefault()) + xml->createDocument (String::empty, false, includeXmlHeader);
}

String getStringFromValueTree() const


+ 0
- 673
libs/juce/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp View File

@@ -1,673 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI 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.
==============================================================================
*/
AudioSampleBuffer::AudioSampleBuffer() noexcept
: numChannels (0), size (0), allocatedBytes (0),
channels (static_cast<float**> (preallocatedChannelSpace)),
isClear (false)
{
}
AudioSampleBuffer::AudioSampleBuffer (const int numChans,
const int numSamples) noexcept
: numChannels (numChans),
size (numSamples)
{
jassert (numSamples >= 0);
jassert (numChans >= 0);
allocateData();
}
AudioSampleBuffer::AudioSampleBuffer (const AudioSampleBuffer& other) noexcept
: numChannels (other.numChannels),
size (other.size),
allocatedBytes (other.allocatedBytes)
{
if (allocatedBytes == 0)
{
allocateChannels (other.channels, 0);
}
else
{
allocateData();
if (other.isClear)
{
clear();
}
else
{
for (int i = 0; i < numChannels; ++i)
FloatVectorOperations::copy (channels[i], other.channels[i], size);
}
}
}
void AudioSampleBuffer::allocateData()
{
const size_t channelListSize = sizeof (float*) * (size_t) (numChannels + 1);
allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (float) + channelListSize + 32;
allocatedData.malloc (allocatedBytes);
channels = reinterpret_cast<float**> (allocatedData.getData());
float* chan = (float*) (allocatedData + channelListSize);
for (int i = 0; i < numChannels; ++i)
{
channels[i] = chan;
chan += size;
}
channels [numChannels] = nullptr;
isClear = false;
}
AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
const int numChans,
const int numSamples) noexcept
: numChannels (numChans),
size (numSamples),
allocatedBytes (0)
{
jassert (dataToReferTo != nullptr);
jassert (numChans >= 0 && numSamples >= 0);
allocateChannels (dataToReferTo, 0);
}
AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo,
const int numChans,
const int startSample,
const int numSamples) noexcept
: numChannels (numChans),
size (numSamples),
allocatedBytes (0),
isClear (false)
{
jassert (dataToReferTo != nullptr);
jassert (numChans >= 0 && startSample >= 0 && numSamples >= 0);
allocateChannels (dataToReferTo, startSample);
}
void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo,
const int newNumChannels,
const int newNumSamples) noexcept
{
jassert (dataToReferTo != nullptr);
jassert (newNumChannels >= 0 && newNumSamples >= 0);
if (allocatedBytes != 0)
{
allocatedBytes = 0;
allocatedData.free();
}
numChannels = newNumChannels;
size = newNumSamples;
allocateChannels (dataToReferTo, 0);
jassert (! isClear);
}
void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset)
{
jassert (offset >= 0);
// (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools)
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
{
channels = static_cast<float**> (preallocatedChannelSpace);
}
else
{
allocatedData.malloc ((size_t) numChannels + 1, sizeof (float*));
channels = reinterpret_cast<float**> (allocatedData.getData());
}
for (int i = 0; i < numChannels; ++i)
{
// you have to pass in the same number of valid pointers as numChannels
jassert (dataToReferTo[i] != nullptr);
channels[i] = dataToReferTo[i] + offset;
}
channels [numChannels] = nullptr;
isClear = false;
}
AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) noexcept
{
if (this != &other)
{
setSize (other.getNumChannels(), other.getNumSamples(), false, false, false);
if (other.isClear)
{
clear();
}
else
{
isClear = false;
for (int i = 0; i < numChannels; ++i)
FloatVectorOperations::copy (channels[i], other.channels[i], size);
}
}
return *this;
}
AudioSampleBuffer::~AudioSampleBuffer() noexcept
{
}
void AudioSampleBuffer::setSize (const int newNumChannels,
const int newNumSamples,
const bool keepExistingContent,
const bool clearExtraSpace,
const bool avoidReallocating) noexcept
{
jassert (newNumChannels >= 0);
jassert (newNumSamples >= 0);
if (newNumSamples != size || newNumChannels != numChannels)
{
const size_t allocatedSamplesPerChannel = ((size_t) newNumSamples + 3) & ~3u;
const size_t channelListSize = ((sizeof (float*) * (size_t) (newNumChannels + 1)) + 15) & ~15u;
const size_t newTotalBytes = ((size_t) newNumChannels * (size_t) allocatedSamplesPerChannel * sizeof (float))
+ channelListSize + 32;
if (keepExistingContent)
{
HeapBlock<char, true> newData;
newData.allocate (newTotalBytes, clearExtraSpace || isClear);
const size_t numSamplesToCopy = (size_t) jmin (newNumSamples, size);
float** const newChannels = reinterpret_cast<float**> (newData.getData());
float* newChan = reinterpret_cast<float*> (newData + channelListSize);
for (int j = 0; j < newNumChannels; ++j)
{
newChannels[j] = newChan;
newChan += allocatedSamplesPerChannel;
}
if (! isClear)
{
const int numChansToCopy = jmin (numChannels, newNumChannels);
for (int i = 0; i < numChansToCopy; ++i)
FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy);
}
allocatedData.swapWith (newData);
allocatedBytes = newTotalBytes;
channels = newChannels;
}
else
{
if (avoidReallocating && allocatedBytes >= newTotalBytes)
{
if (clearExtraSpace || isClear)
allocatedData.clear (newTotalBytes);
}
else
{
allocatedBytes = newTotalBytes;
allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear);
channels = reinterpret_cast<float**> (allocatedData.getData());
}
float* chan = reinterpret_cast<float*> (allocatedData + channelListSize);
for (int i = 0; i < newNumChannels; ++i)
{
channels[i] = chan;
chan += allocatedSamplesPerChannel;
}
}
channels [newNumChannels] = 0;
size = newNumSamples;
numChannels = newNumChannels;
}
}
void AudioSampleBuffer::clear() noexcept
{
if (! isClear)
{
for (int i = 0; i < numChannels; ++i)
FloatVectorOperations::clear (channels[i], size);
isClear = true;
}
}
void AudioSampleBuffer::clear (const int startSample,
const int numSamples) noexcept
{
jassert (startSample >= 0 && startSample + numSamples <= size);
if (! isClear)
{
if (startSample == 0 && numSamples == size)
isClear = true;
for (int i = 0; i < numChannels; ++i)
FloatVectorOperations::clear (channels[i] + startSample, numSamples);
}
}
void AudioSampleBuffer::clear (const int channel,
const int startSample,
const int numSamples) noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (startSample >= 0 && startSample + numSamples <= size);
if (! isClear)
FloatVectorOperations::clear (channels [channel] + startSample, numSamples);
}
float AudioSampleBuffer::getSample (int channel, int index) const noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (isPositiveAndBelow (index, size));
return *(channels [channel] + index);
}
void AudioSampleBuffer::setSample (int channel, int index, float newValue) noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (isPositiveAndBelow (index, size));
*(channels [channel] + index) = newValue;
isClear = false;
}
void AudioSampleBuffer::addSample (int channel, int index, float valueToAdd) noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (isPositiveAndBelow (index, size));
*(channels [channel] + index) += valueToAdd;
isClear = false;
}
void AudioSampleBuffer::applyGain (const int channel,
const int startSample,
int numSamples,
const float gain) noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (startSample >= 0 && startSample + numSamples <= size);
if (gain != 1.0f && ! isClear)
{
float* const d = channels [channel] + startSample;
if (gain == 0.0f)
FloatVectorOperations::clear (d, numSamples);
else
FloatVectorOperations::multiply (d, gain, numSamples);
}
}
void AudioSampleBuffer::applyGainRamp (const int channel,
const int startSample,
int numSamples,
float startGain,
float endGain) noexcept
{
if (! isClear)
{
if (startGain == endGain)
{
applyGain (channel, startSample, numSamples, startGain);
}
else
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (startSample >= 0 && startSample + numSamples <= size);
const float increment = (endGain - startGain) / numSamples;
float* d = channels [channel] + startSample;
while (--numSamples >= 0)
{
*d++ *= startGain;
startGain += increment;
}
}
}
}
void AudioSampleBuffer::applyGain (int startSample, int numSamples, float gain) noexcept
{
for (int i = 0; i < numChannels; ++i)
applyGain (i, startSample, numSamples, gain);
}
void AudioSampleBuffer::applyGain (const float gain) noexcept
{
applyGain (0, size, gain);
}
void AudioSampleBuffer::applyGainRamp (int startSample, int numSamples,
float startGain, float endGain) noexcept
{
for (int i = 0; i < numChannels; ++i)
applyGainRamp (i, startSample, numSamples, startGain, endGain);
}
void AudioSampleBuffer::addFrom (const int destChannel,
const int destStartSample,
const AudioSampleBuffer& source,
const int sourceChannel,
const int sourceStartSample,
int numSamples,
const float gain) noexcept
{
jassert (&source != this || sourceChannel != destChannel);
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size);
if (gain != 0.0f && numSamples > 0 && ! source.isClear)
{
float* const d = channels [destChannel] + destStartSample;
const float* const s = source.channels [sourceChannel] + sourceStartSample;
if (isClear)
{
isClear = false;
if (gain != 1.0f)
FloatVectorOperations::copyWithMultiply (d, s, gain, numSamples);
else
FloatVectorOperations::copy (d, s, numSamples);
}
else
{
if (gain != 1.0f)
FloatVectorOperations::addWithMultiply (d, s, gain, numSamples);
else
FloatVectorOperations::add (d, s, numSamples);
}
}
}
void AudioSampleBuffer::addFrom (const int destChannel,
const int destStartSample,
const float* source,
int numSamples,
const float gain) noexcept
{
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (source != nullptr);
if (gain != 0.0f && numSamples > 0)
{
float* const d = channels [destChannel] + destStartSample;
if (isClear)
{
isClear = false;
if (gain != 1.0f)
FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples);
else
FloatVectorOperations::copy (d, source, numSamples);
}
else
{
if (gain != 1.0f)
FloatVectorOperations::addWithMultiply (d, source, gain, numSamples);
else
FloatVectorOperations::add (d, source, numSamples);
}
}
}
void AudioSampleBuffer::addFromWithRamp (const int destChannel,
const int destStartSample,
const float* source,
int numSamples,
float startGain,
const float endGain) noexcept
{
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (source != nullptr);
if (startGain == endGain)
{
addFrom (destChannel, destStartSample, source, numSamples, startGain);
}
else
{
if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f))
{
isClear = false;
const float increment = (endGain - startGain) / numSamples;
float* d = channels [destChannel] + destStartSample;
while (--numSamples >= 0)
{
*d++ += startGain * *source++;
startGain += increment;
}
}
}
}
void AudioSampleBuffer::copyFrom (const int destChannel,
const int destStartSample,
const AudioSampleBuffer& source,
const int sourceChannel,
const int sourceStartSample,
int numSamples) noexcept
{
jassert (&source != this || sourceChannel != destChannel);
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size);
if (numSamples > 0)
{
if (source.isClear)
{
if (! isClear)
FloatVectorOperations::clear (channels [destChannel] + destStartSample, numSamples);
}
else
{
isClear = false;
FloatVectorOperations::copy (channels [destChannel] + destStartSample,
source.channels [sourceChannel] + sourceStartSample,
numSamples);
}
}
}
void AudioSampleBuffer::copyFrom (const int destChannel,
const int destStartSample,
const float* source,
int numSamples) noexcept
{
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (source != nullptr);
if (numSamples > 0)
{
isClear = false;
FloatVectorOperations::copy (channels [destChannel] + destStartSample, source, numSamples);
}
}
void AudioSampleBuffer::copyFrom (const int destChannel,
const int destStartSample,
const float* source,
int numSamples,
const float gain) noexcept
{
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (source != nullptr);
if (numSamples > 0)
{
float* const d = channels [destChannel] + destStartSample;
if (gain != 1.0f)
{
if (gain == 0)
{
if (! isClear)
FloatVectorOperations::clear (d, numSamples);
}
else
{
isClear = false;
FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples);
}
}
else
{
isClear = false;
FloatVectorOperations::copy (d, source, numSamples);
}
}
}
void AudioSampleBuffer::copyFromWithRamp (const int destChannel,
const int destStartSample,
const float* source,
int numSamples,
float startGain,
float endGain) noexcept
{
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (source != nullptr);
if (startGain == endGain)
{
copyFrom (destChannel, destStartSample, source, numSamples, startGain);
}
else
{
if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f))
{
isClear = false;
const float increment = (endGain - startGain) / numSamples;
float* d = channels [destChannel] + destStartSample;
while (--numSamples >= 0)
{
*d++ = startGain * *source++;
startGain += increment;
}
}
}
}
void AudioSampleBuffer::reverse (int channel, int startSample, int numSamples) const noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (startSample >= 0 && startSample + numSamples <= size);
if (! isClear)
std::reverse (channels[channel] + startSample,
channels[channel] + startSample + numSamples);
}
void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept
{
for (int i = 0; i < numChannels; ++i)
reverse (i, startSample, numSamples);
}
Range<float> AudioSampleBuffer::findMinMax (const int channel,
const int startSample,
int numSamples) const noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (startSample >= 0 && startSample + numSamples <= size);
if (isClear)
return Range<float>();
return FloatVectorOperations::findMinAndMax (channels [channel] + startSample, numSamples);
}
float AudioSampleBuffer::getMagnitude (const int channel,
const int startSample,
const int numSamples) const noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (startSample >= 0 && startSample + numSamples <= size);
if (isClear)
return 0.0f;
const Range<float> r (findMinMax (channel, startSample, numSamples));
return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd());
}
float AudioSampleBuffer::getMagnitude (int startSample, int numSamples) const noexcept
{
float mag = 0.0f;
if (! isClear)
for (int i = 0; i < numChannels; ++i)
mag = jmax (mag, getMagnitude (i, startSample, numSamples));
return mag;
}
float AudioSampleBuffer::getRMSLevel (const int channel,
const int startSample,
const int numSamples) const noexcept
{
jassert (isPositiveAndBelow (channel, numChannels));
jassert (startSample >= 0 && startSample + numSamples <= size);
if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear)
return 0.0f;
const float* const data = channels [channel] + startSample;
double sum = 0.0;
for (int i = 0; i < numSamples; ++i)
{
const float sample = data [i];
sum += sample * sample;
}
return (float) std::sqrt (sum / numSamples);
}

+ 639
- 92
libs/juce/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
File diff suppressed because it is too large
View File


+ 0
- 1
libs/juce/source/modules/juce_audio_basics/juce_audio_basics.cpp View File

@@ -80,7 +80,6 @@ namespace juce
{
#include "buffers/juce_AudioDataConverters.cpp"
#include "buffers/juce_AudioSampleBuffer.cpp"
#include "buffers/juce_FloatVectorOperations.cpp"
#include "effects/juce_IIRFilter.cpp"
#include "effects/juce_IIRFilterOld.cpp"


+ 1
- 1
libs/juce/source/modules/juce_audio_basics/juce_audio_basics.h View File

@@ -35,8 +35,8 @@ namespace juce
#undef Factor
#include "buffers/juce_AudioDataConverters.h"
#include "buffers/juce_AudioSampleBuffer.h"
#include "buffers/juce_FloatVectorOperations.h"
#include "buffers/juce_AudioSampleBuffer.h"
#include "effects/juce_Decibels.h"
#include "effects/juce_IIRFilter.h"
#include "effects/juce_IIRFilterOld.h"


+ 1
- 1
libs/juce/source/modules/juce_audio_basics/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_audio_basics",
"name": "JUCE audio and midi data classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 37
- 6
libs/juce/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp View File

@@ -71,6 +71,18 @@ bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const no
return noteOnTime < other.noteOnTime;
}
void SynthesiserVoice::renderNextBlock (AudioBuffer<double>& outputBuffer,
int startSample, int numSamples)
{
AudioBuffer<double> subBuffer (outputBuffer.getArrayOfWritePointers(),
outputBuffer.getNumChannels(),
startSample, numSamples);
tempBuffer.makeCopyOf (subBuffer);
renderNextBlock (tempBuffer, 0, numSamples);
subBuffer.makeCopyOf (tempBuffer);
}
//==============================================================================
Synthesiser::Synthesiser()
: sampleRate (0),
@@ -156,8 +168,11 @@ void Synthesiser::setCurrentPlaybackSampleRate (const double newRate)
}
}
void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBuffer& midiData,
int startSample, int numSamples)
template <typename floatType>
void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
const MidiBuffer& midiData,
int startSample,
int numSamples)
{
// must set the sample rate before using this!
jassert (sampleRate != 0);
@@ -174,7 +189,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
{
if (! midiIterator.getNextEvent (m, midiEventPos))
{
renderVoices (outputBuffer, startSample, numSamples);
renderVoices (outputAudio, startSample, numSamples);
return;
}
@@ -182,7 +197,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
if (samplesToNextMidiMessage >= numSamples)
{
renderVoices (outputBuffer, startSample, numSamples);
renderVoices (outputAudio, startSample, numSamples);
handleMidiEvent (m);
break;
}
@@ -193,7 +208,7 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
continue;
}
renderVoices (outputBuffer, startSample, samplesToNextMidiMessage);
renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
handleMidiEvent (m);
startSample += samplesToNextMidiMessage;
numSamples -= samplesToNextMidiMessage;
@@ -203,7 +218,23 @@ void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBu
handleMidiEvent (m);
}
void Synthesiser::renderVoices (AudioSampleBuffer& buffer, int startSample, int numSamples)
// explicit template instantiation
template void Synthesiser::processNextBlock<float> (AudioBuffer<float>& outputAudio,
const MidiBuffer& midiData,
int startSample,
int numSamples);
template void Synthesiser::processNextBlock<double> (AudioBuffer<double>& outputAudio,
const MidiBuffer& midiData,
int startSample,
int numSamples);
void Synthesiser::renderVoices (AudioBuffer<float>& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);
}
void Synthesiser::renderVoices (AudioBuffer<double>& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->renderNextBlock (buffer, startSample, numSamples);


+ 24
- 4
libs/juce/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h View File

@@ -182,9 +182,12 @@ public:
involve rendering as little as 1 sample at a time. In between rendering callbacks,
the voice's methods will be called to tell it about note and controller events.
*/
virtual void renderNextBlock (AudioSampleBuffer& outputBuffer,
virtual void renderNextBlock (AudioBuffer<float>& outputBuffer,
int startSample,
int numSamples) = 0;
virtual void renderNextBlock (AudioBuffer<double>& outputBuffer,
int startSample,
int numSamples);
/** Changes the voice's reference sample rate.
@@ -255,6 +258,8 @@ private:
SynthesiserSound::Ptr currentlyPlayingSound;
bool keyIsDown, sustainPedalDown, sostenutoPedalDown;
AudioBuffer<float> tempBuffer;
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for this method.
virtual int stopNote (bool) { return 0; }
@@ -504,10 +509,17 @@ public:
both to the audio output buffer and the midi input buffer, so any midi events
with timestamps outside the specified region will be ignored.
*/
void renderNextBlock (AudioSampleBuffer& outputAudio,
inline void renderNextBlock (AudioBuffer<float>& outputAudio,
const MidiBuffer& inputMidi,
int startSample,
int numSamples);
int numSamples)
{ processNextBlock (outputAudio, inputMidi, startSample, numSamples); }
inline void renderNextBlock (AudioBuffer<double>& outputAudio,
const MidiBuffer& inputMidi,
int startSample,
int numSamples)
{ processNextBlock (outputAudio, inputMidi, startSample, numSamples); }
/** Returns the current target sample rate at which rendering is being done.
Subclasses may need to know this so that they can pitch things correctly.
@@ -545,7 +557,9 @@ protected:
By default this just calls renderNextBlock() on each voice, but you may need
to override it to handle custom cases.
*/
virtual void renderVoices (AudioSampleBuffer& outputAudio,
virtual void renderVoices (AudioBuffer<float>& outputAudio,
int startSample, int numSamples);
virtual void renderVoices (AudioBuffer<double>& outputAudio,
int startSample, int numSamples);
/** Searches through the voices to find one that's not currently playing, and
@@ -592,6 +606,12 @@ protected:
private:
//==============================================================================
template <typename floatType>
void processNextBlock (AudioBuffer<floatType>& outputAudio,
const MidiBuffer& inputMidi,
int startSample,
int numSamples);
//==============================================================================
double sampleRate;
uint32 lastNoteOnCounter;
int minimumSubBlockSize;


+ 288
- 36
libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp View File

@@ -93,7 +93,6 @@ AudioDeviceManager::AudioDeviceManager()
numOutputChansNeeded (2),
listNeedsScanning (true),
inputLevel (0),
testSoundPosition (0),
cpuUsageMs (0),
timeToCpuScale (0)
{
@@ -589,8 +588,6 @@ void AudioDeviceManager::stopDevice()
{
if (currentAudioDevice != nullptr)
currentAudioDevice->stop();
testSound = nullptr;
}
void AudioDeviceManager::closeAudioDevice()
@@ -762,20 +759,6 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
for (int i = 0; i < numOutputChannels; ++i)
zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples);
}
if (testSound != nullptr)
{
const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
const float* const src = testSound->getReadPointer (0, testSoundPosition);
for (int i = 0; i < numOutputChannels; ++i)
for (int j = 0; j < numSamps; ++j)
outputChannelData [i][j] += src[j];
testSoundPosition += numSamps;
if (testSoundPosition >= testSound->getNumSamples())
testSound = nullptr;
}
}
void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
@@ -944,42 +927,311 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
}
//==============================================================================
void AudioDeviceManager::playTestSound()
// This is an AudioTransportSource which will own it's assigned source
class AudioSourceOwningTransportSource : public AudioTransportSource
{
{ // cunningly nested to swap, unlock and delete in that order.
ScopedPointer<AudioSampleBuffer> oldSound;
public:
AudioSourceOwningTransportSource() {}
~AudioSourceOwningTransportSource() { setSource (nullptr); }
void setSource (PositionableAudioSource* newSource)
{
if (src != newSource)
{
const ScopedLock sl (audioCallbackLock);
oldSound = testSound;
ScopedPointer<PositionableAudioSource> oldSourceDeleter (src);
src = newSource;
// tell the base class about the new source before deleting the old one
AudioTransportSource::setSource (newSource);
}
}
testSoundPosition = 0;
private:
ScopedPointer<PositionableAudioSource> src;
if (currentAudioDevice != nullptr)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource)
};
//==============================================================================
// An Audio player which will remove itself from the AudioDeviceManager's
// callback list once it finishes playing its source
class AutoRemovingSourcePlayer : public AudioSourcePlayer,
private ChangeListener
{
public:
struct DeleteOnMessageThread : public CallbackMessage
{
DeleteOnMessageThread (AutoRemovingSourcePlayer* p) : parent (p) {}
void messageCallback() override { delete parent; }
AutoRemovingSourcePlayer* parent;
};
//==============================================================================
AutoRemovingSourcePlayer (AudioDeviceManager& deviceManager, bool ownSource)
: manager (deviceManager),
deleteWhenDone (ownSource),
hasAddedCallback (false),
recursiveEntry (false)
{
}
void changeListenerCallback (ChangeBroadcaster* newSource) override
{
if (AudioTransportSource* currentTransport
= dynamic_cast<AudioTransportSource*> (getCurrentSource()))
{
ignoreUnused (newSource);
jassert (newSource == currentTransport);
if (! currentTransport->isPlaying())
{
// this will call audioDeviceStopped!
manager.removeAudioCallback (this);
}
else if (! hasAddedCallback)
{
hasAddedCallback = true;
manager.addAudioCallback (this);
}
}
}
void audioDeviceStopped() override
{
if (! recursiveEntry)
{
ScopedValueSetter<bool> s (recursiveEntry, true, false);
manager.removeAudioCallback (this);
AudioSourcePlayer::audioDeviceStopped();
if (MessageManager* mm = MessageManager::getInstanceWithoutCreating())
{
if (mm->isThisTheMessageThread())
delete this;
else
(new DeleteOnMessageThread (this))->post();
}
}
}
void setSource (AudioTransportSource* newSource)
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
const int soundLength = (int) sampleRate;
AudioSource* oldSource = getCurrentSource();
if (AudioTransportSource* oldTransport = dynamic_cast<AudioTransportSource*> (oldSource))
oldTransport->removeChangeListener (this);
const double frequency = 440.0;
const float amplitude = 0.5f;
if (newSource != nullptr)
newSource->addChangeListener (this);
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
AudioSourcePlayer::setSource (newSource);
AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength);
if (deleteWhenDone)
delete oldSource;
}
for (int i = 0; i < soundLength; ++i)
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
private:
// only allow myself to be deleted when my audio callback has been removed
~AutoRemovingSourcePlayer()
{
setSource (nullptr);
}
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
AudioDeviceManager& manager;
bool deleteWhenDone, hasAddedCallback, recursiveEntry;
const ScopedLock sl (audioCallbackLock);
testSound = newSound;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer)
};
//==============================================================================
// An AudioSource which simply outputs a buffer
class AudioSampleBufferSource : public PositionableAudioSource
{
public:
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool shouldLoop, bool ownBuffer)
: position (0),
buffer (audioBuffer),
looping (shouldLoop),
deleteWhenDone (ownBuffer)
{}
~AudioSampleBufferSource()
{
if (deleteWhenDone)
delete buffer;
}
//==============================================================================
void setNextReadPosition (int64 newPosition) override
{
jassert (newPosition >= 0);
if (looping)
newPosition = newPosition % static_cast<int64> (buffer->getNumSamples());
position = jmin (buffer->getNumSamples(), static_cast<int> (newPosition));
}
int64 getNextReadPosition() const override
{
return static_cast<int64> (position);
}
int64 getTotalLength() const override
{
return static_cast<int64> (buffer->getNumSamples());
}
bool isLooping() const override
{
return looping;
}
void setLooping (bool shouldLoop) override
{
looping = shouldLoop;
}
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
{
ignoreUnused (samplesPerBlockExpected, sampleRate);
}
void releaseResources() override
{}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
int max = jmin (buffer->getNumSamples() - position, bufferToFill.numSamples);
jassert (max >= 0);
{
int ch;
int maxInChannels = buffer->getNumChannels();
int maxOutChannels = jmin (bufferToFill.buffer->getNumChannels(),
jmax (maxInChannels, 2));
for (ch = 0; ch < maxOutChannels; ch++)
{
int inChannel = ch % maxInChannels;
if (max > 0)
bufferToFill.buffer->copyFrom (ch, bufferToFill.startSample, *buffer, inChannel, position, max);
}
for (; ch < bufferToFill.buffer->getNumChannels(); ++ch)
bufferToFill.buffer->clear (ch, bufferToFill.startSample, bufferToFill.numSamples);
}
position += max;
if (looping)
position = position % buffer->getNumSamples();
}
private:
//==============================================================================
int position;
AudioSampleBuffer* buffer;
bool looping, deleteWhenDone;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource)
};
void AudioDeviceManager::playSound (const File& file)
{
if (file.existsAsFile())
{
AudioFormatManager formatManager;
formatManager.registerBasicFormats();
playSound (formatManager.createReaderFor (file), true);
}
}
void AudioDeviceManager::playSound (const void* resourceData, size_t resourceSize)
{
if (resourceData != nullptr && resourceSize > 0)
{
AudioFormatManager formatManager;
formatManager.registerBasicFormats();
MemoryInputStream* mem = new MemoryInputStream (resourceData, resourceSize, false);
playSound (formatManager.createReaderFor (mem), true);
}
}
void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFinished)
{
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true);
}
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished)
{
if (audioSource != nullptr && currentAudioDevice != nullptr)
{
if (AudioTransportSource* transport = dynamic_cast<AudioTransportSource*> (audioSource))
{
AutoRemovingSourcePlayer* player = new AutoRemovingSourcePlayer (*this, deleteWhenFinished);
player->setSource (transport);
}
else
{
AudioTransportSource* transportSource;
if (deleteWhenFinished)
{
AudioSourceOwningTransportSource* owningTransportSource = new AudioSourceOwningTransportSource();
owningTransportSource->setSource (audioSource);
transportSource = owningTransportSource;
}
else
{
transportSource = new AudioTransportSource;
transportSource->setSource (audioSource);
}
// recursively call myself
playSound (transportSource, true);
transportSource->start();
}
}
else
{
if (deleteWhenFinished)
delete audioSource;
}
}
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished)
{
playSound (new AudioSampleBufferSource (buffer, false, deleteWhenFinished), true);
}
void AudioDeviceManager::playTestSound()
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
const int soundLength = (int) sampleRate;
const double frequency = 440.0;
const float amplitude = 0.5f;
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
AudioSampleBuffer* newSound = new AudioSampleBuffer (1, soundLength);
for (int i = 0; i < soundLength; ++i)
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
playSound (newSound, true);
}
//==============================================================================
void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement)
{
if (enableMeasurement)


+ 45
- 2
libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h View File

@@ -404,6 +404,51 @@ public:
*/
void playTestSound();
/** Plays a sound from a file. */
void playSound (const File& file);
/** Convenient method to play sound from a JUCE resource. */
void playSound (const void* resourceData, size_t resourceSize);
/** Plays the sound from an audio format reader.
If deleteWhenFinished is true then the format reader will be
automatically deleted once the sound has finished playing.
*/
void playSound (AudioFormatReader* buffer, bool deleteWhenFinished = false);
/** Plays the sound from a positionable audio source.
This will output the sound coming from a positionable audio source.
This gives you slightly more control over the sound playback compared
to the other playSound methods. For example, if you would like to
stop the sound prematurely you can call this method with a
TransportAudioSource and then call audioSource->stop. Note that,
you must call audioSource->start to start the playback, if your
audioSource is a TransportAudioSource.
The audio device manager will not hold any references to this audio
source once the audio source has stopped playing for any reason,
for example when the sound has finished playing or when you have
called audioSource->stop. Therefore, calling audioSource->start() on
a finished audioSource will not restart the sound again. If this is
desired simply call playSound with the same audioSource again.
@param audioSource the audio source to play
@param deleteWhenFinished If this is true then the audio source will
be deleted once the device manager has finished playing.
*/
void playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished = false);
/** Plays the sound from an audio sample buffer.
This will output the sound contained in an audio sample buffer. If
deleteWhenFinished is true then the audio sample buffer will be
automatically deleted once the sound has finished playing.
*/
void playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished = false);
//==============================================================================
/** Turns on level-measuring.
When enabled, the device manager will measure the peak input level
@@ -452,8 +497,6 @@ private:
mutable bool listNeedsScanning;
Atomic<int> inputLevelMeasurementEnabledCount;
double inputLevel;
ScopedPointer<AudioSampleBuffer> testSound;
int testSoundPosition;
AudioSampleBuffer tempBuffer;
struct MidiCallbackInfo


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_audio_devices",
"name": "JUCE audio and midi I/O device classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes to play and record from audio and midi i/o devices.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 294
- 18
libs/juce/source/modules/juce_audio_devices/native/juce_android_Midi.cpp View File

@@ -22,11 +22,228 @@
==============================================================================
*/
StringArray MidiOutput::getDevices()
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getJuceAndroidMidiInputDevices, "getJuceAndroidMidiInputDevices", "()[Ljava/lang/String;") \
METHOD (getJuceAndroidMidiOutputDevices, "getJuceAndroidMidiOutputDevices", "()[Ljava/lang/String;") \
METHOD (openMidiInputPortWithJuceIndex, "openMidiInputPortWithJuceIndex", "(IJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
METHOD (openMidiOutputPortWithJuceIndex, "openMidiOutputPortWithJuceIndex", "(I)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
METHOD (getInputPortNameForJuceIndex, "getInputPortNameForJuceIndex", "(I)Ljava/lang/String;") \
METHOD (getOutputPortNameForJuceIndex, "getOutputPortNameForJuceIndex", "(I)Ljava/lang/String;")
DECLARE_JNI_CLASS (MidiDeviceManager, JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager")
#undef JNI_CLASS_MEMBERS
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (start, "start", "()V" )\
METHOD (stop, "stop", "()V") \
METHOD (close, "close", "()V") \
METHOD (sendMidi, "sendMidi", "([BII)V")
DECLARE_JNI_CLASS (JuceMidiPort, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort")
#undef JNI_CLASS_MEMBERS
//==============================================================================
class AndroidMidiInput
{
public:
AndroidMidiInput (MidiInput* midiInput, int portIdx,
juce::MidiInputCallback* midiInputCallback, jobject deviceManager)
: juceMidiInput (midiInput),
callback (midiInputCallback),
midiConcatenator (2048),
javaMidiDevice (getEnv()->CallObjectMethod (deviceManager,
MidiDeviceManager.openMidiInputPortWithJuceIndex,
(jint) portIdx,
(jlong) this))
{
}
~AndroidMidiInput()
{
if (jobject d = javaMidiDevice.get())
{
getEnv()->CallVoidMethod (d, JuceMidiPort.close);
javaMidiDevice.clear();
}
}
bool isOpen() const noexcept
{
return javaMidiDevice != nullptr;
}
void start()
{
if (jobject d = javaMidiDevice.get())
getEnv()->CallVoidMethod (d, JuceMidiPort.start);
}
void stop()
{
if (jobject d = javaMidiDevice.get())
getEnv()->CallVoidMethod (d, JuceMidiPort.stop);
callback = nullptr;
}
void receive (jbyteArray byteArray, jlong offset, jint len, jlong timestamp)
{
jassert (byteArray != nullptr);
jbyte* data = getEnv()->GetByteArrayElements (byteArray, nullptr);
HeapBlock<uint8> buffer (len);
std::memcpy (buffer.getData(), data + offset, len);
midiConcatenator.pushMidiData (buffer.getData(),
len, static_cast<double> (timestamp) * 1.0e-9,
juceMidiInput, *callback);
getEnv()->ReleaseByteArrayElements (byteArray, data, 0);
}
private:
MidiInput* juceMidiInput;
MidiInputCallback* callback;
GlobalRef javaMidiDevice;
MidiDataConcatenator midiConcatenator;
};
//==============================================================================
class AndroidMidiOutput
{
StringArray devices;
public:
AndroidMidiOutput (jobject midiDevice)
: javaMidiDevice (midiDevice)
{
}
~AndroidMidiOutput()
{
if (jobject d = javaMidiDevice.get())
{
getEnv()->CallVoidMethod (d, JuceMidiPort.close);
javaMidiDevice.clear();
}
}
void send (jbyteArray byteArray, jint offset, jint len)
{
if (jobject d = javaMidiDevice.get())
getEnv()->CallVoidMethod (d,
JuceMidiPort.sendMidi,
byteArray, offset, len);
}
private:
GlobalRef javaMidiDevice;
};
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceMidiInputPort), handleReceive,
void, (JNIEnv* env, jobject device, jlong host, jbyteArray byteArray,
jint offset, jint count, jlong timestamp))
{
// Java may create a Midi thread which JUCE doesn't know about and this callback may be
// received on this thread. Java will have already created a JNI Env for this new thread,
// which we need to tell Juce about
setEnv (env);
reinterpret_cast<AndroidMidiInput*> (host)->receive (byteArray, offset, count, timestamp);
}
//==============================================================================
class AndroidMidiDeviceManager
{
public:
AndroidMidiDeviceManager ()
: deviceManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidMidiDeviceManager))
{
}
String getInputPortNameForJuceIndex (int idx)
{
if (jobject dm = deviceManager.get())
{
LocalRef<jstring> string ((jstring) getEnv()->CallObjectMethod (dm, MidiDeviceManager.getInputPortNameForJuceIndex, idx));
return juceString (string);
}
return String();
}
String getOutputPortNameForJuceIndex (int idx)
{
if (jobject dm = deviceManager.get())
{
LocalRef<jstring> string ((jstring) getEnv()->CallObjectMethod (dm, MidiDeviceManager.getOutputPortNameForJuceIndex, idx));
return juceString (string);
}
return String();
}
StringArray getDevices (bool input)
{
if (jobject dm = deviceManager.get())
{
jobjectArray jDevices
= (jobjectArray) getEnv()->CallObjectMethod (dm, input ? MidiDeviceManager.getJuceAndroidMidiInputDevices
: MidiDeviceManager.getJuceAndroidMidiOutputDevices);
// Create a local reference as converting this
// to a JUCE string will call into JNI
LocalRef<jobjectArray> devices (jDevices);
return javaStringArrayToJuce (devices);
}
return StringArray();
}
AndroidMidiInput* openMidiInputPortWithIndex (int idx, MidiInput* juceMidiInput, juce::MidiInputCallback* callback)
{
if (jobject dm = deviceManager.get())
{
ScopedPointer<AndroidMidiInput> androidMidiInput (new AndroidMidiInput (juceMidiInput, idx, callback, dm));
if (androidMidiInput->isOpen())
return androidMidiInput.release();
}
return nullptr;
}
AndroidMidiOutput* openMidiOutputPortWithIndex (int idx)
{
if (jobject dm = deviceManager.get())
if (jobject javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithJuceIndex, (jint) idx))
return new AndroidMidiOutput (javaMidiPort);
return devices;
return nullptr;
}
private:
static StringArray javaStringArrayToJuce (jobjectArray jStrings)
{
StringArray retval;
JNIEnv* env = getEnv();
const int count = env->GetArrayLength (jStrings);
for (int i = 0; i < count; ++i)
{
LocalRef<jstring> string ((jstring) env->GetObjectArrayElement (jStrings, i));
retval.add (juceString (string));
}
return retval;
}
GlobalRef deviceManager;
};
//==============================================================================
StringArray MidiOutput::getDevices()
{
AndroidMidiDeviceManager manager;
return manager.getDevices (false);
}
int MidiOutput::getDefaultDeviceIndex()
@@ -36,50 +253,109 @@ int MidiOutput::getDefaultDeviceIndex()
MidiOutput* MidiOutput::openDevice (int index)
{
if (index < 0)
return nullptr;
AndroidMidiDeviceManager manager;
String midiOutputName = manager.getOutputPortNameForJuceIndex (index);
if (midiOutputName.isEmpty())
{
// you supplied an invalid device index!
jassertfalse;
return nullptr;
}
if (AndroidMidiOutput* midiOutput = manager.openMidiOutputPortWithIndex (index))
{
MidiOutput* retval = new MidiOutput (midiOutputName);
retval->internal = midiOutput;
return retval;
}
return nullptr;
}
MidiOutput::~MidiOutput()
{
stopBackgroundThread();
delete reinterpret_cast<AndroidMidiOutput*> (internal);
}
void MidiOutput::sendMessageNow (const MidiMessage&)
void MidiOutput::sendMessageNow (const MidiMessage& message)
{
if (AndroidMidiOutput* androidMidi = reinterpret_cast<AndroidMidiOutput*>(internal))
{
JNIEnv* env = getEnv();
const int messageSize = message.getRawDataSize();
LocalRef<jbyteArray> messageContent = LocalRef<jbyteArray> (env->NewByteArray (messageSize));
jbyteArray content = messageContent.get();
jbyte* rawBytes = env->GetByteArrayElements (content, nullptr);
std::memcpy (rawBytes, message.getRawData(), messageSize);
env->ReleaseByteArrayElements (content, rawBytes, 0);
androidMidi->send (content, (jint) 0, (jint) messageSize);
}
}
//==============================================================================
MidiInput::MidiInput (const String& name_)
: name (name_),
internal (0)
MidiInput::MidiInput (const String& nm) : name (nm)
{
}
MidiInput::~MidiInput()
StringArray MidiInput::getDevices()
{
AndroidMidiDeviceManager manager;
return manager.getDevices (true);
}
void MidiInput::start()
int MidiInput::getDefaultDeviceIndex()
{
return 0;
}
void MidiInput::stop()
MidiInput* MidiInput::openDevice (int index, juce::MidiInputCallback* callback)
{
if (index < 0)
return nullptr;
AndroidMidiDeviceManager manager;
String midiInputName = manager.getInputPortNameForJuceIndex (index);
if (midiInputName.isEmpty())
{
// you supplied an invalid device index!
jassertfalse;
return nullptr;
}
ScopedPointer<MidiInput> midiInput (new MidiInput (midiInputName));
midiInput->internal = manager.openMidiInputPortWithIndex (index, midiInput, callback);
return midiInput->internal != nullptr ? midiInput.release()
: nullptr;
}
int MidiInput::getDefaultDeviceIndex()
void MidiInput::start()
{
return 0;
if (AndroidMidiInput* mi = reinterpret_cast<AndroidMidiInput*> (internal))
mi->start();
}
StringArray MidiInput::getDevices()
void MidiInput::stop()
{
StringArray devs;
return devs;
if (AndroidMidiInput* mi = reinterpret_cast<AndroidMidiInput*> (internal))
mi->stop();
}
MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
MidiInput::~MidiInput()
{
return nullptr;
delete reinterpret_cast<AndroidMidiInput*> (internal);
}

+ 218
- 99
libs/juce/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp View File

@@ -32,7 +32,7 @@ bool isOpenSLAvailable()
//==============================================================================
class OpenSLAudioIODevice : public AudioIODevice,
public Thread
private Thread
{
public:
OpenSLAudioIODevice (const String& deviceName)
@@ -81,13 +81,28 @@ public:
Array<double> getAvailableSampleRates() override
{
static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 };
return Array<double> (rates, numElementsInArray (rates));
Array<double> retval (rates, numElementsInArray (rates));
// make sure the native sample rate is pafrt of the list
double native = getNativeSampleRate();
if (native != 0.0 && ! retval.contains (native))
retval.add (native);
return retval;
}
Array<int> getAvailableBufferSizes() override
{
static const int sizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size
return Array<int> (sizes, numElementsInArray (sizes));
// we need to offer the lowest possible buffer size which
// is the native buffer size
const int defaultNumMultiples = 8;
const int nativeBufferSize = getNativeBufferSize();
Array<int> retval;
for (int i = 1; i < defaultNumMultiples; ++i)
retval.add (i * nativeBufferSize);
return retval;
}
String open (const BigInteger& inputChannels,
@@ -116,8 +131,28 @@ public:
outputBuffer.setSize (jmax (1, numOutputChannels), actualBufferSize);
outputBuffer.clear();
recorder = engine.createRecorder (numInputChannels, sampleRate);
player = engine.createPlayer (numOutputChannels, sampleRate);
const int audioBuffersToEnqueue = hasLowLatencyAudioPath ? buffersToEnqueueForLowLatency
: buffersToEnqueueSlowAudio;
DBG ("OpenSL: numInputChannels = " << numInputChannels
<< ", numOutputChannels = " << numOutputChannels
<< ", nativeBufferSize = " << getNativeBufferSize()
<< ", nativeSampleRate = " << getNativeSampleRate()
<< ", actualBufferSize = " << actualBufferSize
<< ", audioBuffersToEnqueue = " << audioBuffersToEnqueue
<< ", sampleRate = " << sampleRate);
if (numInputChannels > 0)
recorder = engine.createRecorder (numInputChannels, sampleRate,
audioBuffersToEnqueue, actualBufferSize);
if (numOutputChannels > 0)
player = engine.createPlayer (numOutputChannels, sampleRate,
audioBuffersToEnqueue, actualBufferSize);
// pre-fill buffers
for (int i = 0; i < audioBuffersToEnqueue; ++i)
processBuffers();
startThread (8);
@@ -134,18 +169,30 @@ public:
player = nullptr;
}
int getDefaultBufferSize() override { return 1024; }
int getOutputLatencyInSamples() override { return outputLatency; }
int getInputLatencyInSamples() override { return inputLatency; }
bool isOpen() override { return deviceOpen; }
int getCurrentBufferSizeSamples() override { return actualBufferSize; }
int getCurrentBitDepth() override { return 16; }
double getCurrentSampleRate() override { return sampleRate; }
BigInteger getActiveOutputChannels() const override { return activeOutputChans; }
BigInteger getActiveInputChannels() const override { return activeInputChans; }
String getLastError() override { return lastError; }
bool isPlaying() override { return callback != nullptr; }
int getDefaultBufferSize() override
{
// Only on a Pro-Audio device will we set the lowest possible buffer size
// by default. We need to be more conservative on other devices
// as they may be low-latency, but still have a crappy CPU.
return (isProAudioDevice() ? 1 : 6)
* defaultBufferSizeIsMultipleOfNative * getNativeBufferSize();
}
double getCurrentSampleRate() override
{
return (sampleRate == 0.0 ? getNativeSampleRate() : sampleRate);
}
void start (AudioIODeviceCallback* newCallback) override
{
stop();
@@ -184,6 +231,55 @@ private:
struct Player;
struct Recorder;
enum
{
// The number of buffers to enqueue needs to be at least two for the audio to use the low-latency
// audio path (see "Performance" section in ndk/docs/Additional_library_docs/opensles/index.html)
buffersToEnqueueForLowLatency = 2,
buffersToEnqueueSlowAudio = 4,
defaultBufferSizeIsMultipleOfNative = 1
};
//==================================================================================================
static String audioManagerGetProperty (const String& property)
{
const LocalRef<jstring> jProperty (javaString (property));
const LocalRef<jstring> text ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
jProperty.get()));
if (text.get() != 0)
return juceString (text);
return String();
}
static bool androidHasSystemFeature (const String& property)
{
const LocalRef<jstring> jProperty (javaString (property));
return android.activity.callBooleanMethod (JuceAppActivity.hasSystemFeature, jProperty.get());
}
static double getNativeSampleRate()
{
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
}
static int getNativeBufferSize()
{
const int val = audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER").getIntValue();
return val > 0 ? val : 512;
}
static bool isProAudioDevice()
{
return androidHasSystemFeature ("android.hardware.audio.pro");
}
static bool hasLowLatencyAudioPath()
{
return androidHasSystemFeature ("android.hardware.audio.low_latency");
}
//==================================================================================================
AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback)
{
const ScopedLock sl (callbackLock);
@@ -192,29 +288,45 @@ private:
return oldCallback;
}
void run() override
void processBuffers()
{
if (recorder != nullptr) recorder->start();
if (player != nullptr) player->start();
if (recorder != nullptr)
recorder->readNextBlock (inputBuffer, *this);
while (! threadShouldExit())
{
if (player != nullptr) player->writeBuffer (outputBuffer, *this);
if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this);
const ScopedLock sl (callbackLock);
if (callback != nullptr)
{
callback->audioDeviceIOCallback (numInputChannels > 0 ? inputBuffer.getArrayOfReadPointers() : nullptr, numInputChannels,
numOutputChannels > 0 ? outputBuffer.getArrayOfWritePointers() : nullptr, numOutputChannels,
actualBufferSize);
}
else
{
outputBuffer.clear();
}
}
if (player != nullptr)
player->writeBuffer (outputBuffer, *this);
}
void run() override
{
setThreadToAudioPriority ();
if (recorder != nullptr) recorder->start();
if (player != nullptr) player->start();
while (! threadShouldExit())
processBuffers();
}
void setThreadToAudioPriority ()
{
// see android.os.Process.THREAD_PRIORITY_AUDIO
const int THREAD_PRIORITY_AUDIO = -16;
jint priority = THREAD_PRIORITY_AUDIO;
if (priority != android.activity.callIntMethod (JuceAppActivity.setCurrentThreadPriority, (jint) priority))
DBG ("Unable to set audio thread priority: priority is still " << priority);
}
//==================================================================================================
@@ -225,7 +337,8 @@ private:
{
if (library.open ("libOpenSLES.so"))
{
typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*);
typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*,
SLuint32, const SLInterfaceID*, const SLboolean*);
if (CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine"))
{
@@ -252,21 +365,21 @@ private:
if (engineObject != nullptr) (*engineObject)->Destroy (engineObject);
}
Player* createPlayer (const int numChannels, const int sampleRate)
Player* createPlayer (const int numChannels, const int sampleRate, const int numBuffers, const int bufferSize)
{
if (numChannels <= 0)
return nullptr;
ScopedPointer<Player> player (new Player (numChannels, sampleRate, *this));
ScopedPointer<Player> player (new Player (numChannels, sampleRate, *this, numBuffers, bufferSize));
return player->openedOk() ? player.release() : nullptr;
}
Recorder* createRecorder (const int numChannels, const int sampleRate)
Recorder* createRecorder (const int numChannels, const int sampleRate, const int numBuffers, const int bufferSize)
{
if (numChannels <= 0)
return nullptr;
ScopedPointer<Recorder> recorder (new Recorder (numChannels, sampleRate, *this));
ScopedPointer<Recorder> recorder (new Recorder (numChannels, sampleRate, *this, numBuffers, bufferSize));
return recorder->openedOk() ? recorder.release() : nullptr;
}
@@ -288,12 +401,13 @@ private:
//==================================================================================================
struct BufferList
{
BufferList (const int numChannels_)
: numChannels (numChannels_), bufferSpace (numChannels_ * numSamples * numBuffers), nextBlock (0)
BufferList (const int numChannels_, const int numBuffers_, const int numSamples_)
: numChannels (numChannels_), numBuffers (numBuffers_), numSamples (numSamples_),
bufferSpace (numChannels_ * numSamples * numBuffers), nextBlock (0)
{
}
int16* waitForFreeBuffer (Thread& threadToCheck)
int16* waitForFreeBuffer (Thread& threadToCheck) noexcept
{
while (numBlocksOut.get() == numBuffers)
{
@@ -306,7 +420,7 @@ private:
return getNextBuffer();
}
int16* getNextBuffer()
int16* getNextBuffer() noexcept
{
if (++nextBlock == numBuffers)
nextBlock = 0;
@@ -314,13 +428,12 @@ private:
return bufferSpace + nextBlock * numChannels * numSamples;
}
void bufferReturned() { --numBlocksOut; dataArrived.signal(); }
void bufferSent() { ++numBlocksOut; dataArrived.signal(); }
void bufferReturned() noexcept { --numBlocksOut; dataArrived.signal(); }
void bufferSent() noexcept { ++numBlocksOut; dataArrived.signal(); }
int getBufferSizeBytes() const { return numChannels * numSamples * sizeof (int16); }
int getBufferSizeBytes() const noexcept { return numChannels * numSamples * sizeof (int16); }
const int numChannels;
enum { numSamples = 256, numBuffers = 16 };
const int numChannels, numBuffers, numSamples;
private:
HeapBlock<int16> bufferSpace;
@@ -332,24 +445,23 @@ private:
//==================================================================================================
struct Player
{
Player (int numChannels, int sampleRate, Engine& engine)
Player (int numChannels, int sampleRate, Engine& engine, int playerNumBuffers, int playerBufferSize)
: playerObject (nullptr), playerPlay (nullptr), playerBufferQueue (nullptr),
bufferList (numChannels)
bufferList (numChannels, playerNumBuffers, playerBufferSize)
{
jassert (numChannels == 2);
SLDataFormat_PCM pcmFormat =
{
SL_DATAFORMAT_PCM,
(SLuint32) numChannels,
(SLuint32) (sampleRate * 1000), // (sample rate units are millihertz)
(SLuint32) (sampleRate * 1000),
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
(numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT),
SL_BYTEORDER_LITTLEENDIAN
};
SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers };
SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
static_cast<SLuint32> (bufferList.numBuffers) };
SLDataSource audioSrc = { &bufferQueue, &pcmFormat };
SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, engine.outputMixObject };
@@ -385,10 +497,11 @@ private:
void start()
{
jassert (openedOk());
check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_PLAYING));
}
void writeBuffer (const AudioSampleBuffer& buffer, Thread& thread)
void writeBuffer (const AudioSampleBuffer& buffer, Thread& thread) noexcept
{
jassert (buffer.getNumChannels() == bufferList.numChannels);
jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers);
@@ -398,26 +511,27 @@ private:
while (numSamples > 0)
{
int16* const destBuffer = bufferList.waitForFreeBuffer (thread);
if (destBuffer == nullptr)
break;
for (int i = 0; i < bufferList.numChannels; ++i)
if (int16* const destBuffer = bufferList.waitForFreeBuffer (thread))
{
typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> DstSampleType;
typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SrcSampleType;
for (int i = 0; i < bufferList.numChannels; ++i)
{
typedef AudioData::Pointer<AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> DstSampleType;
typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SrcSampleType;
DstSampleType dstData (destBuffer + i, bufferList.numChannels);
SrcSampleType srcData (buffer.getReadPointer (i, offset));
dstData.convertSamples (srcData, bufferList.numSamples);
}
DstSampleType dstData (destBuffer + i, bufferList.numChannels);
SrcSampleType srcData (buffer.getReadPointer (i, offset));
dstData.convertSamples (srcData, bufferList.numSamples);
}
check ((*playerBufferQueue)->Enqueue (playerBufferQueue, destBuffer, bufferList.getBufferSizeBytes()));
bufferList.bufferSent();
enqueueBuffer (destBuffer);
numSamples -= bufferList.numSamples;
offset += bufferList.numSamples;
numSamples -= bufferList.numSamples;
offset += bufferList.numSamples;
}
else
{
break;
}
}
}
@@ -428,10 +542,16 @@ private:
BufferList bufferList;
static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context)
void enqueueBuffer (int16* buffer) noexcept
{
check ((*playerBufferQueue)->Enqueue (playerBufferQueue, buffer, bufferList.getBufferSizeBytes()));
bufferList.bufferSent();
}
static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) noexcept
{
jassert (queue == static_cast <Player*> (context)->playerBufferQueue); (void) queue;
static_cast <Player*> (context)->bufferList.bufferReturned();
jassert (queue == static_cast<Player*> (context)->playerBufferQueue); (void) queue;
static_cast<Player*> (context)->bufferList.bufferReturned();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player)
@@ -440,13 +560,11 @@ private:
//==================================================================================================
struct Recorder
{
Recorder (int numChannels, int sampleRate, Engine& engine)
Recorder (int numChannels, int sampleRate, Engine& engine, const int numBuffers, const int numSamples)
: recorderObject (nullptr), recorderRecord (nullptr),
recorderBufferQueue (nullptr), configObject (nullptr),
bufferList (numChannels)
bufferList (numChannels, numBuffers, numSamples)
{
jassert (numChannels == 1); // STEREO doesn't always work!!
SLDataFormat_PCM pcmFormat =
{
SL_DATAFORMAT_PCM,
@@ -461,7 +579,8 @@ private:
SLDataLocator_IODevice ioDevice = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr };
SLDataSource audioSrc = { &ioDevice, nullptr };
SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers };
SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
static_cast<SLuint32> (bufferList.numBuffers) };
SLDataSink audioSink = { &bufferQueue, &pcmFormat };
const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
@@ -474,16 +593,14 @@ private:
{
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord));
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue));
check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDCONFIGURATION, &configObject));
// not all android versions seem to have a config object
SLresult result = (*recorderObject)->GetInterface (recorderObject,
*engine.SL_IID_ANDROIDCONFIGURATION, &configObject);
if (result != SL_RESULT_SUCCESS)
configObject = nullptr;
check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this));
check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED));
for (int i = bufferList.numBuffers; --i >= 0;)
{
int16* const buffer = bufferList.getNextBuffer();
jassert (buffer != nullptr);
enqueueBuffer (buffer);
}
}
}
}
@@ -519,25 +636,27 @@ private:
while (numSamples > 0)
{
int16* const srcBuffer = bufferList.waitForFreeBuffer (thread);
if (srcBuffer == nullptr)
break;
for (int i = 0; i < bufferList.numChannels; ++i)
if (int16* const srcBuffer = bufferList.waitForFreeBuffer (thread))
{
typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DstSampleType;
typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const> SrcSampleType;
for (int i = 0; i < bufferList.numChannels; ++i)
{
typedef AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DstSampleType;
typedef AudioData::Pointer<AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const> SrcSampleType;
DstSampleType dstData (buffer.getWritePointer (i, offset));
SrcSampleType srcData (srcBuffer + i, bufferList.numChannels);
dstData.convertSamples (srcData, bufferList.numSamples);
}
DstSampleType dstData (buffer.getWritePointer (i, offset));
SrcSampleType srcData (srcBuffer + i, bufferList.numChannels);
dstData.convertSamples (srcData, bufferList.numSamples);
}
enqueueBuffer (srcBuffer);
enqueueBuffer (srcBuffer);
numSamples -= bufferList.numSamples;
offset += bufferList.numSamples;
numSamples -= bufferList.numSamples;
offset += bufferList.numSamples;
}
else
{
break;
}
}
}
@@ -558,16 +677,16 @@ private:
BufferList bufferList;
void enqueueBuffer (int16* buffer)
void enqueueBuffer (int16* buffer) noexcept
{
check ((*recorderBufferQueue)->Enqueue (recorderBufferQueue, buffer, bufferList.getBufferSizeBytes()));
bufferList.bufferSent();
}
static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context)
static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) noexcept
{
jassert (queue == static_cast <Recorder*> (context)->recorderBufferQueue); (void) queue;
static_cast <Recorder*> (context)->bufferList.bufferReturned();
jassert (queue == static_cast<Recorder*> (context)->recorderBufferQueue); (void) queue;
static_cast<Recorder*> (context)->bufferList.bufferReturned();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Recorder)
@@ -581,7 +700,7 @@ private:
ScopedPointer<Recorder> recorder;
//==============================================================================
static bool check (const SLresult result)
static bool check (const SLresult result) noexcept
{
jassert (result == SL_RESULT_SUCCESS);
return result == SL_RESULT_SUCCESS;
@@ -598,14 +717,14 @@ public:
OpenSLAudioDeviceType() : AudioIODeviceType (openSLTypeName) {}
//==============================================================================
void scanForDevices() {}
StringArray getDeviceNames (bool wantInputNames) const { return StringArray (openSLTypeName); }
int getDefaultDeviceIndex (bool forInput) const { return 0; }
int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; }
bool hasSeparateInputsAndOutputs() const { return false; }
void scanForDevices() override {}
StringArray getDeviceNames (bool wantInputNames) const override { return StringArray (openSLTypeName); }
int getDefaultDeviceIndex (bool forInput) const override { return 0; }
int getIndexOfDevice (AudioIODevice* device, bool asInput) const override { return device != nullptr ? 0 : -1; }
bool hasSeparateInputsAndOutputs() const override { return false; }
AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName)
const String& inputDeviceName) override
{
ScopedPointer<OpenSLAudioIODevice> dev;


+ 2
- 2
libs/juce/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp View File

@@ -233,7 +233,7 @@ public:
for (int i = 0; i < numStreams; ++i)
{
const AudioBuffer& b = bufList->mBuffers[i];
const ::AudioBuffer& b = bufList->mBuffers[i];
for (unsigned int j = 0; j < b.mNumberChannels; ++j)
{
@@ -1945,7 +1945,7 @@ private:
for (int i = 0; i < numStreams; ++i)
{
const AudioBuffer& b = bufList->mBuffers[i];
const ::AudioBuffer& b = bufList->mBuffers[i];
total += b.mNumberChannels;
}
}


+ 1
- 1
libs/juce/source/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp View File

@@ -380,7 +380,7 @@ public:
&destinationAudioFormat);
if (status == noErr)
{
bufferList.malloc (1, sizeof (AudioBufferList) + numChannels * sizeof (AudioBuffer));
bufferList.malloc (1, sizeof (AudioBufferList) + numChannels * sizeof (::AudioBuffer));
bufferList->mNumberBuffers = numChannels;
ok = true;
}


+ 1
- 1
libs/juce/source/modules/juce_audio_formats/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_audio_formats",
"name": "JUCE audio file format codecs",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for reading and writing various audio file formats.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 9
- 0
libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp View File

@@ -45,6 +45,11 @@
#pragma clang diagnostic ignored "-Wsign-conversion"
#endif
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable : 4127)
#endif
#include "AAX_Exports.cpp"
#include "AAX_ICollection.h"
#include "AAX_IComponentDescriptor.h"
@@ -63,6 +68,10 @@
#include "AAX_UtilsNative.h"
#include "AAX_Enums.h"
#ifdef _MSC_VER
#pragma warning (pop)
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif


+ 5
- 7
libs/juce/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm View File

@@ -504,10 +504,8 @@ public:
return numChannelConfigs;
}
UInt32 GetAudioChannelLayout (AudioUnitScope scope,
AudioUnitElement element,
AudioChannelLayout *outLayoutPtr,
Boolean &outWritable) override
UInt32 GetAudioChannelLayout (AudioUnitScope scope, AudioUnitElement element,
AudioChannelLayout* outLayoutPtr, Boolean& outWritable) override
{
// fallback to old code if this plug-in does not have multi channel IO
if (! hasMultiChannelConfiguration())
@@ -886,7 +884,7 @@ public:
for (unsigned int i = 0; i < outBuffer.mNumberBuffers; ++i)
{
AudioBuffer& buf = outBuffer.mBuffers[i];
::AudioBuffer& buf = outBuffer.mBuffers[i];
if (buf.mNumberChannels == 1)
{
@@ -908,7 +906,7 @@ public:
for (unsigned int i = 0; i < inBuffer.mNumberBuffers; ++i)
{
const AudioBuffer& buf = inBuffer.mBuffers[i];
const ::AudioBuffer& buf = inBuffer.mBuffers[i];
if (buf.mNumberChannels == 1)
{
@@ -1027,7 +1025,7 @@ public:
for (unsigned int i = 0; i < outBuffer.mNumberBuffers; ++i)
{
AudioBuffer& buf = outBuffer.mBuffers[i];
::AudioBuffer& buf = outBuffer.mBuffers[i];
if (buf.mNumberChannels > 1)
{


+ 97
- 26
libs/juce/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp View File

@@ -69,6 +69,11 @@
#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
#endif
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable : 4458)
#endif
/* These files come with the Steinberg VST SDK - to get them, you'll need to
visit the Steinberg website and agree to whatever is currently required to
get them. The best version to get is the VST3 SDK, which also contains
@@ -96,6 +101,10 @@
namespace juce { extern Steinberg::FUID getJuceVST3ComponentIID(); }
#endif
#ifdef _MSC_VER
#pragma warning (pop)
#endif
#ifdef __clang__
#pragma clang diagnostic pop
#endif
@@ -243,6 +252,27 @@ class JuceVSTWrapper : public AudioEffectX,
private Timer,
private AsyncUpdater
{
private:
//==============================================================================
template <typename FloatType>
struct VstTempBuffers
{
VstTempBuffers() {}
~VstTempBuffers() { release(); }
void release() noexcept
{
for (int i = tempChannels.size(); --i >= 0;)
delete[] (tempChannels.getUnchecked(i));
tempChannels.clear();
}
HeapBlock<FloatType*> channels;
Array<FloatType*> tempChannels; // see note in processReplacing()
juce::AudioBuffer<FloatType> processTempBuffer;
};
public:
//==============================================================================
JuceVSTWrapper (audioMasterCallback audioMasterCB, AudioProcessor* const af)
@@ -264,7 +294,6 @@ public:
#else
useNSView (false),
#endif
processTempBuffer (1, 1),
hostWindow (0)
{
filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0);
@@ -280,6 +309,7 @@ public:
setNumOutputs (numOutChans);
canProcessReplacing (true);
canDoubleReplacing (filter->supportsDoublePrecisionProcessing());
isSynth ((JucePlugin_IsSynth) != 0);
setInitialDelay (filter->getLatencySamples());
@@ -306,7 +336,6 @@ public:
jassert (editorComp == 0);
channels.free();
deleteTempChannels();
jassert (activePlugins.contains (this));
@@ -496,23 +525,27 @@ public:
void process (float** inputs, float** outputs, VstInt32 numSamples)
{
VstTempBuffers<float>& tmpBuffers = floatTempBuffers;
const int numIn = numInChans;
const int numOut = numOutChans;
processTempBuffer.setSize (numIn, numSamples, false, false, true);
tmpBuffers.processTempBuffer.setSize (numIn, numSamples, false, false, true);
for (int i = numIn; --i >= 0;)
processTempBuffer.copyFrom (i, 0, outputs[i], numSamples);
tmpBuffers.processTempBuffer.copyFrom (i, 0, outputs[i], numSamples);
processReplacing (inputs, outputs, numSamples);
AudioSampleBuffer dest (outputs, numOut, numSamples);
for (int i = jmin (numIn, numOut); --i >= 0;)
dest.addFrom (i, 0, processTempBuffer, i, 0, numSamples);
dest.addFrom (i, 0, tmpBuffers.processTempBuffer, i, 0, numSamples);
}
void processReplacing (float** inputs, float** outputs, VstInt32 numSamples) override
template <typename FloatType>
void internalProcessReplacing (FloatType** inputs, FloatType** outputs,
VstInt32 numSamples, VstTempBuffers<FloatType>& tmpBuffers)
{
if (firstProcessCallback)
{
@@ -557,7 +590,7 @@ public:
int i;
for (i = 0; i < numOut; ++i)
{
float* chan = tempChannels.getUnchecked(i);
FloatType* chan = tmpBuffers.tempChannels.getUnchecked(i);
if (chan == nullptr)
{
@@ -570,24 +603,24 @@ public:
{
if (outputs[j] == chan)
{
chan = new float [blockSize * 2];
tempChannels.set (i, chan);
chan = new FloatType [blockSize * 2];
tmpBuffers.tempChannels.set (i, chan);
break;
}
}
}
if (i < numIn && chan != inputs[i])
memcpy (chan, inputs[i], sizeof (float) * (size_t) numSamples);
memcpy (chan, inputs[i], sizeof (FloatType) * (size_t) numSamples);
channels[i] = chan;
tmpBuffers.channels[i] = chan;
}
for (; i < numIn; ++i)
channels[i] = inputs[i];
tmpBuffers.channels[i] = inputs[i];
{
AudioSampleBuffer chans (channels, jmax (numIn, numOut), numSamples);
AudioBuffer<FloatType> chans (tmpBuffers.channels, jmax (numIn, numOut), numSamples);
if (isBypassed)
filter->processBlockBypassed (chans, midiEvents);
@@ -597,8 +630,8 @@ public:
// copy back any temp channels that may have been used..
for (i = 0; i < numOut; ++i)
if (const float* const chan = tempChannels.getUnchecked(i))
memcpy (outputs[i], chan, sizeof (float) * (size_t) numSamples);
if (const FloatType* const chan = tmpBuffers.tempChannels.getUnchecked(i))
memcpy (outputs[i], chan, sizeof (FloatType) * (size_t) numSamples);
}
}
@@ -644,16 +677,47 @@ public:
}
}
void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames) override
{
jassert (! filter->isUsingDoublePrecision());
internalProcessReplacing (inputs, outputs, sampleFrames, floatTempBuffers);
}
void processDoubleReplacing (double** inputs, double** outputs, VstInt32 sampleFrames) override
{
jassert (filter->isUsingDoublePrecision());
internalProcessReplacing (inputs, outputs, sampleFrames, doubleTempBuffers);
}
//==============================================================================
VstInt32 startProcess() override { return 0; }
VstInt32 stopProcess() override { return 0; }
//==============================================================================
bool setProcessPrecision (VstInt32 vstPrecision) override
{
if (! isProcessing)
{
if (filter != nullptr)
{
filter->setProcessingPrecision (vstPrecision == kVstProcessPrecision64 && filter->supportsDoublePrecisionProcessing()
? AudioProcessor::doublePrecision
: AudioProcessor::singlePrecision);
return true;
}
}
return false;
}
void resume() override
{
if (filter != nullptr)
{
isProcessing = true;
channels.calloc ((size_t) (numInChans + numOutChans));
floatTempBuffers.channels.calloc ((size_t) (numInChans + numOutChans));
doubleTempBuffers.channels.calloc ((size_t) (numInChans + numOutChans));
double rate = getSampleRate();
jassert (rate > 0);
@@ -695,7 +759,8 @@ public:
outgoingEvents.freeEvents();
isProcessing = false;
channels.free();
floatTempBuffers.channels.free();
doubleTempBuffers.channels.free();
deleteTempChannels();
}
@@ -1453,9 +1518,8 @@ private:
int numInChans, numOutChans;
bool isProcessing, isBypassed, hasShutdown, isInSizeWindow, firstProcessCallback;
bool shouldDeleteEditor, useNSView;
HeapBlock<float*> channels;
Array<float*> tempChannels; // see note in processReplacing()
AudioSampleBuffer processTempBuffer;
VstTempBuffers<float> floatTempBuffers;
VstTempBuffers<double> doubleTempBuffers;
#if JUCE_MAC
void* hostWindow;
@@ -1513,15 +1577,22 @@ private:
#endif
//==============================================================================
void deleteTempChannels()
template <typename FloatType>
void deleteTempChannels (VstTempBuffers<FloatType>& tmpBuffers)
{
for (int i = tempChannels.size(); --i >= 0;)
delete[] (tempChannels.getUnchecked(i));
tempChannels.clear();
tmpBuffers.release();
if (filter != nullptr)
tempChannels.insertMultiple (0, nullptr, filter->getNumInputChannels() + filter->getNumOutputChannels());
{
int numChannels = filter->getNumInputChannels() + filter->getNumOutputChannels();
tmpBuffers.tempChannels.insertMultiple (0, nullptr, numChannels);
}
}
void deleteTempChannels()
{
deleteTempChannels (floatTempBuffers);
deleteTempChannels (doubleTempBuffers);
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper)


+ 122
- 54
libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp View File

@@ -743,6 +743,14 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVST3EditController)
};
namespace
{
template <typename FloatType> struct AudioBusPointerHelper {};
template <> struct AudioBusPointerHelper<float> { static inline float** impl (Vst::AudioBusBuffers& data) noexcept { return data.channelBuffers32; } };
template <> struct AudioBusPointerHelper<double> { static inline double** impl (Vst::AudioBusBuffers& data) noexcept { return data.channelBuffers64; } };
}
//==============================================================================
class JuceVST3Component : public Vst::IComponent,
public Vst::IAudioProcessor,
@@ -956,8 +964,8 @@ public:
? (int) processSetup.maxSamplesPerBlock
: bufferSize;
channelList.clear();
channelList.insertMultiple (0, nullptr, jmax (JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) + 1);
allocateChannelLists (channelListFloat);
allocateChannelLists (channelListDouble);
preparePlugin (sampleRate, bufferSize);
}
@@ -1440,7 +1448,9 @@ public:
tresult PLUGIN_API canProcessSampleSize (Steinberg::int32 symbolicSampleSize) override
{
return symbolicSampleSize == Vst::kSample32 ? kResultTrue : kResultFalse;
return (symbolicSampleSize == Vst::kSample32
|| (getPluginInstance().supportsDoublePrecisionProcessing()
&& symbolicSampleSize == Vst::kSample64)) ? kResultTrue : kResultFalse;
}
Steinberg::uint32 PLUGIN_API getLatencySamples() override
@@ -1456,6 +1466,10 @@ public:
processSetup = newSetup;
processContext.sampleRate = processSetup.sampleRate;
getPluginInstance().setProcessingPrecision (newSetup.symbolicSampleSize == Vst::kSample64
? AudioProcessor::doublePrecision
: AudioProcessor::singlePrecision);
preparePlugin (processSetup.sampleRate, processSetup.maxSamplesPerBlock);
return kResultTrue;
@@ -1532,9 +1546,13 @@ public:
tresult PLUGIN_API process (Vst::ProcessData& data) override
{
if (pluginInstance == nullptr)
return kResultFalse;
if ((processSetup.symbolicSampleSize == Vst::kSample64) != pluginInstance->isUsingDoublePrecision())
return kResultFalse;
if (data.processContext != nullptr)
processContext = *data.processContext;
else
@@ -1551,60 +1569,19 @@ public:
const int numMidiEventsComingIn = midiBuffer.getNumEvents();
#endif
const int numInputChans = (data.inputs != nullptr && data.inputs[0].channelBuffers32 != nullptr) ? (int) data.inputs[0].numChannels : 0;
const int numOutputChans = (data.outputs != nullptr && data.outputs[0].channelBuffers32 != nullptr) ? (int) data.outputs[0].numChannels : 0;
int totalChans = 0;
while (totalChans < numInputChans)
if (getHostType().isWavelab())
{
channelList.set (totalChans, data.inputs[0].channelBuffers32[totalChans]);
++totalChans;
}
while (totalChans < numOutputChans)
{
channelList.set (totalChans, data.outputs[0].channelBuffers32[totalChans]);
++totalChans;
}
AudioSampleBuffer buffer;
if (totalChans != 0)
buffer.setDataToReferTo (channelList.getRawDataPointer(), totalChans, (int) data.numSamples);
else if (getHostType().isWavelab()
&& pluginInstance->getNumInputChannels() + pluginInstance->getNumOutputChannels() > 0)
return kResultFalse;
{
const ScopedLock sl (pluginInstance->getCallbackLock());
pluginInstance->setNonRealtime (data.processMode == Vst::kOffline);
const int numInputChans = (data.inputs != nullptr && data.inputs[0].channelBuffers32 != nullptr) ? (int) data.inputs[0].numChannels : 0;
const int numOutputChans = (data.outputs != nullptr && data.outputs[0].channelBuffers32 != nullptr) ? (int) data.outputs[0].numChannels : 0;
if (data.inputParameterChanges != nullptr)
processParameterChanges (*data.inputParameterChanges);
if (pluginInstance->isSuspended())
{
buffer.clear();
}
else
{
if (isBypassed())
pluginInstance->processBlockBypassed (buffer, midiBuffer);
else
pluginInstance->processBlock (buffer, midiBuffer);
}
if ((pluginInstance->getNumInputChannels() + pluginInstance->getNumOutputChannels()) > 0
&& (numInputChans + numOutputChans) == 0)
return kResultFalse;
}
for (int i = 0; i < numOutputChans; ++i)
FloatVectorOperations::copy (data.outputs[0].channelBuffers32[i], buffer.getReadPointer (i), (int) data.numSamples);
// clear extra busses..
if (data.outputs != nullptr)
for (int i = 1; i < data.numOutputs; ++i)
for (int f = 0; f < data.outputs[i].numChannels; ++f)
FloatVectorOperations::clear (data.outputs[i].channelBuffers32[f], (int) data.numSamples);
if (processSetup.symbolicSampleSize == Vst::kSample32) processAudio<float> (data, channelListFloat);
else if (processSetup.symbolicSampleSize == Vst::kSample64) processAudio<double> (data, channelListDouble);
else jassertfalse;
#if JucePlugin_ProducesMidiOutput
if (data.outputEvents != nullptr)
@@ -1648,7 +1625,8 @@ private:
Vst::BusList audioInputs, audioOutputs, eventInputs, eventOutputs;
MidiBuffer midiBuffer;
Array<float*> channelList;
Array<float*> channelListFloat;
Array<double*> channelListDouble;
ScopedJuceInitialiser_GUI libraryInitialiser;
@@ -1656,6 +1634,52 @@ private:
static const char* kJucePrivateDataIdentifier;
//==============================================================================
template <typename FloatType>
void processAudio (Vst::ProcessData& data, Array<FloatType*>& channelList)
{
const int totalChans = prepareChannelLists (channelList, data);
AudioBuffer<FloatType> buffer;
if (totalChans != 0)
buffer.setDataToReferTo (channelList.getRawDataPointer(), totalChans, (int) data.numSamples);
{
const ScopedLock sl (pluginInstance->getCallbackLock());
pluginInstance->setNonRealtime (data.processMode == Vst::kOffline);
if (data.inputParameterChanges != nullptr)
processParameterChanges (*data.inputParameterChanges);
if (pluginInstance->isSuspended())
{
buffer.clear();
}
else
{
if (isBypassed())
pluginInstance->processBlockBypassed (buffer, midiBuffer);
else
pluginInstance->processBlock (buffer, midiBuffer);
}
}
if (data.outputs != nullptr)
{
for (int i = 0; i < data.numOutputs; ++i)
FloatVectorOperations::copy (getPointerForAudioBus<FloatType> (data.outputs[0])[i],
buffer.getReadPointer (i), (int) data.numSamples);
}
// clear extra busses..
if (data.outputs != nullptr)
for (int i = 1; i < data.numOutputs; ++i)
for (int f = 0; f < data.outputs[i].numChannels; ++f)
FloatVectorOperations::clear (getPointerForAudioBus<FloatType> (data.outputs[i])[f], (int) data.numSamples);
}
//==============================================================================
void addBusTo (Vst::BusList& busList, Vst::Bus* newBus)
{
@@ -1680,6 +1704,50 @@ private:
return nullptr;
}
//==============================================================================
template <typename FloatType>
void allocateChannelLists (Array<FloatType*>& channelList)
{
channelList.clear();
channelList.insertMultiple (0, nullptr, jmax (JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) + 1);
}
template <typename FloatType>
static FloatType** getPointerForAudioBus (Vst::AudioBusBuffers& data) noexcept
{
return AudioBusPointerHelper<FloatType>::impl (data);
}
template <typename FloatType>
static int prepareChannelLists (Array<FloatType*>& channelList, Vst::ProcessData& data) noexcept
{
int totalChans = 0;
FloatType** inChannelBuffers =
data.inputs != nullptr ? getPointerForAudioBus<FloatType> (data.inputs[0]) : nullptr;
FloatType** outChannelBuffers =
data.outputs != nullptr ? getPointerForAudioBus<FloatType> (data.outputs[0]) : nullptr;
const int numInputChans = (data.inputs != nullptr && inChannelBuffers != nullptr) ? (int) data.inputs[0].numChannels : 0;
const int numOutputChans = (data.outputs != nullptr && outChannelBuffers != nullptr) ? (int) data.outputs[0].numChannels : 0;
for (int idx = 0; totalChans < numInputChans; ++idx)
{
channelList.set (totalChans, inChannelBuffers[idx]);
++totalChans;
}
// note that the loop bounds are correct: as VST-3 is always process replacing
// we already know the output channel buffers of the first numInputChans channels
for (int idx = 0; totalChans < numOutputChans; ++idx)
{
channelList.set (totalChans, outChannelBuffers[idx]);
++totalChans;
}
return totalChans;
}
//==============================================================================
enum InternalParameters
{


+ 1
- 1
libs/juce/source/modules/juce_audio_plugin_client/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_audio_plugin_client",
"name": "JUCE audio plugin wrapper classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for building VST, VST3, RTAS, AAX and AU plugins.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 2
- 1
libs/juce/source/modules/juce_audio_plugin_client/utility/juce_FakeMouseMoveGenerator.h View File

@@ -51,7 +51,8 @@ public:
if (Component* const comp = Desktop::getInstance().findComponentAt (screenPos.roundToInt()))
if (ComponentPeer* const peer = comp->getPeer())
if (! peer->isFocused())
peer->handleMouseEvent (0, peer->globalToLocal (screenPos), mods, Time::currentTimeMillis());
peer->handleMouseEvent (0, peer->globalToLocal (screenPos), mods,
MouseInputSource::invalidPressure, Time::currentTimeMillis());
}
}


+ 1
- 1
libs/juce/source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -1223,7 +1223,7 @@ private:
//==============================================================================
size_t getAudioBufferSizeInBytes() const noexcept
{
return offsetof (AudioBufferList, mBuffers) + (sizeof (AudioBuffer) * numOutputBusChannels);
return offsetof (AudioBufferList, mBuffers) + (sizeof (::AudioBuffer) * numOutputBusChannels);
}
AudioBufferList* getAudioBufferListForBus (AudioUnitElement busIndex) const noexcept


+ 43
- 19
libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -344,21 +344,25 @@ private:
};
//==============================================================================
namespace VST3BufferExchange
template <typename FloatType>
struct VST3BufferExchange
{
typedef Array<float*> Bus;
typedef Array<FloatType*> Bus;
typedef Array<Bus> BusMap;
static inline void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, float** raw) { vstBuffers.channelBuffers32 = raw; }
static inline void assignRawPointer (Steinberg::Vst::AudioBusBuffers& vstBuffers, double** raw) { vstBuffers.channelBuffers64 = raw; }
/** Assigns a series of AudioSampleBuffer's channels to an AudioBusBuffers'
@warning For speed, does not check the channel count and offsets
according to the AudioSampleBuffer
*/
void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers,
Bus& bus,
AudioSampleBuffer& buffer,
int numChannels, int channelStartOffset,
int sampleOffset = 0)
static void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers,
Bus& bus,
AudioBuffer<FloatType>& buffer,
int numChannels, int channelStartOffset,
int sampleOffset = 0)
{
const int channelEnd = numChannels + channelStartOffset;
jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels());
@@ -368,7 +372,7 @@ namespace VST3BufferExchange
for (int i = channelStartOffset; i < channelEnd; ++i)
bus.add (buffer.getWritePointer (i, sampleOffset));
vstBuffers.channelBuffers32 = bus.getRawDataPointer();
assignRawPointer (vstBuffers, bus.getRawDataPointer());
vstBuffers.numChannels = numChannels;
vstBuffers.silenceFlags = 0;
}
@@ -376,7 +380,7 @@ namespace VST3BufferExchange
static void mapArrangementToBusses (int& channelIndexOffset, int index,
Array<Steinberg::Vst::AudioBusBuffers>& result,
BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement,
AudioSampleBuffer& source)
AudioBuffer<FloatType>& source)
{
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits();
@@ -387,18 +391,16 @@ namespace VST3BufferExchange
busMapToUse.add (Bus());
if (numChansForBus > 0)
{
associateBufferTo (result.getReference (index),
busMapToUse.getReference (index),
source, numChansForBus, channelIndexOffset);
}
channelIndexOffset += numChansForBus;
}
inline void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<Steinberg::Vst::SpeakerArrangement>& arrangements,
AudioSampleBuffer& source)
static inline void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<Steinberg::Vst::SpeakerArrangement>& arrangements,
AudioBuffer<FloatType>& source)
{
int channelIndexOffset = 0;
@@ -407,10 +409,10 @@ namespace VST3BufferExchange
arrangements.getUnchecked (i), source);
}
inline void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result,
Steinberg::Vst::IAudioProcessor& processor,
BusMap& busMapToUse, bool isInput, int numBusses,
AudioSampleBuffer& source)
static inline void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result,
Steinberg::Vst::IAudioProcessor& processor,
BusMap& busMapToUse, bool isInput, int numBusses,
AudioBuffer<FloatType>& source)
{
int channelIndexOffset = 0;
@@ -420,6 +422,28 @@ namespace VST3BufferExchange
getArrangementForBus (&processor, isInput, i),
source);
}
}
};
template <typename FloatType>
struct VST3FloatAndDoubleBusMapCompositeHelper {};
struct VST3FloatAndDoubleBusMapComposite
{
VST3BufferExchange<float>::BusMap floatVersion;
VST3BufferExchange<double>::BusMap doubleVersion;
template <typename FloatType>
inline typename VST3BufferExchange<FloatType>::BusMap& get() { return VST3FloatAndDoubleBusMapCompositeHelper<FloatType>::get (*this); }
};
template <> struct VST3FloatAndDoubleBusMapCompositeHelper<float>
{
static inline VST3BufferExchange<float>::BusMap& get (VST3FloatAndDoubleBusMapComposite& impl) { return impl.floatVersion; }
};
template <> struct VST3FloatAndDoubleBusMapCompositeHelper<double>
{
static inline VST3BufferExchange<double>::BusMap& get (VST3FloatAndDoubleBusMapComposite& impl) { return impl.doubleVersion; }
};
#endif // JUCE_VST3COMMON_H_INCLUDED

+ 47
- 31
libs/juce/source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -1702,7 +1702,7 @@ public:
using namespace Vst;
ProcessSetup setup;
setup.symbolicSampleSize = kSample32;
setup.symbolicSampleSize = isUsingDoublePrecision() ? kSample64 : kSample32;
setup.maxSamplesPerBlock = estimatedSamplesPerBlock;
setup.sampleRate = newSampleRate;
setup.processMode = isNonRealtime() ? kOffline : kRealtime;
@@ -1769,39 +1769,56 @@ public:
JUCE_CATCH_ALL_ASSERT
}
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
bool supportsDoublePrecisionProcessing() const override
{
using namespace Vst;
return (processor->canProcessSampleSize (Vst::kSample64) == kResultTrue);
}
if (isActive
&& processor != nullptr
&& processor->canProcessSampleSize (kSample32) == kResultTrue)
{
const int numSamples = buffer.getNumSamples();
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
jassert (! isUsingDoublePrecision());
if (isActive && processor != nullptr)
processAudio (buffer, midiMessages, Vst::kSample32);
}
void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
{
jassert (isUsingDoublePrecision());
if (isActive && processor != nullptr)
processAudio (buffer, midiMessages, Vst::kSample64);
}
template <typename FloatType>
void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages,
Vst::SymbolicSampleSizes sampleSize)
{
using namespace Vst;
const int numSamples = buffer.getNumSamples();
ProcessData data;
data.processMode = isNonRealtime() ? kOffline : kRealtime;
data.symbolicSampleSize = kSample32;
data.numInputs = numInputAudioBusses;
data.numOutputs = numOutputAudioBusses;
data.inputParameterChanges = inputParameterChanges;
data.outputParameterChanges = outputParameterChanges;
data.numSamples = (Steinberg::int32) numSamples;
ProcessData data;
data.processMode = isNonRealtime() ? kOffline : kRealtime;
data.symbolicSampleSize = sampleSize;
data.numInputs = numInputAudioBusses;
data.numOutputs = numOutputAudioBusses;
data.inputParameterChanges = inputParameterChanges;
data.outputParameterChanges = outputParameterChanges;
data.numSamples = (Steinberg::int32) numSamples;
updateTimingInformation (data, getSampleRate());
updateTimingInformation (data, getSampleRate());
for (int i = getNumInputChannels(); i < buffer.getNumChannels(); ++i)
buffer.clear (i, 0, numSamples);
for (int i = getNumInputChannels(); i < buffer.getNumChannels(); ++i)
buffer.clear (i, 0, numSamples);
associateTo (data, buffer);
associateTo (data, midiMessages);
associateTo (data, buffer);
associateTo (data, midiMessages);
processor->process (data);
processor->process (data);
MidiEventList::toMidiBuffer (midiMessages, *midiOutputs);
MidiEventList::toMidiBuffer (midiMessages, *midiOutputs);
inputParameterChanges->clearAllQueues();
}
inputParameterChanges->clearAllQueues();
}
//==============================================================================
@@ -2151,7 +2168,7 @@ private:
*/
int numInputAudioBusses, numOutputAudioBusses;
Array<Vst::SpeakerArrangement> inputArrangements, outputArrangements; // Caching to improve performance and to avoid possible non-thread-safe calls to getBusArrangements().
VST3BufferExchange::BusMap inputBusMap, outputBusMap;
VST3FloatAndDoubleBusMapComposite inputBusMap, outputBusMap;
Array<Vst::AudioBusBuffers> inputBusses, outputBusses;
//==============================================================================
@@ -2393,12 +2410,11 @@ private:
}
//==============================================================================
void associateTo (Vst::ProcessData& destination, AudioSampleBuffer& buffer)
template <typename FloatType>
void associateTo (Vst::ProcessData& destination, AudioBuffer<FloatType>& buffer)
{
using namespace VST3BufferExchange;
mapBufferToBusses (inputBusses, inputBusMap, inputArrangements, buffer);
mapBufferToBusses (outputBusses, outputBusMap, outputArrangements, buffer);
VST3BufferExchange<FloatType>::mapBufferToBusses (inputBusses, inputBusMap.get<FloatType>(), inputArrangements, buffer);
VST3BufferExchange<FloatType>::mapBufferToBusses (outputBusses, outputBusMap.get<FloatType>(), outputArrangements, buffer);
destination.inputs = inputBusses.getRawDataPointer();
destination.outputs = outputBusses.getRawDataPointer();


+ 147
- 104
libs/juce/source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp View File

@@ -715,8 +715,7 @@ public:
name (mh->pluginName),
wantsMidiMessages (false),
initialised (false),
isPowerOn (false),
tempBuffer (1, 1)
isPowerOn (false)
{
try
{
@@ -938,6 +937,18 @@ public:
dispatch (effSetSampleRate, 0, 0, 0, (float) rate);
dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0);
if (supportsDoublePrecisionProcessing())
{
VstInt32 vstPrecision = isUsingDoublePrecision() ? kVstProcessPrecision64
: kVstProcessPrecision32;
// if you get an assertion here then your plug-in claims it supports double precision
// but returns an error when we try to change the precision
VstIntPtr err = dispatch (effSetProcessPrecision, 0, (VstIntPtr) vstPrecision, 0, 0);
jassert (err > 0);
ignoreUnused (err);
}
tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected);
if (! isPowerOn)
@@ -980,110 +991,22 @@ public:
}
}
void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override
void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override
{
const int numSamples = buffer.getNumSamples();
if (initialised)
{
if (AudioPlayHead* const currentPlayHead = getPlayHead())
{
AudioPlayHead::CurrentPositionInfo position;
if (currentPlayHead->getCurrentPosition (position))
{
vstHostTime.samplePos = (double) position.timeInSamples;
vstHostTime.tempo = position.bpm;
vstHostTime.timeSigNumerator = position.timeSigNumerator;
vstHostTime.timeSigDenominator = position.timeSigDenominator;
vstHostTime.ppqPos = position.ppqPosition;
vstHostTime.barStartPos = position.ppqPositionOfLastBarStart;
vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid;
VstInt32 newTransportFlags = 0;
if (position.isPlaying) newTransportFlags |= kVstTransportPlaying;
if (position.isRecording) newTransportFlags |= kVstTransportRecording;
if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording)))
vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged;
else
vstHostTime.flags &= ~kVstTransportChanged;
switch (position.frameRate)
{
case AudioPlayHead::fps24: setHostTimeFrameRate (0, 24.0, position.timeInSeconds); break;
case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break;
case AudioPlayHead::fps2997: setHostTimeFrameRate (2, 29.97, position.timeInSeconds); break;
case AudioPlayHead::fps30: setHostTimeFrameRate (3, 30.0, position.timeInSeconds); break;
case AudioPlayHead::fps2997drop: setHostTimeFrameRate (4, 29.97, position.timeInSeconds); break;
case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break;
default: break;
}
if (position.isLooping)
{
vstHostTime.cycleStartPos = position.ppqLoopStart;
vstHostTime.cycleEndPos = position.ppqLoopEnd;
vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive);
}
else
{
vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive);
}
}
}
vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds();
if (wantsMidiMessages)
{
midiEventsToSend.clear();
midiEventsToSend.ensureSize (1);
MidiBuffer::Iterator iter (midiMessages);
const uint8* midiData;
int numBytesOfMidiData, samplePosition;
while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition))
{
midiEventsToSend.addEvent (midiData, numBytesOfMidiData,
jlimit (0, numSamples - 1, samplePosition));
}
effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend.events, 0);
}
_clearfp();
if ((effect->flags & effFlagsCanReplacing) != 0)
{
effect->processReplacing (effect, buffer.getArrayOfWritePointers(), buffer.getArrayOfWritePointers(), numSamples);
}
else
{
tempBuffer.setSize (effect->numOutputs, numSamples);
tempBuffer.clear();
effect->process (effect, buffer.getArrayOfWritePointers(), tempBuffer.getArrayOfWritePointers(), numSamples);
for (int i = effect->numOutputs; --i >= 0;)
buffer.copyFrom (i, 0, tempBuffer.getReadPointer (i), numSamples);
}
}
else
{
// Not initialised, so just bypass..
for (int i = 0; i < getNumOutputChannels(); ++i)
buffer.clear (i, 0, buffer.getNumSamples());
}
jassert (! isUsingDoublePrecision());
processAudio (buffer, midiMessages);
}
{
// copy any incoming midi..
const ScopedLock sl (midiInLock);
void processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages) override
{
jassert (isUsingDoublePrecision());
processAudio (buffer, midiMessages);
}
midiMessages.swapWith (incomingMidi);
incomingMidi.clear();
}
bool supportsDoublePrecisionProcessing() const override
{
return ((effect->flags & effFlagsCanReplacing) != 0
&& (effect->flags & effFlagsCanDoubleReplacing) != 0);
}
//==============================================================================
@@ -1673,12 +1596,132 @@ private:
CriticalSection lock;
bool wantsMidiMessages, initialised, isPowerOn;
mutable StringArray programNames;
AudioSampleBuffer tempBuffer;
AudioBuffer<float> tempBuffer;
CriticalSection midiInLock;
MidiBuffer incomingMidi;
VSTMidiEventList midiEventsToSend;
VstTimeInfo vstHostTime;
//==============================================================================
template <typename FloatType>
void processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages)
{
const int numSamples = buffer.getNumSamples();
if (initialised)
{
if (AudioPlayHead* const currentPlayHead = getPlayHead())
{
AudioPlayHead::CurrentPositionInfo position;
if (currentPlayHead->getCurrentPosition (position))
{
vstHostTime.samplePos = (double) position.timeInSamples;
vstHostTime.tempo = position.bpm;
vstHostTime.timeSigNumerator = position.timeSigNumerator;
vstHostTime.timeSigDenominator = position.timeSigDenominator;
vstHostTime.ppqPos = position.ppqPosition;
vstHostTime.barStartPos = position.ppqPositionOfLastBarStart;
vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid;
VstInt32 newTransportFlags = 0;
if (position.isPlaying) newTransportFlags |= kVstTransportPlaying;
if (position.isRecording) newTransportFlags |= kVstTransportRecording;
if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording)))
vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged;
else
vstHostTime.flags &= ~kVstTransportChanged;
switch (position.frameRate)
{
case AudioPlayHead::fps24: setHostTimeFrameRate (0, 24.0, position.timeInSeconds); break;
case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break;
case AudioPlayHead::fps2997: setHostTimeFrameRate (2, 29.97, position.timeInSeconds); break;
case AudioPlayHead::fps30: setHostTimeFrameRate (3, 30.0, position.timeInSeconds); break;
case AudioPlayHead::fps2997drop: setHostTimeFrameRate (4, 29.97, position.timeInSeconds); break;
case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break;
default: break;
}
if (position.isLooping)
{
vstHostTime.cycleStartPos = position.ppqLoopStart;
vstHostTime.cycleEndPos = position.ppqLoopEnd;
vstHostTime.flags |= (kVstCyclePosValid | kVstTransportCycleActive);
}
else
{
vstHostTime.flags &= ~(kVstCyclePosValid | kVstTransportCycleActive);
}
}
}
vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds();
if (wantsMidiMessages)
{
midiEventsToSend.clear();
midiEventsToSend.ensureSize (1);
MidiBuffer::Iterator iter (midiMessages);
const uint8* midiData;
int numBytesOfMidiData, samplePosition;
while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition))
{
midiEventsToSend.addEvent (midiData, numBytesOfMidiData,
jlimit (0, numSamples - 1, samplePosition));
}
effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend.events, 0);
}
_clearfp();
invokeProcessFunction (buffer, numSamples);
}
else
{
// Not initialised, so just bypass..
for (int i = 0; i < getNumOutputChannels(); ++i)
buffer.clear (i, 0, buffer.getNumSamples());
}
{
// copy any incoming midi..
const ScopedLock sl (midiInLock);
midiMessages.swapWith (incomingMidi);
incomingMidi.clear();
}
}
//==============================================================================
inline void invokeProcessFunction (AudioBuffer<float>& buffer, VstInt32 sampleFrames)
{
if ((effect->flags & effFlagsCanReplacing) != 0)
{
effect->processReplacing (effect, buffer.getArrayOfWritePointers(), buffer.getArrayOfWritePointers(), sampleFrames);
}
else
{
tempBuffer.setSize (effect->numOutputs, sampleFrames);
tempBuffer.clear();
effect->process (effect, buffer.getArrayOfWritePointers(), tempBuffer.getArrayOfWritePointers(), sampleFrames);
for (int i = effect->numOutputs; --i >= 0;)
buffer.copyFrom (i, 0, tempBuffer.getReadPointer (i), sampleFrames);
}
}
inline void invokeProcessFunction (AudioBuffer<double>& buffer, VstInt32 sampleFrames)
{
effect->processDoubleReplacing (effect, buffer.getArrayOfWritePointers(), buffer.getArrayOfWritePointers(), sampleFrames);
}
//==============================================================================
void setHostTimeFrameRate (long frameRateIndex, double frameRate, double currentTime) noexcept
{


+ 2
- 0
libs/juce/source/modules/juce_audio_processors/juce_audio_processors.cpp View File

@@ -166,5 +166,7 @@ void AutoResizingNSViewComponentWithParent::timerCallback()
#include "scanning/juce_KnownPluginList.cpp"
#include "scanning/juce_PluginDirectoryScanner.cpp"
#include "scanning/juce_PluginListComponent.cpp"
#include "utilities/juce_AudioProcessorValueTreeState.cpp"
#include "utilities/juce_AudioProcessorParameters.cpp"
}

+ 6
- 0
libs/juce/source/modules/juce_audio_processors/juce_audio_processors.h View File

@@ -92,6 +92,12 @@ class AudioProcessor;
#include "format_types/juce_VST3PluginFormat.h"
#include "scanning/juce_PluginDirectoryScanner.h"
#include "scanning/juce_PluginListComponent.h"
#include "utilities/juce_AudioProcessorValueTreeState.h"
#include "utilities/juce_AudioProcessorParameterWithID.h"
#include "utilities/juce_AudioParameterFloat.h"
#include "utilities/juce_AudioParameterInt.h"
#include "utilities/juce_AudioParameterBool.h"
#include "utilities/juce_AudioParameterChoice.h"
}


+ 3
- 2
libs/juce/source/modules/juce_audio_processors/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_audio_processors",
"name": "JUCE audio plugin hosting classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for loading and playing VST, AU, or internally-generated audio processors.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -17,7 +17,8 @@
"browse": [ "processors/*",
"format/*",
"format_types/*",
"scanning/*"
"scanning/*",
"utilities/*"
],
"OSXFrameworks": "CoreAudio CoreMIDI AudioToolbox",


+ 29
- 2
libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp View File

@@ -38,7 +38,8 @@ AudioProcessor::AudioProcessor()
numOutputChannels (0),
latencySamples (0),
suspended (false),
nonRealtime (false)
nonRealtime (false),
processingPrecision (singlePrecision)
{
}
@@ -325,7 +326,33 @@ void AudioProcessor::suspendProcessing (const bool shouldBeSuspended)
}
void AudioProcessor::reset() {}
void AudioProcessor::processBlockBypassed (AudioSampleBuffer&, MidiBuffer&) {}
void AudioProcessor::processBlockBypassed (AudioBuffer<float>&, MidiBuffer&) {}
void AudioProcessor::processBlockBypassed (AudioBuffer<double>&, MidiBuffer&) {}
void AudioProcessor::processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
{
ignoreUnused (buffer, midiMessages);
// If you hit this assertion then either the caller called the double
// precision version of processBlock on a processor which does not support it
// (i.e. supportsDoublePrecisionProcessing() returns false), or the implementation
// of the AudioProcessor forgot to override the double precision version of this method
jassertfalse;
}
void AudioProcessor::setProcessingPrecision (ProcessingPrecision precision) noexcept
{
// If you hit this assertion then you're trying to use double precision
// processing on a processor which does not support it!
jassert (precision != doublePrecision || supportsDoublePrecisionProcessing());
processingPrecision = precision;
}
bool AudioProcessor::supportsDoublePrecisionProcessing() const
{
return false;
}
#if ! JUCE_AUDIO_PROCESSOR_NO_GUI
//==============================================================================


+ 113
- 2
libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -48,6 +48,14 @@ protected:
AudioProcessor();
public:
//==============================================================================
enum ProcessingPrecision
{
singlePrecision,
doublePrecision
};
//==============================================================================
/** Destructor. */
virtual ~AudioProcessor();
@@ -125,9 +133,74 @@ public:
processBlock() method to send out an asynchronous message. You could also use
the AsyncUpdater class in a similar way.
*/
virtual void processBlock (AudioSampleBuffer& buffer,
virtual void processBlock (AudioBuffer<float>& buffer,
MidiBuffer& midiMessages) = 0;
/** Renders the next block.
When this method is called, the buffer contains a number of channels which is
at least as great as the maximum number of input and output channels that
this filter is using. It will be filled with the filter's input data and
should be replaced with the filter's output.
So for example if your filter has 2 input channels and 4 output channels, then
the buffer will contain 4 channels, the first two being filled with the
input data. Your filter should read these, do its processing, and replace
the contents of all 4 channels with its output.
Or if your filter has 5 inputs and 2 outputs, the buffer will have 5 channels,
all filled with data, and your filter should overwrite the first 2 of these
with its output. But be VERY careful not to write anything to the last 3
channels, as these might be mapped to memory that the host assumes is read-only!
Note that if you have more outputs than inputs, then only those channels that
correspond to an input channel are guaranteed to contain sensible data - e.g.
in the case of 2 inputs and 4 outputs, the first two channels contain the input,
but the last two channels may contain garbage, so you should be careful not to
let this pass through without being overwritten or cleared.
Also note that the buffer may have more channels than are strictly necessary,
but you should only read/write from the ones that your filter is supposed to
be using.
The number of samples in these buffers is NOT guaranteed to be the same for every
callback, and may be more or less than the estimated value given to prepareToPlay().
Your code must be able to cope with variable-sized blocks, or you're going to get
clicks and crashes!
Also note that some hosts will occasionally decide to pass a buffer containing
zero samples, so make sure that your algorithm can deal with that!
If the filter is receiving a midi input, then the midiMessages array will be filled
with the midi messages for this block. Each message's timestamp will indicate the
message's time, as a number of samples from the start of the block.
Any messages left in the midi buffer when this method has finished are assumed to
be the filter's midi output. This means that your filter should be careful to
clear any incoming messages from the array if it doesn't want them to be passed-on.
Be very careful about what you do in this callback - it's going to be called by
the audio thread, so any kind of interaction with the UI is absolutely
out of the question. If you change a parameter in here and need to tell your UI to
update itself, the best way is probably to inherit from a ChangeBroadcaster, let
the UI components register as listeners, and then call sendChangeMessage() inside the
processBlock() method to send out an asynchronous message. You could also use
the AsyncUpdater class in a similar way.
*/
virtual void processBlock (AudioBuffer<double>& buffer,
MidiBuffer& midiMessages);
/** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but
you may override this method e.g. to add latency compensation to the data to match
the processor's latency characteristics. This will avoid situations where bypassing
will shift the signal forward in time, possibly creating pre-echo effects and odd timings.
Another use for this method would be to cross-fade or morph between the wet (not bypassed)
and dry (bypassed) signals.
*/
virtual void processBlockBypassed (AudioBuffer<float>& buffer,
MidiBuffer& midiMessages);
/** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but
you may override this method e.g. to add latency compensation to the data to match
@@ -136,9 +209,46 @@ public:
Another use for this method would be to cross-fade or morph between the wet (not bypassed)
and dry (bypassed) signals.
*/
virtual void processBlockBypassed (AudioSampleBuffer& buffer,
virtual void processBlockBypassed (AudioBuffer<double>& buffer,
MidiBuffer& midiMessages);
//==============================================================================
/** Returns true if the Audio processor supports double precision floating point processing.
The default implementation will always return false.
If you return true here then you must override the double precision versions
of processBlock. Additionally, you must call getProcessingPrecision() in
your prepareToPlay method to determine the precision with which you need to
allocate your internal buffers.
@see getProcessingPrecision, setProcessingPrecision
*/
virtual bool supportsDoublePrecisionProcessing() const;
/** Returns the precision-mode of the processor.
Depending on the result of this method you MUST call the corresponding version
of processBlock. The default processing precision is single precision.
@see setProcessingPrecision, supportsDoublePrecisionProcessing
*/
ProcessingPrecision getProcessingPrecision() const noexcept { return processingPrecision; }
/** Returns true if the current precision is set to doublePrecision. */
bool isUsingDoublePrecision() const noexcept { return processingPrecision == doublePrecision; }
/** Changes the processing precision of the receiver. A client of the AudioProcessor
calls this function to indicate which version of processBlock (single or double
precision) it intends to call. The client MUST call this function before calling
the prepareToPlay method so that the receiver can do any necessary allocations
in the prepareToPlay() method. An implementation of prepareToPlay() should call
getProcessingPrecision() to determine with which precision it should allocate
it's internal buffers.
Note that setting the processing precision to double floating point precision
on a receiver which does not support double precision processing (i.e.
supportsDoublePrecisionProcessing() returns false) will result in an assertion.
@see getProcessingPrecision, supportsDoublePrecisionProcessing
*/
void setProcessingPrecision (ProcessingPrecision precision) noexcept;
//==============================================================================
/** Returns the current AudioPlayHead object that should be used to find
out the state and position of the playhead.
@@ -742,6 +852,7 @@ private:
double sampleRate;
int blockSize, numInputChannels, numOutputChannels, latencySamples;
bool suspended, nonRealtime;
ProcessingPrecision processingPrecision;
CriticalSection callbackLock, listenerLock;
String inputSpeakerArrangement, outputSpeakerArrangement;


+ 235
- 54
libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -25,28 +25,81 @@
const int AudioProcessorGraph::midiChannelIndex = 0x1000;
//==============================================================================
namespace GraphRenderingOps
template <typename FloatType, typename Impl> struct FloatDoubleUtil {};
template <typename Tag, typename Type> struct FloatDoubleType {};
template <typename Tag>
struct FloatAndDoubleComposition
{
typedef typename FloatDoubleType<Tag, float>::Type FloatType;
typedef typename FloatDoubleType<Tag, double>::Type DoubleType;
template <typename FloatingType>
inline typename FloatDoubleType<Tag, FloatingType>::Type& get() noexcept
{
return FloatDoubleUtil<FloatingType, FloatAndDoubleComposition<Tag> >::get (*this);
}
FloatType floatVersion;
DoubleType doubleVersion;
};
template <typename Impl> struct FloatDoubleUtil<float, Impl> { static inline typename Impl::FloatType& get (Impl& i) noexcept { return i.floatVersion; } };
template <typename Impl> struct FloatDoubleUtil<double, Impl> { static inline typename Impl::DoubleType& get (Impl& i) noexcept { return i.doubleVersion; } };
struct FloatPlaceholder;
template <typename FloatingType> struct FloatDoubleType<HeapBlock<FloatPlaceholder>, FloatingType> { typedef HeapBlock<FloatingType> Type; };
template <typename FloatingType> struct FloatDoubleType<HeapBlock<FloatPlaceholder*>, FloatingType> { typedef HeapBlock<FloatingType*> Type; };
template <typename FloatingType> struct FloatDoubleType<AudioBuffer<FloatPlaceholder>, FloatingType> { typedef AudioBuffer<FloatingType> Type; };
template <typename FloatingType> struct FloatDoubleType<AudioBuffer<FloatPlaceholder>*, FloatingType> { typedef AudioBuffer<FloatingType>* Type; };
//==============================================================================
struct AudioGraphRenderingOp
namespace GraphRenderingOps
{
AudioGraphRenderingOp() noexcept {}
virtual ~AudioGraphRenderingOp() {}
virtual void perform (AudioSampleBuffer& sharedBufferChans,
struct AudioGraphRenderingOpBase
{
AudioGraphRenderingOpBase() noexcept {}
virtual ~AudioGraphRenderingOpBase() {}
virtual void perform (AudioBuffer<float>& sharedBufferChans,
const OwnedArray<MidiBuffer>& sharedMidiBuffers,
const int numSamples) = 0;
virtual void perform (AudioBuffer<double>& sharedBufferChans,
const OwnedArray<MidiBuffer>& sharedMidiBuffers,
const int numSamples) = 0;
JUCE_LEAK_DETECTOR (AudioGraphRenderingOp)
JUCE_LEAK_DETECTOR (AudioGraphRenderingOpBase)
};
// use CRTP
template <class Child>
struct AudioGraphRenderingOp : public AudioGraphRenderingOpBase
{
void perform (AudioBuffer<float>& sharedBufferChans,
const OwnedArray<MidiBuffer>& sharedMidiBuffers,
const int numSamples) override
{
static_cast<Child*> (this)->perform (sharedBufferChans, sharedMidiBuffers, numSamples);
}
void perform (AudioBuffer<double>& sharedBufferChans,
const OwnedArray<MidiBuffer>& sharedMidiBuffers,
const int numSamples) override
{
static_cast<Child*> (this)->perform (sharedBufferChans, sharedMidiBuffers, numSamples);
}
};
//==============================================================================
struct ClearChannelOp : public AudioGraphRenderingOp
struct ClearChannelOp : public AudioGraphRenderingOp<ClearChannelOp>
{
ClearChannelOp (const int channel) noexcept : channelNum (channel) {}
void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
template <typename FloatType>
void perform (AudioBuffer<FloatType>& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
{
sharedBufferChans.clear (channelNum, 0, numSamples);
}
@@ -57,13 +110,14 @@ struct ClearChannelOp : public AudioGraphRenderingOp
};
//==============================================================================
struct CopyChannelOp : public AudioGraphRenderingOp
struct CopyChannelOp : public AudioGraphRenderingOp<CopyChannelOp>
{
CopyChannelOp (const int srcChan, const int dstChan) noexcept
: srcChannelNum (srcChan), dstChannelNum (dstChan)
{}
void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
template <typename FloatType>
void perform (AudioBuffer<FloatType>& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
{
sharedBufferChans.copyFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples);
}
@@ -74,13 +128,14 @@ struct CopyChannelOp : public AudioGraphRenderingOp
};
//==============================================================================
struct AddChannelOp : public AudioGraphRenderingOp
struct AddChannelOp : public AudioGraphRenderingOp<AddChannelOp>
{
AddChannelOp (const int srcChan, const int dstChan) noexcept
: srcChannelNum (srcChan), dstChannelNum (dstChan)
{}
void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
template <typename FloatType>
void perform (AudioBuffer<FloatType>& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
{
sharedBufferChans.addFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples);
}
@@ -91,11 +146,12 @@ struct AddChannelOp : public AudioGraphRenderingOp
};
//==============================================================================
struct ClearMidiBufferOp : public AudioGraphRenderingOp
struct ClearMidiBufferOp : public AudioGraphRenderingOp<ClearMidiBufferOp>
{
ClearMidiBufferOp (const int buffer) noexcept : bufferNum (buffer) {}
void perform (AudioSampleBuffer&, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int)
template <typename FloatType>
void perform (AudioBuffer<FloatType>&, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int)
{
sharedMidiBuffers.getUnchecked (bufferNum)->clear();
}
@@ -106,13 +162,14 @@ struct ClearMidiBufferOp : public AudioGraphRenderingOp
};
//==============================================================================
struct CopyMidiBufferOp : public AudioGraphRenderingOp
struct CopyMidiBufferOp : public AudioGraphRenderingOp<CopyMidiBufferOp>
{
CopyMidiBufferOp (const int srcBuffer, const int dstBuffer) noexcept
: srcBufferNum (srcBuffer), dstBufferNum (dstBuffer)
{}
void perform (AudioSampleBuffer&, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int)
template <typename FloatType>
void perform (AudioBuffer<FloatType>&, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int)
{
*sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum);
}
@@ -123,13 +180,14 @@ struct CopyMidiBufferOp : public AudioGraphRenderingOp
};
//==============================================================================
struct AddMidiBufferOp : public AudioGraphRenderingOp
struct AddMidiBufferOp : public AudioGraphRenderingOp<AddMidiBufferOp>
{
AddMidiBufferOp (const int srcBuffer, const int dstBuffer)
: srcBufferNum (srcBuffer), dstBufferNum (dstBuffer)
{}
void perform (AudioSampleBuffer&, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int numSamples)
template <typename FloatType>
void perform (AudioBuffer<FloatType>&, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int numSamples)
{
sharedMidiBuffers.getUnchecked (dstBufferNum)
->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0);
@@ -141,24 +199,27 @@ struct AddMidiBufferOp : public AudioGraphRenderingOp
};
//==============================================================================
struct DelayChannelOp : public AudioGraphRenderingOp
struct DelayChannelOp : public AudioGraphRenderingOp<DelayChannelOp>
{
DelayChannelOp (const int chan, const int delaySize)
: channel (chan),
bufferSize (delaySize + 1),
readIndex (0), writeIndex (delaySize)
{
buffer.calloc ((size_t) bufferSize);
buffer.floatVersion. calloc ((size_t) bufferSize);
buffer.doubleVersion.calloc ((size_t) bufferSize);
}
void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
template <typename FloatType>
void perform (AudioBuffer<FloatType>& sharedBufferChans, const OwnedArray<MidiBuffer>&, const int numSamples)
{
float* data = sharedBufferChans.getWritePointer (channel, 0);
FloatType* data = sharedBufferChans.getWritePointer (channel, 0);
HeapBlock<FloatType>& block = buffer.get<FloatType>();
for (int i = numSamples; --i >= 0;)
{
buffer [writeIndex] = *data;
*data++ = buffer [readIndex];
block [writeIndex] = *data;
*data++ = block [readIndex];
if (++readIndex >= bufferSize) readIndex = 0;
if (++writeIndex >= bufferSize) writeIndex = 0;
@@ -166,41 +227,67 @@ struct DelayChannelOp : public AudioGraphRenderingOp
}
private:
HeapBlock<float> buffer;
FloatAndDoubleComposition<HeapBlock<FloatPlaceholder> > buffer;
const int channel, bufferSize;
int readIndex, writeIndex;
JUCE_DECLARE_NON_COPYABLE (DelayChannelOp)
};
//==============================================================================
struct ProcessBufferOp : public AudioGraphRenderingOp
struct ProcessBufferOp : public AudioGraphRenderingOp<ProcessBufferOp>
{
ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& n,
const Array<int>& audioChannels,
const Array<int>& audioChannelsUsed,
const int totalNumChans,
const int midiBuffer)
: node (n),
processor (n->getProcessor()),
audioChannelsToUse (audioChannels),
audioChannelsToUse (audioChannelsUsed),
totalChans (jmax (1, totalNumChans)),
midiBufferToUse (midiBuffer)
{
channels.calloc ((size_t) totalChans);
audioChannels.floatVersion. calloc ((size_t) totalChans);
audioChannels.doubleVersion.calloc ((size_t) totalChans);
while (audioChannelsToUse.size() < totalChans)
audioChannelsToUse.add (0);
}
void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int numSamples)
template <typename FloatType>
void perform (AudioBuffer<FloatType>& sharedBufferChans, const OwnedArray<MidiBuffer>& sharedMidiBuffers, const int numSamples)
{
HeapBlock<FloatType*>& channels = audioChannels.get<FloatType>();
for (int i = totalChans; --i >= 0;)
channels[i] = sharedBufferChans.getWritePointer (audioChannelsToUse.getUnchecked (i), 0);
AudioSampleBuffer buffer (channels, totalChans, numSamples);
AudioBuffer<FloatType> buffer (channels, totalChans, numSamples);
processor->processBlock (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse));
callProcess (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse));
}
void callProcess (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
processor->processBlock (buffer, midiMessages);
}
void callProcess (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
{
if (processor->isUsingDoublePrecision())
{
processor->processBlock (buffer, midiMessages);
}
else
{
// if the processor is in single precision mode but the graph in double
// precision then we need to convert between buffer formats. Note, that
// this will only happen if the processor does not support double
// precision processing.
tempBuffer.makeCopyOf (buffer);
processor->processBlock (tempBuffer, midiMessages);
buffer.makeCopyOf (tempBuffer);
}
}
const AudioProcessorGraph::Node::Ptr node;
@@ -208,7 +295,8 @@ struct ProcessBufferOp : public AudioGraphRenderingOp
private:
Array<int> audioChannelsToUse;
HeapBlock<float*> channels;
FloatAndDoubleComposition<HeapBlock<FloatPlaceholder*> > audioChannels;
AudioBuffer<float> tempBuffer;
const int totalChans;
const int midiBufferToUse;
@@ -861,13 +949,17 @@ AudioProcessorGraph::Node::Node (const uint32 nodeID, AudioProcessor* const p) n
}
void AudioProcessorGraph::Node::prepare (const double newSampleRate, const int newBlockSize,
AudioProcessorGraph* const graph)
AudioProcessorGraph* const graph, ProcessingPrecision precision)
{
if (! isPrepared)
{
isPrepared = true;
setParentGraph (graph);
// try to align the precision of the processor and the graph
processor->setProcessingPrecision (processor->supportsDoublePrecisionProcessing() ? precision
: singlePrecision);
processor->setPlayConfigDetails (processor->getNumInputChannels(),
processor->getNumOutputChannels(),
newSampleRate, newBlockSize);
@@ -892,10 +984,53 @@ void AudioProcessorGraph::Node::setParentGraph (AudioProcessorGraph* const graph
ioProc->setParentGraph (graph);
}
//==============================================================================
struct AudioProcessorGraph::AudioProcessorGraphBufferHelpers
{
AudioProcessorGraphBufferHelpers()
{
currentAudioInputBuffer.floatVersion = nullptr;
currentAudioInputBuffer.doubleVersion = nullptr;
}
void setRenderingBufferSize (int newNumChannels, int newNumSamples)
{
renderingBuffers.floatVersion. setSize (newNumChannels, newNumSamples);
renderingBuffers.doubleVersion.setSize (newNumChannels, newNumSamples);
renderingBuffers.floatVersion. clear();
renderingBuffers.doubleVersion.clear();
}
void release()
{
renderingBuffers.floatVersion. setSize (1, 1);
renderingBuffers.doubleVersion.setSize (1, 1);
currentAudioInputBuffer.floatVersion = nullptr;
currentAudioInputBuffer.doubleVersion = nullptr;
currentAudioOutputBuffer.floatVersion. setSize (1, 1);
currentAudioOutputBuffer.doubleVersion.setSize (1, 1);
}
void prepareInOutBuffers(int newNumChannels, int newNumSamples)
{
currentAudioInputBuffer.floatVersion = nullptr;
currentAudioInputBuffer.doubleVersion = nullptr;
currentAudioOutputBuffer.floatVersion. setSize (newNumChannels, newNumSamples);
currentAudioOutputBuffer.doubleVersion.setSize (newNumChannels, newNumSamples);
}
FloatAndDoubleComposition<AudioBuffer<FloatPlaceholder> > renderingBuffers;
FloatAndDoubleComposition<AudioBuffer<FloatPlaceholder>*> currentAudioInputBuffer;
FloatAndDoubleComposition<AudioBuffer<FloatPlaceholder> > currentAudioOutputBuffer;
};
//==============================================================================
AudioProcessorGraph::AudioProcessorGraph()
: lastNodeId (0),
currentAudioInputBuffer (nullptr),
: lastNodeId (0), audioBuffers (new AudioProcessorGraphBufferHelpers),
currentMidiInputBuffer (nullptr)
{
}
@@ -1140,7 +1275,7 @@ bool AudioProcessorGraph::removeIllegalConnections()
static void deleteRenderOpArray (Array<void*>& ops)
{
for (int i = ops.size(); --i >= 0;)
delete static_cast<GraphRenderingOps::AudioGraphRenderingOp*> (ops.getUnchecked(i));
delete static_cast<GraphRenderingOps::AudioGraphRenderingOpBase*> (ops.getUnchecked(i));
}
void AudioProcessorGraph::clearRenderingSequence()
@@ -1193,7 +1328,7 @@ void AudioProcessorGraph::buildRenderingSequence()
{
Node* const node = nodes.getUnchecked(i);
node->prepare (getSampleRate(), getBlockSize(), this);
node->prepare (getSampleRate(), getBlockSize(), this, getProcessingPrecision());
int j = 0;
for (; j < orderedNodes.size(); ++j)
@@ -1214,8 +1349,7 @@ void AudioProcessorGraph::buildRenderingSequence()
// swap over to the new rendering sequence..
const ScopedLock sl (getCallbackLock());
renderingBuffers.setSize (numRenderingBuffersNeeded, getBlockSize());
renderingBuffers.clear();
audioBuffers->setRenderingBufferSize (numRenderingBuffersNeeded, getBlockSize());
for (int i = midiBuffers.size(); --i >= 0;)
midiBuffers.getUnchecked(i)->clear();
@@ -1238,8 +1372,8 @@ void AudioProcessorGraph::handleAsyncUpdate()
//==============================================================================
void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int estimatedSamplesPerBlock)
{
currentAudioInputBuffer = nullptr;
currentAudioOutputBuffer.setSize (jmax (1, getNumOutputChannels()), estimatedSamplesPerBlock);
audioBuffers->prepareInOutBuffers (jmax (1, getNumOutputChannels()), estimatedSamplesPerBlock);
currentMidiInputBuffer = nullptr;
currentMidiOutputBuffer.clear();
@@ -1247,16 +1381,19 @@ void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int estimatedSam
buildRenderingSequence();
}
bool AudioProcessorGraph::supportsDoublePrecisionProcessing() const
{
return true;
}
void AudioProcessorGraph::releaseResources()
{
for (int i = 0; i < nodes.size(); ++i)
nodes.getUnchecked(i)->unprepare();
renderingBuffers.setSize (1, 1);
audioBuffers->release();
midiBuffers.clear();
currentAudioInputBuffer = nullptr;
currentAudioOutputBuffer.setSize (1, 1);
currentMidiInputBuffer = nullptr;
currentMidiOutputBuffer.clear();
}
@@ -1287,8 +1424,13 @@ void AudioProcessorGraph::setPlayHead (AudioPlayHead* audioPlayHead)
nodes.getUnchecked(i)->getProcessor()->setPlayHead (audioPlayHead);
}
void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
template <typename FloatType>
void AudioProcessorGraph::processAudio (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages)
{
AudioBuffer<FloatType>& renderingBuffers = audioBuffers->renderingBuffers.get<FloatType>();
AudioBuffer<FloatType>*& currentAudioInputBuffer = audioBuffers->currentAudioInputBuffer.get<FloatType>();
AudioBuffer<FloatType>& currentAudioOutputBuffer = audioBuffers->currentAudioOutputBuffer.get<FloatType>();
const int numSamples = buffer.getNumSamples();
currentAudioInputBuffer = &buffer;
@@ -1299,8 +1441,8 @@ void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& m
for (int i = 0; i < renderingOps.size(); ++i)
{
GraphRenderingOps::AudioGraphRenderingOp* const op
= (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i);
GraphRenderingOps::AudioGraphRenderingOpBase* const op
= (GraphRenderingOps::AudioGraphRenderingOpBase*) renderingOps.getUnchecked(i);
op->perform (renderingBuffers, midiBuffers, numSamples);
}
@@ -1331,6 +1473,21 @@ bool AudioProcessorGraph::producesMidi() const { return tru
void AudioProcessorGraph::getStateInformation (juce::MemoryBlock&) {}
void AudioProcessorGraph::setStateInformation (const void*, int) {}
void AudioProcessorGraph::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
processAudio (buffer, midiMessages);
}
void AudioProcessorGraph::processBlock (AudioBuffer<double>& buffer, MidiBuffer& midiMessages)
{
processAudio (buffer, midiMessages);
}
// explicit template instantiation
template void AudioProcessorGraph::processAudio<float> ( AudioBuffer<float>& buffer,
MidiBuffer& midiMessages);
template void AudioProcessorGraph::processAudio<double> (AudioBuffer<double>& buffer,
MidiBuffer& midiMessages);
//==============================================================================
AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType deviceType)
@@ -1384,19 +1541,31 @@ void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources()
{
}
void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer& buffer,
bool AudioProcessorGraph::AudioGraphIOProcessor::supportsDoublePrecisionProcessing() const
{
return true;
}
template <typename FloatType>
void AudioProcessorGraph::AudioGraphIOProcessor::processAudio (AudioBuffer<FloatType>& buffer,
MidiBuffer& midiMessages)
{
AudioBuffer<FloatType>*& currentAudioInputBuffer =
graph->audioBuffers->currentAudioInputBuffer.get<FloatType>();
AudioBuffer<FloatType>& currentAudioOutputBuffer =
graph->audioBuffers->currentAudioOutputBuffer.get<FloatType>();
jassert (graph != nullptr);
switch (type)
{
case audioOutputNode:
{
for (int i = jmin (graph->currentAudioOutputBuffer.getNumChannels(),
for (int i = jmin (currentAudioOutputBuffer.getNumChannels(),
buffer.getNumChannels()); --i >= 0;)
{
graph->currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples());
currentAudioOutputBuffer.addFrom (i, 0, buffer, i, 0, buffer.getNumSamples());
}
break;
@@ -1404,10 +1573,10 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer
case audioInputNode:
{
for (int i = jmin (graph->currentAudioInputBuffer->getNumChannels(),
for (int i = jmin (currentAudioInputBuffer->getNumChannels(),
buffer.getNumChannels()); --i >= 0;)
{
buffer.copyFrom (i, 0, *graph->currentAudioInputBuffer, i, 0, buffer.getNumSamples());
buffer.copyFrom (i, 0, *currentAudioInputBuffer, i, 0, buffer.getNumSamples());
}
break;
@@ -1426,6 +1595,18 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer
}
}
void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<float>& buffer,
MidiBuffer& midiMessages)
{
processAudio (buffer, midiMessages);
}
void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioBuffer<double>& buffer,
MidiBuffer& midiMessages)
{
processAudio (buffer, midiMessages);
}
bool AudioProcessorGraph::AudioGraphIOProcessor::silenceInProducesSilenceOut() const
{
return isOutput();


+ 18
- 7
libs/juce/source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h View File

@@ -25,7 +25,6 @@
#ifndef JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED
#define JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED
//==============================================================================
/**
A type of AudioProcessor which plays back a graph of other AudioProcessors.
@@ -92,7 +91,7 @@ public:
Node (uint32 nodeId, AudioProcessor*) noexcept;
void setParentGraph (AudioProcessorGraph*) const;
void prepare (double newSampleRate, int newBlockSize, AudioProcessorGraph*);
void prepare (double newSampleRate, int newBlockSize, AudioProcessorGraph*, ProcessingPrecision);
void unprepare();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node)
@@ -308,7 +307,9 @@ public:
void fillInPluginDescription (PluginDescription&) const override;
void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override;
void releaseResources() override;
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
void processBlock (AudioBuffer<float>& , MidiBuffer&) override;
void processBlock (AudioBuffer<double>&, MidiBuffer&) override;
bool supportsDoublePrecisionProcessing() const override;
const String getInputChannelName (int channelIndex) const override;
const String getOutputChannelName (int channelIndex) const override;
@@ -340,6 +341,10 @@ public:
const IODeviceType type;
AudioProcessorGraph* graph;
//==============================================================================
template <typename floatType>
void processAudio (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor)
};
@@ -347,7 +352,9 @@ public:
const String getName() const override;
void prepareToPlay (double, int) override;
void releaseResources() override;
void processBlock (AudioSampleBuffer&, MidiBuffer&) override;
void processBlock (AudioBuffer<float>&, MidiBuffer&) override;
void processBlock (AudioBuffer<double>&, MidiBuffer&) override;
bool supportsDoublePrecisionProcessing() const override;
void reset() override;
void setNonRealtime (bool) noexcept override;
@@ -376,17 +383,21 @@ public:
void setStateInformation (const void* data, int sizeInBytes) override;
private:
//==============================================================================
template <typename floatType>
void processAudio (AudioBuffer<floatType>& buffer, MidiBuffer& midiMessages);
//==============================================================================
ReferenceCountedArray<Node> nodes;
OwnedArray<Connection> connections;
uint32 lastNodeId;
AudioSampleBuffer renderingBuffers;
OwnedArray<MidiBuffer> midiBuffers;
Array<void*> renderingOps;
friend class AudioGraphIOProcessor;
AudioSampleBuffer* currentAudioInputBuffer;
AudioSampleBuffer currentAudioOutputBuffer;
struct AudioProcessorGraphBufferHelpers;
ScopedPointer<AudioProcessorGraphBufferHelpers> audioBuffers;
MidiBuffer* currentMidiInputBuffer;
MidiBuffer currentMidiOutputBuffer;


+ 63
- 0
libs/juce/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
libs/juce/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
libs/juce/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
libs/juce/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
libs/juce/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
libs/juce/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 limitRange (v) / (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
libs/juce/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
libs/juce/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

+ 38
- 2
libs/juce/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp View File

@@ -80,12 +80,16 @@ public:
deviceManager (dm),
noItemsMessage (noItems)
{
items = MidiInput::getDevices();
updateDevices();
setModel (this);
setOutlineThickness (1);
}
void updateDevices()
{
items = MidiInput::getDevices();
}
int getNumRows() override
{
return items.size();
@@ -1011,11 +1015,19 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager&
midiInputsLabel = new Label (String::empty, TRANS ("Active MIDI inputs:"));
midiInputsLabel->setJustificationType (Justification::topRight);
midiInputsLabel->attachToComponent (midiInputsList, true);
if (BluetoothMidiDevicePairingDialogue::isAvailable())
{
addAndMakeVisible (bluetoothButton = new TextButton (TRANS("Bluetooth MIDI"),
TRANS("Scan for bluetooth MIDI devices")));
bluetoothButton->addListener (this);
}
}
else
{
midiInputsList = nullptr;
midiInputsLabel = nullptr;
bluetoothButton = nullptr;
}
if (showMidiOutputSelector)
@@ -1034,6 +1046,7 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager&
deviceManager.addChangeListener (this);
updateAllControls();
startTimer (1000);
}
AudioDeviceSelectorComponent::~AudioDeviceSelectorComponent()
@@ -1073,10 +1086,26 @@ void AudioDeviceSelectorComponent::resized()
r.removeFromTop (space);
}
if (bluetoothButton != nullptr)
{
bluetoothButton->setBounds (r.removeFromTop (24));
r.removeFromTop (space);
}
if (midiOutputSelector != nullptr)
midiOutputSelector->setBounds (r.removeFromTop (itemHeight));
}
void AudioDeviceSelectorComponent::timerCallback()
{
// TODO
// unfortunately, the AudioDeviceManager only gives us changeListenerCallbacks
// if an audio device has changed, but not if a MIDI device has changed.
// This needs to be implemented properly. Until then, we use a workaround
// where we update the whole component once per second on a timer callback.
updateAllControls();
}
void AudioDeviceSelectorComponent::comboBoxChanged (ComboBox* comboBoxThatHasChanged)
{
if (comboBoxThatHasChanged == deviceTypeDropDown)
@@ -1133,6 +1162,7 @@ void AudioDeviceSelectorComponent::updateAllControls()
if (midiInputsList != nullptr)
{
midiInputsList->updateDevices();
midiInputsList->updateContent();
midiInputsList->repaint();
}
@@ -1159,3 +1189,9 @@ void AudioDeviceSelectorComponent::updateAllControls()
resized();
}
void AudioDeviceSelectorComponent::buttonClicked (Button* btn)
{
if (bluetoothButton == btn)
BluetoothMidiDevicePairingDialogue::open();
}

+ 9
- 1
libs/juce/source/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.h View File

@@ -37,7 +37,9 @@
*/
class JUCE_API AudioDeviceSelectorComponent : public Component,
private ComboBoxListener, // (can't use ComboBox::Listener due to idiotic VC2005 bug)
private ChangeListener
private ChangeListener,
private Button::Listener,
private Timer
{
public:
//==============================================================================
@@ -84,8 +86,13 @@ public:
//==============================================================================
/** @internal */
void resized() override;
/** @internal */
void timerCallback() override;
private:
//==============================================================================
void buttonClicked (Button*) override;
//==============================================================================
ScopedPointer<ComboBox> deviceTypeDropDown;
ScopedPointer<Label> deviceTypeDropDownLabel;
@@ -101,6 +108,7 @@ private:
ScopedPointer<MidiInputSelectorComponentListBox> midiInputsList;
ScopedPointer<ComboBox> midiOutputSelector;
ScopedPointer<Label> midiInputsLabel, midiOutputLabel;
ScopedPointer<TextButton> bluetoothButton;
void comboBoxChanged (ComboBox*) override;
void changeListenerCallback (ChangeBroadcaster*) override;


+ 76
- 0
libs/juce/source/modules/juce_audio_utils/gui/juce_BluetoothMidiDevicePairingDialogue.h View File

@@ -0,0 +1,76 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI 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_BLUETOOTHMIDIDEVICPAIRINGCOMPONENT_H_INCLUDED
#define JUCE_BLUETOOTHMIDIDEVICPAIRINGCOMPONENT_H_INCLUDED
class BluetoothMidiSelectorOverlay;
//==============================================================================
/**
Opens a Bluetooth MIDI pairing dialogue that allows the user to view and
connect to Bluetooth MIDI devices that are currently found nearby.
The dialogue will ignore non-MIDI Bluetooth devices.
Only after a Bluetooth MIDI device has been paired will its MIDI ports
be available through JUCE's MidiInput and MidiOutput classes.
This dialogue is currently only available on iOS and Android. On OSX,
you should instead pair Bluetooth MIDI devices using the "Audio MIDI Setup"
app (located in /Applications/Utilities). On Windows, you should use
the system settings. On Linux, Bluetooth MIDI devices are currently not
supported.
*/
class BluetoothMidiDevicePairingDialogue
{
public:
/** Opens the Bluetooth MIDI pairing dialogue, if it is available.
@return true if the dialogue was opened, false on error.
*/
static bool open();
/** Checks if a Bluetooth MIDI pairing dialogue is available on this
platform.
On iOS, this will be true for iOS versions 8.0 and higher.
On Android, this will be true only for Android SDK versions 23 and
higher, and additionally only if the device itself supports MIDI
over Bluetooth.
On deskrop platforms, this will typically be false as the bluetooth
pairing is not done inside the app but by other means.
@return true if the Bluetooth MIDI pairing dialogue is available,
false otherwise.
*/
static bool isAvailable();
};
#endif // JUCE_BLUETOOTHMIDIDEVICPAIRINGCOMPONENT_H_INCLUDED

+ 17
- 0
libs/juce/source/modules/juce_audio_utils/juce_audio_utils.cpp View File

@@ -49,4 +49,21 @@ namespace juce
#include "gui/juce_AudioAppComponent.cpp"
#include "players/juce_AudioProcessorPlayer.cpp"
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
#include "../juce_gui_extra/embedding/juce_UIViewComponent.h"
#endif
#if JUCE_MAC
#include "native/juce_mac_BluetoothMidiDevicePairingDialogue.mm"
#elif JUCE_IOS
#include "native/juce_ios_BluetoothMidiDevicePairingDialogue.mm"
#elif JUCE_ANDROID
#include "../juce_core/native/juce_android_JNIHelpers.h"
#include "native/juce_android_BluetoothMidiDevicePairingDialogue.cpp"
#elif JUCE_LINUX
#include "native/juce_linux_BluetoothMidiDevicePairingDialogue.cpp"
#elif JUCE_WINDOWS
#include "native/juce_win_BluetoothMidiDevicePairingDialogue.cpp"
#endif
}

+ 1
- 0
libs/juce/source/modules/juce_audio_utils/juce_audio_utils.h View File

@@ -41,6 +41,7 @@ namespace juce
#include "gui/juce_AudioVisualiserComponent.h"
#include "gui/juce_MidiKeyboardComponent.h"
#include "gui/juce_AudioAppComponent.h"
#include "gui/juce_BluetoothMidiDevicePairingDialogue.h"
#include "players/juce_AudioProcessorPlayer.h"
}


+ 5
- 2
libs/juce/source/modules/juce_audio_utils/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_audio_utils",
"name": "JUCE extra audio utility classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for audio-related GUI and miscellaneous tasks.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -17,5 +17,8 @@
{ "file": "juce_audio_utils.mm", "target": "xcode" } ],
"browse": [ "gui/*",
"players/*" ]
"players/*",
"native/*" ],
"iOSFrameworks": "CoreAudioKit"
}

+ 438
- 0
libs/juce/source/modules/juce_audio_utils/native/juce_android_BluetoothMidiDevicePairingDialogue.cpp View File

@@ -0,0 +1,438 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI 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.
==============================================================================
*/
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (getMidiBluetoothAddresses, "getMidiBluetoothAddresses", "()[Ljava/lang/String;") \
METHOD (pairBluetoothMidiDevice, "pairBluetoothMidiDevice", "(Ljava/lang/String;)Z") \
METHOD (unpairBluetoothMidiDevice, "unpairBluetoothMidiDevice", "(Ljava/lang/String;)V") \
METHOD (getHumanReadableStringForBluetoothAddress, "getHumanReadableStringForBluetoothAddress", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (isBluetoothDevicePaired, "isBluetoothDevicePaired", "(Ljava/lang/String;)Z")
DECLARE_JNI_CLASS (AndroidBluetoothManager, JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager");
#undef JNI_CLASS_MEMBERS
//==============================================================================
struct AndroidBluetoothMidiInterface
{
static StringArray getBluetoothMidiDevicesNearby()
{
StringArray retval;
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
// if this is null then bluetooth is not enabled
if (btManager.get() == nullptr)
return StringArray();
jobjectArray jDevices = (jobjectArray) env->CallObjectMethod (btManager.get(),
AndroidBluetoothManager.getMidiBluetoothAddresses);
LocalRef<jobjectArray> devices (jDevices);
const int count = env->GetArrayLength (devices.get());
for (int i = 0; i < count; ++i)
{
LocalRef<jstring> string ((jstring) env->GetObjectArrayElement (devices.get(), i));
retval.add (juceString (string));
}
return retval;
}
//==========================================================================
static bool pairBluetoothMidiDevice (const String& bluetoothAddress)
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
if (btManager.get() == nullptr)
return false;
jboolean result = env->CallBooleanMethod (btManager.get(), AndroidBluetoothManager.pairBluetoothMidiDevice,
javaString (bluetoothAddress).get());
return result;
}
static void unpairBluetoothMidiDevice (const String& bluetoothAddress)
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
if (btManager.get() != nullptr)
env->CallVoidMethod (btManager.get(), AndroidBluetoothManager.unpairBluetoothMidiDevice,
javaString (bluetoothAddress).get());
}
//==========================================================================
static String getHumanReadableStringForBluetoothAddress (const String& address)
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
if (btManager.get() == nullptr)
return address;
LocalRef<jstring> string ((jstring) env->CallObjectMethod (btManager.get(),
AndroidBluetoothManager.getHumanReadableStringForBluetoothAddress,
javaString (address).get()));
if (string.get() == nullptr)
return address;
return juceString (string);
}
//==========================================================================
static bool isBluetoothDevicePaired (const String& address)
{
JNIEnv* env = getEnv();
LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
if (btManager.get() == nullptr)
return false;
return env->CallBooleanMethod (btManager.get(), AndroidBluetoothManager.isBluetoothDevicePaired,
javaString (address).get());
}
};
//==============================================================================
struct AndroidBluetoothMidiDevice
{
enum ConnectionStatus
{
offline,
connected,
disconnected,
connecting,
disconnecting
};
AndroidBluetoothMidiDevice (String deviceName, String address, ConnectionStatus status)
: name (deviceName), bluetoothAddress (address), connectionStatus (status)
{
// can't create a device without a valid name and bluetooth address!
jassert (! name.isEmpty());
jassert (! bluetoothAddress.isEmpty());
}
bool operator== (const AndroidBluetoothMidiDevice& other) const noexcept
{
return bluetoothAddress == other.bluetoothAddress;
}
bool operator!= (const AndroidBluetoothMidiDevice& other) const noexcept
{
return ! operator== (other);
}
const String name, bluetoothAddress;
ConnectionStatus connectionStatus;
};
//==============================================================================
class AndroidBluetoothMidiDevicesListBox : public ListBox,
private ListBoxModel,
private Timer
{
public:
//==========================================================================
AndroidBluetoothMidiDevicesListBox()
: timerPeriodInMs (1000)
{
setRowHeight (40);
setModel (this);
setOutlineThickness (1);
updateDeviceList();
startTimer (timerPeriodInMs);
}
void pairDeviceThreadFinished() // callback from PairDeviceThread
{
updateDeviceList();
startTimer (timerPeriodInMs);
}
private:
//==========================================================================
typedef AndroidBluetoothMidiDevice::ConnectionStatus DeviceStatus;
int getNumRows() override
{
return devices.size();
}
void paintListBoxItem (int rowNumber, Graphics& g,
int width, int height, bool rowIsSelected) override
{
if (isPositiveAndBelow (rowNumber, devices.size()))
{
const AndroidBluetoothMidiDevice& device = devices.getReference (rowNumber);
const String statusString (getDeviceStatusString (device.connectionStatus));
g.fillAll (Colours::white);
const float xmargin = 3.0f;
const float ymargin = 3.0f;
const float fontHeight = 0.4f * height;
const float deviceNameWidth = 0.6f * width;
g.setFont (fontHeight);
g.setColour (getDeviceNameFontColour (device.connectionStatus));
g.drawText (device.name,
xmargin, ymargin,
deviceNameWidth - (2.0f * xmargin), height - (2.0f * ymargin),
Justification::topLeft, true);
g.setColour (getDeviceStatusFontColour (device.connectionStatus));
g.drawText (statusString,
deviceNameWidth + xmargin, ymargin,
width - deviceNameWidth - (2.0f * xmargin), height - (2.0f * ymargin),
Justification::topRight, true);
g.setColour (Colours::grey);
g.drawHorizontalLine (height - 1, xmargin, width);
}
}
//==========================================================================
static Colour getDeviceNameFontColour (DeviceStatus deviceStatus) noexcept
{
if (deviceStatus == AndroidBluetoothMidiDevice::offline)
return Colours::grey;
return Colours::black;
}
static Colour getDeviceStatusFontColour (DeviceStatus deviceStatus) noexcept
{
if (deviceStatus == AndroidBluetoothMidiDevice::offline
|| deviceStatus == AndroidBluetoothMidiDevice::connecting
|| deviceStatus == AndroidBluetoothMidiDevice::disconnecting)
return Colours::grey;
if (deviceStatus == AndroidBluetoothMidiDevice::connected)
return Colours::green;
return Colours::black;
}
static String getDeviceStatusString (DeviceStatus deviceStatus) noexcept
{
if (deviceStatus == AndroidBluetoothMidiDevice::offline) return "Offline";
if (deviceStatus == AndroidBluetoothMidiDevice::connected) return "Connected";
if (deviceStatus == AndroidBluetoothMidiDevice::disconnected) return "Not connected";
if (deviceStatus == AndroidBluetoothMidiDevice::connecting) return "Connecting...";
if (deviceStatus == AndroidBluetoothMidiDevice::disconnecting) return "Disconnecting...";
// unknown device state!
jassertfalse;
return "Status unknown";
}
//==========================================================================
void listBoxItemClicked (int row, const MouseEvent&) override
{
const AndroidBluetoothMidiDevice& device = devices.getReference (row);
if (device.connectionStatus == AndroidBluetoothMidiDevice::disconnected)
disconnectedDeviceClicked (row);
else if (device.connectionStatus == AndroidBluetoothMidiDevice::connected)
connectedDeviceClicked (row);
}
void timerCallback() override
{
updateDeviceList();
}
//==========================================================================
struct PairDeviceThread : public Thread,
private AsyncUpdater
{
PairDeviceThread (const String& bluetoothAddressOfDeviceToPair,
AndroidBluetoothMidiDevicesListBox& ownerListBox)
: Thread ("JUCE Bluetooth MIDI Device Pairing Thread"),
bluetoothAddress (bluetoothAddressOfDeviceToPair),
owner (&ownerListBox)
{
startThread();
}
void run() override
{
AndroidBluetoothMidiInterface::pairBluetoothMidiDevice (bluetoothAddress);
triggerAsyncUpdate();
}
void handleAsyncUpdate() override
{
if (owner != nullptr)
owner->pairDeviceThreadFinished();
delete this;
}
private:
String bluetoothAddress;
Component::SafePointer<AndroidBluetoothMidiDevicesListBox> owner;
};
//==========================================================================
void disconnectedDeviceClicked (int row)
{
stopTimer();
AndroidBluetoothMidiDevice& device = devices.getReference (row);
device.connectionStatus = AndroidBluetoothMidiDevice::connecting;
updateContent();
repaint();
new PairDeviceThread (device.bluetoothAddress, *this);
}
void connectedDeviceClicked (int row)
{
AndroidBluetoothMidiDevice& device = devices.getReference (row);
device.connectionStatus = AndroidBluetoothMidiDevice::disconnecting;
updateContent();
repaint();
AndroidBluetoothMidiInterface::unpairBluetoothMidiDevice (device.bluetoothAddress);
}
//==========================================================================
void updateDeviceList()
{
StringArray bluetoothAddresses = AndroidBluetoothMidiInterface::getBluetoothMidiDevicesNearby();
Array<AndroidBluetoothMidiDevice> newDevices;
for (String* address = bluetoothAddresses.begin();
address != bluetoothAddresses.end(); ++address)
{
String name = AndroidBluetoothMidiInterface::getHumanReadableStringForBluetoothAddress (*address);
DeviceStatus status = AndroidBluetoothMidiInterface::isBluetoothDevicePaired (*address)
? AndroidBluetoothMidiDevice::connected
: AndroidBluetoothMidiDevice::disconnected;
newDevices.add (AndroidBluetoothMidiDevice (name, *address, status));
}
devices.swapWith (newDevices);
updateContent();
repaint();
}
Array<AndroidBluetoothMidiDevice> devices;
const int timerPeriodInMs;
};
//==============================================================================
class BluetoothMidiSelectorOverlay : public Component
{
public:
BluetoothMidiSelectorOverlay()
{
setAlwaysOnTop (true);
setVisible (true);
addToDesktop (ComponentPeer::windowHasDropShadow);
setBounds (0, 0, getParentWidth(), getParentHeight());
toFront (true);
addAndMakeVisible (bluetoothDevicesList);
enterModalState (true, nullptr, true);
}
void paint (Graphics& g) override
{
g.fillAll (Colours::black.withAlpha (0.6f));
g.setColour (Colour (0xffdfdfdf));
Rectangle<int> overlayBounds = getOverlayBounds();
g.fillRect (overlayBounds);
g.setColour (Colours::black);
g.setFont (16);
g.drawText ("Bluetooth MIDI Devices",
overlayBounds.removeFromTop (20).reduced (3, 3),
Justification::topLeft, true);
overlayBounds.removeFromTop (2);
g.setFont (12);
g.drawText ("tap to connect/disconnect",
overlayBounds.removeFromTop (18).reduced (3, 3),
Justification::topLeft, true);
}
void inputAttemptWhenModal() override { exitModalState (0); }
void mouseDrag (const MouseEvent&) override {}
void mouseDown (const MouseEvent&) override { exitModalState (0); }
void resized() override { update(); }
void parentSizeChanged() override { update(); }
private:
void update()
{
setBounds (0, 0, getParentWidth(), getParentHeight());
bluetoothDevicesList.setBounds (getOverlayBounds().withTrimmedTop (40));
}
Rectangle<int> getOverlayBounds() const noexcept
{
const int pw = getParentWidth();
const int ph = getParentHeight();
return Rectangle<int> (pw, ph).withSizeKeepingCentre (jmin (400, pw - 14),
jmin (300, ph - 40));
}
AndroidBluetoothMidiDevicesListBox bluetoothDevicesList;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BluetoothMidiSelectorOverlay)
};
//==============================================================================
bool BluetoothMidiDevicePairingDialogue::open()
{
BluetoothMidiSelectorOverlay* overlay = new BluetoothMidiSelectorOverlay;
return true;
}
bool BluetoothMidiDevicePairingDialogue::isAvailable()
{
jobject btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
return btManager != nullptr;
}

+ 138
- 0
libs/juce/source/modules/juce_audio_utils/native/juce_ios_BluetoothMidiDevicePairingDialogue.mm View File

@@ -0,0 +1,138 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI 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.
==============================================================================
*/
// Note: for the Bluetooth Midi selector overlay, we need the class
// UIViewComponent from the juce_gui_extra module. If this module is not
// included in your app, BluetoothMidiDevicePairingDialogue::open() will fail
// and return false.
// It is also not available in the iPhone/iPad simulator.
#if JUCE_MODULE_AVAILABLE_juce_gui_extra && ! TARGET_IPHONE_SIMULATOR
} // (juce namespace)
#include <CoreAudioKit/CoreAudioKit.h>
//==============================================================================
@interface BluetoothSelectorView : NSObject
@property CABTMIDICentralViewController *central;
- (UIView*) getView;
@end
//==============================================================================
@implementation BluetoothSelectorView
- (instancetype) init
{
self = [super init];
self.central = [[CABTMIDICentralViewController alloc] init];
return self;
}
- (UIView*) getView
{
return self.central.view;
}
@end
namespace juce
{
//==============================================================================
class BluetoothMidiSelectorOverlay : public Component
{
public:
BluetoothMidiSelectorOverlay()
{
setAlwaysOnTop (true);
setVisible (true);
addToDesktop (ComponentPeer::windowHasDropShadow);
setBounds (0, 0, getParentWidth(), getParentHeight());
toFront (true);
nativeSelectorComponent.setView ([[[BluetoothSelectorView alloc] init] getView]);
addAndMakeVisible (nativeSelectorComponent);
enterModalState (true, nullptr, true);
}
void paint (Graphics& g) override
{
g.fillAll (Colours::black.withAlpha (0.5f));
}
void inputAttemptWhenModal() override { close(); }
void mouseDrag (const MouseEvent&) override {}
void mouseDown (const MouseEvent&) override { close(); }
void resized () override { update(); }
void parentSizeChanged() override { update(); }
private:
void update()
{
const int pw = getParentWidth();
const int ph = getParentHeight();
nativeSelectorComponent.setBounds (Rectangle<int> (pw, ph)
.withSizeKeepingCentre (jmin (400, pw - 14),
jmin (500, ph - 40)));
}
void close()
{
exitModalState (0);
setVisible (false);
}
UIViewComponent nativeSelectorComponent;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BluetoothMidiSelectorOverlay)
};
#endif // JUCE_MODULE_AVAILABLE_juce_gui_extra && ! TARGET_IPHONE_SIMULATOR
//==============================================================================
bool BluetoothMidiDevicePairingDialogue::open()
{
#if JUCE_MODULE_AVAILABLE_juce_gui_extra && ! TARGET_IPHONE_SIMULATOR
if (isAvailable())
{
new BluetoothMidiSelectorOverlay();
return true;
}
#endif
return false;
}
bool BluetoothMidiDevicePairingDialogue::isAvailable()
{
#if JUCE_MODULE_AVAILABLE_juce_gui_extra && ! TARGET_IPHONE_SIMULATOR
return NSClassFromString ([NSString stringWithUTF8String: "CABTMIDICentralViewController"]) != nil;
#else
return false;
#endif
}

+ 37
- 0
libs/juce/source/modules/juce_audio_utils/native/juce_linux_BluetoothMidiDevicePairingDialogue.cpp View File

@@ -0,0 +1,37 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI 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.
==============================================================================
*/
bool BluetoothMidiDevicePairingDialogue::open()
{
// not implemented on Linux yet!
// You should check whether the dialogue is available on your system
// using isAvailable() before calling open().
jassertfalse;
return false;
}
bool BluetoothMidiDevicePairingDialogue::isAvailable()
{
return false;
}

+ 36
- 0
libs/juce/source/modules/juce_audio_utils/native/juce_mac_BluetoothMidiDevicePairingDialogue.mm View File

@@ -0,0 +1,36 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI 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.
==============================================================================
*/
bool BluetoothMidiDevicePairingDialogue::open()
{
// Do not call this on OSX. Instead, you should pair Bluetooth MIDI devices
// using the "Audio MIDI Setup" app (located in /Applications/Utilities).
jassertfalse;
return false;
}
bool BluetoothMidiDevicePairingDialogue::isAvailable()
{
return false;
}

+ 37
- 0
libs/juce/source/modules/juce_audio_utils/native/juce_win_BluetoothMidiDevicePairingDialogue.cpp View File

@@ -0,0 +1,37 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI 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.
==============================================================================
*/
bool BluetoothMidiDevicePairingDialogue::open()
{
// not implemented on Windows yet!
// You should check whether the dialogue is available on your system
// using isAvailable() before calling open().
jassertfalse;
return false;
}
bool BluetoothMidiDevicePairingDialogue::isAvailable()
{
return false;
}

+ 41
- 2
libs/juce/source/modules/juce_audio_utils/players/juce_AudioProcessorPlayer.cpp View File

@@ -22,11 +22,12 @@
==============================================================================
*/
AudioProcessorPlayer::AudioProcessorPlayer()
AudioProcessorPlayer::AudioProcessorPlayer(bool doDoublePrecisionProcessing)
: processor (nullptr),
sampleRate (0),
blockSize (0),
isPrepared (false),
isDoublePrecision (doDoublePrecisionProcessing),
numInputChans (0),
numOutputChans (0)
{
@@ -45,6 +46,12 @@ void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay)
if (processorToPlay != nullptr && sampleRate > 0 && blockSize > 0)
{
processorToPlay->setPlayConfigDetails (numInputChans, numOutputChans, sampleRate, blockSize);
const bool supportsDouble = processorToPlay->supportsDoublePrecisionProcessing() && isDoublePrecision;
AudioProcessor::ProcessingPrecision precision = supportsDouble ? AudioProcessor::doublePrecision
: AudioProcessor::singlePrecision;
processorToPlay->setProcessingPrecision (precision);
processorToPlay->prepareToPlay (sampleRate, blockSize);
}
@@ -62,6 +69,28 @@ void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay)
}
}
void AudioProcessorPlayer::setDoublePrecisionProcessing (bool doublePrecision)
{
if (doublePrecision != isDoublePrecision)
{
const ScopedLock sl (lock);
if (processor != nullptr)
{
processor->releaseResources();
const bool supportsDouble = processor->supportsDoublePrecisionProcessing() && doublePrecision;
AudioProcessor::ProcessingPrecision precision = supportsDouble ? AudioProcessor::doublePrecision
: AudioProcessor::singlePrecision;
processor->setProcessingPrecision (precision);
processor->prepareToPlay (sampleRate, blockSize);
}
isDoublePrecision = doublePrecision;
}
}
//==============================================================================
void AudioProcessorPlayer::audioDeviceIOCallback (const float** const inputChannelData,
const int numInputChannels,
@@ -126,7 +155,17 @@ void AudioProcessorPlayer::audioDeviceIOCallback (const float** const inputChann
if (! processor->isSuspended())
{
processor->processBlock (buffer, incomingMidi);
if (processor->isUsingDoublePrecision())
{
conversionBuffer.makeCopyOf (buffer);
processor->processBlock (conversionBuffer, incomingMidi);
buffer.makeCopyOf (conversionBuffer);
}
else
{
processor->processBlock (buffer, incomingMidi);
}
return;
}
}


+ 18
- 4
libs/juce/source/modules/juce_audio_utils/players/juce_AudioProcessorPlayer.h View File

@@ -25,7 +25,6 @@
#ifndef JUCE_AUDIOPROCESSORPLAYER_H_INCLUDED
#define JUCE_AUDIOPROCESSORPLAYER_H_INCLUDED
//==============================================================================
/**
An AudioIODeviceCallback object which streams audio through an AudioProcessor.
@@ -43,7 +42,7 @@ class JUCE_API AudioProcessorPlayer : public AudioIODeviceCallback,
{
public:
//==============================================================================
AudioProcessorPlayer();
AudioProcessorPlayer(bool doDoublePrecisionProcessing = false);
/** Destructor. */
virtual ~AudioProcessorPlayer();
@@ -65,6 +64,20 @@ public:
*/
MidiMessageCollector& getMidiMessageCollector() noexcept { return messageCollector; }
/** Switch between double and single floating point precisions processing.
The audio IO callbacks will still operate in single floating point
precision, however, all internal processing including the
AudioProcessor will be processed in double floating point precision if
the AudioProcessor supports it (see
AudioProcessor::supportsDoublePrecisionProcessing()).
Otherwise, the processing will remain single precision irrespective of
the parameter doublePrecision. */
void setDoublePrecisionProcessing (bool doublePrecision);
/** Returns true if this player processes internally processes the samples with
double floating point precision. */
inline bool getDoublePrecisionProcessing() { return isDoublePrecision; }
//==============================================================================
/** @internal */
void audioDeviceIOCallback (const float**, int, float**, int, int) override;
@@ -81,11 +94,12 @@ private:
CriticalSection lock;
double sampleRate;
int blockSize;
bool isPrepared;
bool isPrepared, isDoublePrecision;
int numInputChans, numOutputChans;
HeapBlock<float*> channels;
AudioSampleBuffer tempBuffer;
AudioBuffer<float> tempBuffer;
AudioBuffer<double> conversionBuffer;
MidiBuffer incomingMidi;
MidiMessageCollector messageCollector;


+ 7
- 2
libs/juce/source/modules/juce_core/containers/juce_Array.h View File

@@ -216,13 +216,18 @@ public:
}
//==============================================================================
/** Returns the current number of elements in the array.
*/
/** Returns the current number of elements in the array. */
inline int size() const noexcept
{
return numUsed;
}
/** Returns true if the array is empty, false otherwise. */
inline bool empty() const noexcept
{
return size() == 0;
}
/** Returns one of the elements in the array.
If the index passed in is beyond the range of valid elements, this
will return a default value.


+ 35
- 0
libs/juce/source/modules/juce_core/files/juce_File.cpp View File

@@ -886,6 +886,41 @@ File File::createTempFile (StringRef fileNameEnding)
return tempFile;
}
bool File::createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const
{
if (linkFileToCreate.exists())
{
if (! linkFileToCreate.isSymbolicLink())
{
// user has specified an existing file / directory as the link
// this is bad! the user could end up unintentionally destroying data
jassertfalse;
return false;
}
if (overwriteExisting)
linkFileToCreate.deleteFile();
}
#if JUCE_MAC || JUCE_LINUX
// one common reason for getting an error here is that the file already exists
if (symlink (fullPath.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1)
{
jassertfalse;
return false;
}
return true;
#elif JUCE_WINDOWS
return CreateSymbolicLink (linkFileToCreate.getFullPathName().toWideCharPointer(),
fullPath.toWideCharPointer(),
isDirectory() ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != FALSE;
#else
jassertfalse; // symbolic links not supported on this platform!
return false;
#endif
}
//==============================================================================
MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
: address (nullptr), range (0, file.getSize()), fileHandle (0)


+ 21
- 15
libs/juce/source/modules/juce_core/files/juce_File.h View File

@@ -360,14 +360,6 @@ public:
*/
bool isHidden() const;
/** Returns true if this file is a link or alias that can be followed using getLinkedTarget(). */
bool isLink() const;
/** If this file is a link or alias, this returns the file that it points to.
If the file isn't actually link, it'll just return itself.
*/
File getLinkedTarget() const;
/** Returns a unique identifier for the file, if one is available.
Depending on the OS and file-system, this may be a unix inode number or
@@ -880,7 +872,6 @@ public:
*/
static File createTempFile (StringRef fileNameEnding);
//==============================================================================
/** Returns the current working directory.
@see setAsCurrentWorkingDirectory
@@ -946,8 +937,28 @@ public:
/** Adds a separator character to the end of a path if it doesn't already have one. */
static String addTrailingSeparator (const String& path);
#if JUCE_MAC || JUCE_IOS || DOXYGEN
//==============================================================================
/** Tries to create a symbolic link and returns a boolean to indicate success */
bool createSymbolicLink (const File& linkFileToCreate, bool overwriteExisting) const;
/** Returns true if this file is a link or alias that can be followed using getLinkedTarget(). */
bool isSymbolicLink() const;
/** If this file is a link or alias, this returns the file that it points to.
If the file isn't actually link, it'll just return itself.
*/
File getLinkedTarget() const;
#if JUCE_WINDOWS
/** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */
bool createShortcut (const String& description, const File& linkFileToCreate) const;
/** Windows ONLY - Returns true if this is a win32 .LNK file. */
bool isShortcut() const;
#endif
//==============================================================================
#if JUCE_MAC || JUCE_IOS || DOXYGEN
/** OSX ONLY - Finds the OSType of a file from the its resources. */
OSType getMacOSType() const;
@@ -960,11 +971,6 @@ public:
void addToDock() const;
#endif
#if JUCE_WINDOWS
/** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */
bool createLink (const String& description, const File& linkFileToCreate) const;
#endif
private:
//==============================================================================
String fullPath;


+ 1
- 1
libs/juce/source/modules/juce_core/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_core",
"name": "JUCE core classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.",
"website": "http://www.juce.com/juce",
"license": "ISC Permissive",


+ 2
- 0
libs/juce/source/modules/juce_core/maths/juce_NormalisableRange.h View File

@@ -136,6 +136,8 @@ public:
return v;
}
Range<ValueType> getRange() const noexcept { return Range<ValueType> (start, end); }
/** The start of the non-normalised range. */
ValueType start;


+ 60
- 6
libs/juce/source/modules/juce_core/memory/juce_ByteOrder.h View File

@@ -48,24 +48,54 @@ public:
static uint64 swap (uint64 value) noexcept;
//==============================================================================
/** Swaps the byte order of a 16-bit int if the CPU is big-endian */
/** Swaps the byte order of a 16-bit unsigned int if the CPU is big-endian */
static uint16 swapIfBigEndian (uint16 value) noexcept;
/** Swaps the byte order of a 32-bit int if the CPU is big-endian */
/** Swaps the byte order of a 32-bit unsigned int if the CPU is big-endian */
static uint32 swapIfBigEndian (uint32 value) noexcept;
/** Swaps the byte order of a 64-bit int if the CPU is big-endian */
/** Swaps the byte order of a 64-bit unsigned int if the CPU is big-endian */
static uint64 swapIfBigEndian (uint64 value) noexcept;
/** Swaps the byte order of a 16-bit int if the CPU is little-endian */
/** Swaps the byte order of a 16-bit signed int if the CPU is big-endian */
static int16 swapIfBigEndian (int16 value) noexcept;
/** Swaps the byte order of a 32-bit signed int if the CPU is big-endian */
static int32 swapIfBigEndian (int32 value) noexcept;
/** Swaps the byte order of a 64-bit signed int if the CPU is big-endian */
static int64 swapIfBigEndian (int64 value) noexcept;
/** Swaps the byte order of a 32-bit float if the CPU is big-endian */
static float swapIfBigEndian (float value) noexcept;
/** Swaps the byte order of a 64-bit float if the CPU is big-endian */
static double swapIfBigEndian (double value) noexcept;
/** Swaps the byte order of a 16-bit unsigned int if the CPU is little-endian */
static uint16 swapIfLittleEndian (uint16 value) noexcept;
/** Swaps the byte order of a 32-bit int if the CPU is little-endian */
/** Swaps the byte order of a 32-bit unsigned int if the CPU is little-endian */
static uint32 swapIfLittleEndian (uint32 value) noexcept;
/** Swaps the byte order of a 64-bit int if the CPU is little-endian */
/** Swaps the byte order of a 64-bit unsigned int if the CPU is little-endian */
static uint64 swapIfLittleEndian (uint64 value) noexcept;
/** Swaps the byte order of a 16-bit signed int if the CPU is little-endian */
static int16 swapIfLittleEndian (int16 value) noexcept;
/** Swaps the byte order of a 32-bit signed int if the CPU is little-endian */
static int32 swapIfLittleEndian (int32 value) noexcept;
/** Swaps the byte order of a 64-bit signed int if the CPU is little-endian */
static int64 swapIfLittleEndian (int64 value) noexcept;
/** Swaps the byte order of a 32-bit float if the CPU is little-endian */
static float swapIfLittleEndian (float value) noexcept;
/** Swaps the byte order of a 64-bit float if the CPU is little-endian */
static double swapIfLittleEndian (double value) noexcept;
//==============================================================================
/** Turns 4 bytes into a little-endian integer. */
static uint32 littleEndianInt (const void* bytes) noexcept;
@@ -161,9 +191,21 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return v; }
inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return v; }
inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return v; }
inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return v; }
inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return v; }
inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return v; }
inline float ByteOrder::swapIfBigEndian (const float v) noexcept { return v; }
inline double ByteOrder::swapIfBigEndian (const double v) noexcept { return v; }
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); }
inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return static_cast<int32> (swap (static_cast<uint32> (v))); }
inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return static_cast<int64> (swap (static_cast<uint64> (v))); }
inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast<const uint32*> (bytes); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast<const uint64*> (bytes); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast<const uint16*> (bytes); }
@@ -175,9 +217,21 @@ inline uint64 ByteOrder::swap (uint64 value) noexcept
inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); }
inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return swap (v); }
inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return swap (v); }
inline int16 ByteOrder::swapIfBigEndian (const int16 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline int32 ByteOrder::swapIfBigEndian (const int32 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline int64 ByteOrder::swapIfBigEndian (const int64 v) noexcept { return static_cast<int16> (swap (static_cast<uint16> (v))); }
inline float ByteOrder::swapIfBigEndian (const float v) noexcept { union { uint32 asUInt; float asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline double ByteOrder::swapIfBigEndian (const double v) noexcept { union { uint64 asUInt; double asFloat; } n; n.asFloat = v; n.asUInt = ByteOrder::swap (n.asUInt); return n.asFloat; }
inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; }
inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; }
inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; }
inline int16 ByteOrder::swapIfLittleEndian (const int16 v) noexcept { return v; }
inline int32 ByteOrder::swapIfLittleEndian (const int32 v) noexcept { return v; }
inline int64 ByteOrder::swapIfLittleEndian (const int64 v) noexcept { return v; }
inline float ByteOrder::swapIfLittleEndian (const float v) noexcept { return v; }
inline double ByteOrder::swapIfLittleEndian (const double v) noexcept { return v; }
inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast<const uint32*> (bytes)); }
inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast<const uint64*> (bytes)); }
inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast<const uint16*> (bytes)); }


+ 835
- 0
libs/juce/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
libs/juce/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;
}

+ 222
- 72
libs/juce/source/modules/juce_core/native/java/JuceAppActivity.java View File

@@ -30,28 +30,41 @@ import android.content.DialogInterface;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Looper;
import android.os.Handler;
import android.os.Build;
import android.os.Process;
import android.os.ParcelUuid;
import android.view.*;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.graphics.*;
import android.opengl.*;
import android.text.ClipboardManager;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Log;
import java.lang.Runnable;
import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.TimerTask;
import java.io.*;
import java.net.URL;
import java.net.HttpURLConnection;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.media.AudioManager;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
$$JuceAndroidMidiImports$$ // If you get an error here, you need to re-save your project with the introjucer!
//==============================================================================
public class JuceAppActivity extends Activity
{
@@ -61,6 +74,58 @@ public class JuceAppActivity extends Activity
System.loadLibrary ("juce_jni");
}
//==============================================================================
public static class MidiPortID extends Object
{
public MidiPortID (int index, boolean direction)
{
androidIndex = index;
isInput = direction;
}
public int androidIndex;
public boolean isInput;
@Override
public int hashCode()
{
Integer i = new Integer (androidIndex);
return i.hashCode() * (isInput ? -1 : 1);
}
@Override
public boolean equals (Object obj)
{
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MidiPortID other = (MidiPortID) obj;
return (androidIndex == other.androidIndex && isInput == other.isInput);
}
}
public interface JuceMidiPort
{
boolean isInputPort();
// start, stop does nothing on an output port
void start();
void stop();
void close();
MidiPortID getPortId();
// send will do nothing on an input port
void sendMidi (byte[] msg, int offset, int count);
}
//==============================================================================
$$JuceAndroidMidiCode$$ // If you get an error here, you need to re-save your project with the introjucer!
//==============================================================================
@Override
public void onCreate (Bundle savedInstanceState)
{
@@ -85,9 +150,6 @@ public class JuceAppActivity extends Activity
@Override
protected void onPause()
{
if (viewHolder != null)
viewHolder.onPause();
suspendApp();
super.onPause();
}
@@ -96,10 +158,6 @@ public class JuceAppActivity extends Activity
protected void onResume()
{
super.onResume();
if (viewHolder != null)
viewHolder.onResume();
resumeApp();
}
@@ -142,7 +200,10 @@ public class JuceAppActivity extends Activity
//==============================================================================
private ViewHolder viewHolder;
private MidiDeviceManager midiDeviceManager = null;
private BluetoothManager bluetoothManager = null;
private boolean isScreenSaverEnabled;
private java.util.Timer keepAliveTimer;
public final ComponentPeerView createNewView (boolean opaque, long host)
{
@@ -159,7 +220,7 @@ public class JuceAppActivity extends Activity
group.removeView (view);
}
public final void deleteOpenGLView (OpenGLView view)
public final void deleteNativeSurfaceView (NativeSurfaceView view)
{
ViewGroup group = (ViewGroup) (view.getParent());
@@ -187,28 +248,6 @@ public class JuceAppActivity extends Activity
}
}
public final void onPause()
{
for (int i = getChildCount(); --i >= 0;)
{
View v = getChildAt (i);
if (v instanceof ComponentPeerView)
((ComponentPeerView) v).onPause();
}
}
public final void onResume()
{
for (int i = getChildCount(); --i >= 0;)
{
View v = getChildAt (i);
if (v instanceof ComponentPeerView)
((ComponentPeerView) v).onResume();
}
}
private final int getDPI()
{
DisplayMetrics metrics = new DisplayMetrics();
@@ -230,14 +269,46 @@ public class JuceAppActivity extends Activity
if (isScreenSaverEnabled != enabled)
{
isScreenSaverEnabled = enabled;
if (keepAliveTimer != null)
{
keepAliveTimer.cancel();
keepAliveTimer = null;
}
if (enabled)
{
getWindow().clearFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
else
{
getWindow().addFlags (WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// If no user input is received after about 3 seconds, the OS will lower the
// task's priority, so this timer forces it to be kept active.
keepAliveTimer = new java.util.Timer();
keepAliveTimer.scheduleAtFixedRate (new TimerTask()
{
@Override
public void run()
{
android.app.Instrumentation instrumentation = new android.app.Instrumentation();
try
{
instrumentation.sendKeyDownUpSync (KeyEvent.KEYCODE_UNKNOWN);
}
catch (Exception e)
{
}
}
}, 2000, 2000);
}
}
}
public final boolean getScreenSaver ()
public final boolean getScreenSaver()
{
return isScreenSaverEnabled;
}
@@ -546,70 +617,83 @@ public class JuceAppActivity extends Activity
{
return true; //xxx needs to check overlapping views
}
}
public final void onPause()
{
for (int i = getChildCount(); --i >= 0;)
{
View v = getChildAt (i);
//==============================================================================
public static class NativeSurfaceView extends SurfaceView
implements SurfaceHolder.Callback
{
private long nativeContext = 0;
if (v instanceof OpenGLView)
((OpenGLView) v).onPause();
}
NativeSurfaceView (Context context, long nativeContextPtr)
{
super (context);
nativeContext = nativeContextPtr;
}
public final void onResume()
public Surface getNativeSurface()
{
for (int i = getChildCount(); --i >= 0;)
{
View v = getChildAt (i);
Surface retval = null;
if (v instanceof OpenGLView)
((OpenGLView) v).onResume();
}
SurfaceHolder holder = getHolder();
if (holder != null)
retval = holder.getSurface();
return retval;
}
public OpenGLView createGLView()
//==============================================================================
@Override
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
{
OpenGLView glView = new OpenGLView (getContext());
addView (glView);
return glView;
surfaceChangedNative (nativeContext, holder, format, width, height);
}
}
//==============================================================================
public final class OpenGLView extends GLSurfaceView
implements GLSurfaceView.Renderer
{
OpenGLView (Context context)
@Override
public void surfaceCreated (SurfaceHolder holder)
{
super (context);
setEGLContextClientVersion (2);
setRenderer (this);
setRenderMode (RENDERMODE_WHEN_DIRTY);
surfaceCreatedNative (nativeContext, holder);
}
@Override
public void surfaceDestroyed (SurfaceHolder holder)
{
surfaceDestroyedNative (nativeContext, holder);
}
@Override
public void onSurfaceCreated (GL10 unused, EGLConfig config)
protected void dispatchDraw (Canvas canvas)
{
contextCreated();
super.dispatchDraw (canvas);
dispatchDrawNative (nativeContext, canvas);
}
//==============================================================================
@Override
public void onSurfaceChanged (GL10 unused, int width, int height)
protected void onAttachedToWindow ()
{
contextChangedSize();
super.onAttachedToWindow();
getHolder().addCallback (this);
}
@Override
public void onDrawFrame (GL10 unused)
protected void onDetachedFromWindow ()
{
render();
super.onDetachedFromWindow();
getHolder().removeCallback (this);
}
private native void contextCreated();
private native void contextChangedSize();
private native void render();
//==============================================================================
private native void dispatchDrawNative (long nativeContextPtr, Canvas canvas);
private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
int format, int width, int height);
}
public NativeSurfaceView createNativeSurfaceView(long nativeSurfacePtr)
{
return new NativeSurfaceView (this, nativeSurfacePtr);
}
//==============================================================================
@@ -944,4 +1028,70 @@ public class JuceAppActivity extends Activity
return null;
}
public final int getAndroidSDKVersion()
{
return android.os.Build.VERSION.SDK_INT;
}
public final String audioManagerGetProperty (String property)
{
Object obj = getSystemService (AUDIO_SERVICE);
if (obj == null)
return null;
java.lang.reflect.Method method;
try {
method = obj.getClass().getMethod ("getProperty", String.class);
} catch (SecurityException e) {
return null;
} catch (NoSuchMethodException e) {
return null;
}
if (method == null)
return null;
try {
return (String) method.invoke (obj, property);
} catch (java.lang.IllegalArgumentException e) {
} catch (java.lang.IllegalAccessException e) {
} catch (java.lang.reflect.InvocationTargetException e) {
}
return null;
}
public final int setCurrentThreadPriority (int priority)
{
android.os.Process.setThreadPriority (android.os.Process.myTid(), priority);
return android.os.Process.getThreadPriority (android.os.Process.myTid());
}
public final boolean hasSystemFeature (String property)
{
return getPackageManager().hasSystemFeature (property);
}
private static class JuceThread extends Thread
{
public JuceThread (long host)
{
_this = host;
}
public void run()
{
runThread(_this);
}
private native void runThread (long host);
private long _this;
}
public final Thread createNewThread(long host)
{
return new JuceThread(host);
}
}

+ 28
- 133
libs/juce/source/modules/juce_core/native/juce_android_JNIHelpers.h View File

@@ -36,6 +36,10 @@
//==============================================================================
extern JNIEnv* getEnv() noexcept;
// You should rarely need to use this function. Only if you expect callbacks
// on a java thread which you did not create yourself.
extern void setEnv (JNIEnv* env) noexcept;
//==============================================================================
class GlobalRef
{
@@ -236,6 +240,8 @@ private:
#define JUCE_JNI_CALLBACK(className, methodName, returnType, params) \
extern "C" __attribute__ ((visibility("default"))) JUCE_ARM_SOFT_FLOAT_ABI returnType JUCE_JOIN_MACRO (JUCE_JOIN_MACRO (Java_, className), _ ## methodName) params
//==============================================================================
class AndroidSystem
{
@@ -253,142 +259,11 @@ public:
extern AndroidSystem android;
//==============================================================================
class ThreadLocalJNIEnvHolder
{
public:
ThreadLocalJNIEnvHolder() noexcept
: jvm (nullptr)
{
zeromem (threads, sizeof (threads));
zeromem (envs, sizeof (envs));
}
void initialise (JNIEnv* env)
{
// NB: the DLL can be left loaded by the JVM, so the same static
// objects can end up being reused by subsequent runs of the app
zeromem (threads, sizeof (threads));
zeromem (envs, sizeof (envs));
env->GetJavaVM (&jvm);
addEnv (env);
}
JNIEnv* attach() noexcept
{
if (android.activity != nullptr)
{
if (JNIEnv* env = attachToCurrentThread())
{
SpinLock::ScopedLockType sl (addRemoveLock);
return addEnv (env);
}
jassertfalse;
}
return nullptr;
}
void detach() noexcept
{
if (android.activity != nullptr)
{
jvm->DetachCurrentThread();
removeCurrentThreadFromCache();
}
}
void removeCurrentThreadFromCache()
{
const pthread_t thisThread = pthread_self();
SpinLock::ScopedLockType sl (addRemoveLock);
for (int i = 0; i < maxThreads; ++i)
{
if (threads[i] == thisThread)
{
threads[i] = 0;
envs[i] = nullptr;
}
}
}
JNIEnv* getOrAttach() noexcept
{
if (JNIEnv* env = get())
return env;
SpinLock::ScopedLockType sl (addRemoveLock);
if (JNIEnv* env = get())
return env;
if (JNIEnv* env = attachToCurrentThread())
return addEnv (env);
return nullptr;
}
private:
JavaVM* jvm;
enum { maxThreads = 32 };
pthread_t threads [maxThreads];
JNIEnv* envs [maxThreads];
SpinLock addRemoveLock;
JNIEnv* addEnv (JNIEnv* env) noexcept
{
const pthread_t thisThread = pthread_self();
for (int i = 0; i < maxThreads; ++i)
{
if (threads[i] == 0)
{
envs[i] = env;
threads[i] = thisThread;
return env;
}
}
jassertfalse; // too many threads!
return nullptr;
}
JNIEnv* get() const noexcept
{
const pthread_t thisThread = pthread_self();
for (int i = 0; i < maxThreads; ++i)
if (threads[i] == thisThread)
return envs[i];
return nullptr;
}
JNIEnv* attachToCurrentThread()
{
JNIEnv* env = nullptr;
jvm->AttachCurrentThread (&env, nullptr);
return env;
}
};
extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder;
struct AndroidThreadScope
{
AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); }
~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); }
};
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \
METHOD (deleteOpenGLView, "deleteOpenGLView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView;)V") \
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \
METHOD (postMessage, "postMessage", "(J)V") \
METHOD (finish, "finish", "()V") \
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
@@ -405,7 +280,14 @@ struct AndroidThreadScope
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
METHOD (getTypeFaceFromByteArray,"getTypeFaceFromByteArray","([B)Landroid/graphics/Typeface;") \
METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \
METHOD (getScreenSaver, "getScreenSaver", "()Z")
METHOD (getScreenSaver, "getScreenSaver", "()Z") \
METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \
METHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
METHOD (setCurrentThreadPriority, "setCurrentThreadPriority", "(I)I") \
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
METHOD (createNewThread, "createNewThread", "(J)Ljava/lang/Thread;") \
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH);
#undef JNI_CLASS_MEMBERS
@@ -435,6 +317,19 @@ DECLARE_JNI_CLASS (Paint, "android/graphics/Paint");
DECLARE_JNI_CLASS (Matrix, "android/graphics/Matrix");
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (start, "start", "()V") \
METHOD (stop, "stop", "()V") \
METHOD (setName, "setName", "(Ljava/lang/String;)V") \
METHOD (getName, "getName", "()Ljava/lang/String;") \
METHOD (getId, "getId", "()J") \
STATICMETHOD (currentThread, "currentThread", "()Ljava/lang/Thread;") \
METHOD (setPriority, "setPriority", "(I)V") \
DECLARE_JNI_CLASS (JuceThread, "java/lang/Thread");
#undef JNI_CLASS_MEMBERS
//==============================================================================
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "(IIII)V") \


+ 11
- 25
libs/juce/source/modules/juce_core/native/juce_android_SystemStats.cpp View File

@@ -98,24 +98,19 @@ jfieldID JNIClassBase::resolveStaticField (JNIEnv* env, const char* fieldName, c
}
//==============================================================================
ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder;
#if JUCE_DEBUG
static bool systemInitialised = false;
#endif
ThreadLocalValue<JNIEnv*> androidJNIEnv;
JNIEnv* getEnv() noexcept
{
#if JUCE_DEBUG
if (! systemInitialised)
{
DBG ("*** Call to getEnv() when system not initialised");
jassertfalse;
std::exit (EXIT_FAILURE);
}
#endif
JNIEnv* env = androidJNIEnv.get();
jassert (env != nullptr);
return threadLocalJNIEnvHolder.getOrAttach();
return env;
}
void setEnv (JNIEnv* env) noexcept
{
androidJNIEnv.get() = env;
}
extern "C" jint JNI_OnLoad (JavaVM*, void*)
@@ -134,11 +129,6 @@ void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring
dpi = 160;
JNIClassBase::initialiseAllClasses (env);
threadLocalJNIEnvHolder.initialise (env);
#if JUCE_DEBUG
systemInitialised = true;
#endif
activity = GlobalRef (act);
appFile = juceString (env, file);
appDataDir = juceString (env, dataDir);
@@ -148,10 +138,6 @@ void AndroidSystem::shutdown (JNIEnv* env)
{
activity.clear();
#if JUCE_DEBUG
systemInitialised = false;
#endif
JNIClassBase::releaseAllClasses (env);
}
@@ -253,7 +239,7 @@ String SystemStats::getLogonName()
if (struct passwd* const pw = getpwuid (getuid()))
return CharPointer_UTF8 (pw->pw_name);
return String::empty;
return String();
}
String SystemStats::getFullUserName()
@@ -267,7 +253,7 @@ String SystemStats::getComputerName()
if (gethostname (name, sizeof (name) - 1) == 0)
return name;
return String::empty;
return String();
}


+ 232
- 0
libs/juce/source/modules/juce_core/native/juce_android_Threads.cpp View File

@@ -74,3 +74,235 @@ JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger()
JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {}
JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {}
struct AndroidThreadData
{
AndroidThreadData (Thread* thread) noexcept
: owner (thread), tId (0)
{
}
Thread* owner;
Thread::ThreadID tId;
WaitableEvent eventSet, eventGet;
};
void JUCE_API juce_threadEntryPoint (void*);
extern "C" void* threadEntryProc (void*);
extern "C" void* threadEntryProc (void* userData)
{
ScopedPointer<AndroidThreadData> priv (reinterpret_cast<AndroidThreadData*> (userData));
priv->tId = (Thread::ThreadID) pthread_self();
priv->eventSet.signal();
priv->eventGet.wait (-1);
juce_threadEntryPoint (priv->owner);
return nullptr;
}
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceThread), runThread,
void, (JNIEnv* env, jobject device, jlong host))
{
// Java may create a Midi thread which JUCE doesn't know about and this callback may be
// received on this thread. Java will have already created a JNI Env for this new thread,
// which we need to tell Juce about
setEnv (env);
if (Thread* thread = reinterpret_cast<Thread*> (host))
threadEntryProc (thread);
}
void Thread::launchThread()
{
threadHandle = 0;
ScopedPointer<AndroidThreadData> threadPrivateData = new AndroidThreadData (this);
jobject juceNewThread = android.activity.callObjectMethod (JuceAppActivity.createNewThread, (jlong) threadPrivateData.get());
if (jobject juceThread = getEnv()->NewGlobalRef (juceNewThread))
{
AndroidThreadData* priv = threadPrivateData.release();
threadHandle = (void*) juceThread;
getEnv()->CallVoidMethod (juceThread, JuceThread.start);
priv->eventSet.wait (-1);
threadId = priv->tId;
priv->eventGet.signal();
}
}
void Thread::closeThreadHandle()
{
if (threadHandle != 0)
{
jobject juceThread = reinterpret_cast<jobject> (threadHandle);
getEnv()->DeleteGlobalRef (juceThread);
threadHandle = 0;
}
threadId = 0;
}
void Thread::killThread()
{
if (threadHandle != 0)
{
jobject juceThread = reinterpret_cast<jobject> (threadHandle);
getEnv()->CallVoidMethod (juceThread, JuceThread.stop);
}
}
void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name)
{
LocalRef<jobject> juceThread (getEnv()->CallStaticObjectMethod (JuceThread, JuceThread.currentThread));
if (jobject t = juceThread.get())
getEnv()->CallVoidMethod (t, JuceThread.setName, javaString (name).get());
}
bool Thread::setThreadPriority (void* handle, int priority)
{
if (handle == nullptr)
{
LocalRef<jobject> juceThread (getEnv()->CallStaticObjectMethod (JuceThread, JuceThread.currentThread));
if (jobject t = juceThread.get())
return setThreadPriority (t, priority);
return false;
}
jobject juceThread = reinterpret_cast<jobject> (handle);
const int minPriority = 1;
const int maxPriority = 10;
jint javaPriority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
getEnv()->CallVoidMethod (juceThread, JuceThread.setPriority, javaPriority);
return true;
}
//==============================================================================
struct HighResolutionTimer::Pimpl
{
struct HighResolutionThread : public Thread
{
HighResolutionThread (HighResolutionTimer::Pimpl& parent)
: Thread ("High Resolution Timer"), pimpl (parent)
{
startThread();
}
void run() override
{
pimpl.timerThread();
}
private:
HighResolutionTimer::Pimpl& pimpl;
};
//==============================================================================
Pimpl (HighResolutionTimer& t) : owner (t) {}
~Pimpl()
{
stop();
}
void start (int newPeriod)
{
if (periodMs != newPeriod)
{
if (thread.get() == nullptr
|| thread->getThreadId() != Thread::getCurrentThreadId()
|| thread->threadShouldExit())
{
stop();
periodMs = newPeriod;
thread = new HighResolutionThread (*this);
}
else
{
periodMs = newPeriod;
}
}
}
void stop()
{
if (thread.get() != nullptr)
{
thread->signalThreadShouldExit();
if (thread->getThreadId() != Thread::getCurrentThreadId())
{
thread->waitForThreadToExit (-1);
thread = nullptr;
}
}
}
HighResolutionTimer& owner;
int volatile periodMs;
private:
ScopedPointer<Thread> thread;
void timerThread()
{
jassert (thread.get() != nullptr);
int lastPeriod = periodMs;
Clock clock (lastPeriod);
while (! thread->threadShouldExit())
{
clock.wait();
owner.hiResTimerCallback();
if (lastPeriod != periodMs)
{
lastPeriod = periodMs;
clock = Clock (lastPeriod);
}
}
periodMs = 0;
}
struct Clock
{
Clock (double millis) noexcept : delta ((uint64) (millis * 1000000))
{
}
void wait() noexcept
{
struct timespec t;
t.tv_sec = (time_t) (delta / 1000000000);
t.tv_nsec = (long) (delta % 1000000000);
nanosleep (&t, nullptr);
}
uint64 delta;
};
static bool setThreadToRealtime (pthread_t thread, uint64 periodMs)
{
ignoreUnused (periodMs);
struct sched_param param;
param.sched_priority = sched_get_priority_max (SCHED_RR);
return pthread_setschedparam (thread, SCHED_RR, &param) == 0;
}
JUCE_DECLARE_NON_COPYABLE (Pimpl)
};

+ 1
- 1
libs/juce/source/modules/juce_core/native/juce_linux_CommonFile.cpp View File

@@ -65,7 +65,7 @@ static String getLinkedFile (const String& file)
return String::fromUTF8 (buffer, jmax (0, numBytes));
};
bool File::isLink() const
bool File::isSymbolicLink() const
{
return getLinkedFile (getFullPathName()).isNotEmpty();
}


+ 1
- 1
libs/juce/source/modules/juce_core/native/juce_linux_Files.cpp View File

@@ -158,7 +158,7 @@ File File::getSpecialLocation (const SpecialLocationType type)
case hostApplicationPath:
{
const File f ("/proc/self/exe");
return f.isLink() ? f.getLinkedTarget() : juce_getExecutableFile();
return f.isSymbolicLink() ? f.getLinkedTarget() : juce_getExecutableFile();
}
default:


+ 7
- 2
libs/juce/source/modules/juce_core/native/juce_mac_Files.mm View File

@@ -284,7 +284,7 @@ static NSString* getFileLink (const String& path)
#endif
}
bool File::isLink() const
bool File::isSymbolicLink() const
{
return getFileLink (fullPath) != nil;
}
@@ -400,7 +400,12 @@ bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String&
{
JUCE_AUTORELEASEPOOL
{
NSURL* filenameAsURL = [NSURL URLWithString: juceStringToNS (fileName)];
NSString* fileNameAsNS (juceStringToNS (fileName));
NSURL* filenameAsURL ([NSURL URLWithString: fileNameAsNS]);
if (filenameAsURL == nil)
filenameAsURL = [NSURL fileURLWithPath: fileNameAsNS];
#if JUCE_IOS
(void) parameters;


+ 5
- 18
libs/juce/source/modules/juce_core/native/juce_posix_SharedCode.h View File

@@ -867,6 +867,7 @@ void InterProcessLock::exit()
}
//==============================================================================
#if ! JUCE_ANDROID
void JUCE_API juce_threadEntryPoint (void*);
extern "C" void* threadEntryProc (void*);
@@ -874,10 +875,6 @@ extern "C" void* threadEntryProc (void* userData)
{
JUCE_AUTORELEASEPOOL
{
#if JUCE_ANDROID
const AndroidThreadScope androidEnv;
#endif
juce_threadEntryPoint (userData);
}
@@ -951,6 +948,7 @@ bool Thread::setThreadPriority (void* handle, int priority)
param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
return pthread_setschedparam ((pthread_t) handle, policy, &param) == 0;
}
#endif
Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId()
{
@@ -1180,6 +1178,7 @@ bool ChildProcess::start (const StringArray& args, int streamFlags)
}
//==============================================================================
#if ! JUCE_ANDROID
struct HighResolutionTimer::Pimpl
{
Pimpl (HighResolutionTimer& t) : owner (t), thread (0), shouldStop (false)
@@ -1286,20 +1285,6 @@ private:
uint64_t time, delta;
#elif JUCE_ANDROID
Clock (double millis) noexcept : delta ((uint64) (millis * 1000000))
{
}
void wait() noexcept
{
struct timespec t;
t.tv_sec = (time_t) (delta / 1000000000);
t.tv_nsec = (long) (delta % 1000000000);
nanosleep (&t, nullptr);
}
uint64 delta;
#else
Clock (double millis) noexcept : delta ((uint64) (millis * 1000000))
{
@@ -1348,3 +1333,5 @@ private:
JUCE_DECLARE_NON_COPYABLE (Pimpl)
};
#endif

+ 34
- 2
libs/juce/source/modules/juce_core/native/juce_win32_Files.cpp View File

@@ -631,13 +631,45 @@ String File::getVersion() const
}
//==============================================================================
bool File::isLink() const
bool File::isSymbolicLink() const
{
return (GetFileAttributes (fullPath.toWideCharPointer()) & FILE_ATTRIBUTE_REPARSE_POINT) != 0;
}
bool File::isShortcut() const
{
return hasFileExtension (".lnk");
}
File File::getLinkedTarget() const
{
{
HANDLE h = CreateFile (getFullPathName().toWideCharPointer(),
GENERIC_READ, FILE_SHARE_READ, nullptr,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
if (h != INVALID_HANDLE_VALUE)
{
DWORD requiredSize = ::GetFinalPathNameByHandleW (h, nullptr, 0, FILE_NAME_NORMALIZED);
if (requiredSize > 0)
{
HeapBlock<WCHAR> buffer (requiredSize + 2);
buffer.clear (requiredSize + 2);
requiredSize = ::GetFinalPathNameByHandleW (h, buffer, requiredSize, FILE_NAME_NORMALIZED);
if (requiredSize > 0)
{
CloseHandle (h);
return File (String (buffer));
}
}
CloseHandle (h);
}
}
File result (*this);
String p (getFullPathName());
@@ -664,7 +696,7 @@ File File::getLinkedTarget() const
return result;
}
bool File::createLink (const String& description, const File& linkFileToCreate) const
bool File::createShortcut (const String& description, const File& linkFileToCreate) const
{
linkFileToCreate.deleteFile();


+ 6
- 0
libs/juce/source/modules/juce_core/system/juce_CompilerSupport.h View File

@@ -42,6 +42,7 @@
#define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1
#define JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS 1
#define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1
#define JUCE_COMPILER_SUPPORTS_STATIC_ASSERT 1
#if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL)
#define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1
@@ -93,6 +94,10 @@
#define JUCE_COMPILER_SUPPORTS_VARIADIC_TEMPLATES 1
#endif
#if __has_feature (cxx_static_assert)
#define JUCE_COMPILER_SUPPORTS_STATIC_ASSERT 1
#endif
#ifndef JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL
#define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1
#endif
@@ -115,6 +120,7 @@
#if _MSC_VER >= 1600
#define JUCE_COMPILER_SUPPORTS_NULLPTR 1
#define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1
#define JUCE_COMPILER_SUPPORTS_STATIC_ASSERT 1
#endif
#if _MSC_VER >= 1700


+ 35
- 29
libs/juce/source/modules/juce_core/system/juce_PlatformDefs.h View File

@@ -149,20 +149,44 @@
#endif
//==============================================================================
#ifndef DOXYGEN
namespace juce
{
template <bool b> struct JuceStaticAssert;
template <> struct JuceStaticAssert<true> { static void dummy() {} };
}
#if ! DOXYGEN
#define JUCE_JOIN_MACRO_HELPER(a, b) a ## b
#define JUCE_STRINGIFY_MACRO_HELPER(a) #a
#endif
/** A compile-time assertion macro.
If the expression parameter is false, the macro will cause a compile error. (The actual error
message that the compiler generates may be completely bizarre and seem to have no relation to
the place where you put the static_assert though!)
/** A good old-fashioned C macro concatenation helper.
This combines two items (which may themselves be macros) into a single string,
avoiding the pitfalls of the ## macro operator.
*/
#define static_jassert(expression) juce::JuceStaticAssert<expression>::dummy();
#define JUCE_JOIN_MACRO(item1, item2) JUCE_JOIN_MACRO_HELPER (item1, item2)
/** A handy C macro for stringifying any symbol, rather than just a macro parameter. */
#define JUCE_STRINGIFY(item) JUCE_STRINGIFY_MACRO_HELPER (item)
//==============================================================================
#ifdef JUCE_COMPILER_SUPPORTS_STATIC_ASSERT
/** A compile-time assertion macro.
If the expression parameter is false, the macro will cause a compile error. (The actual error
message that the compiler generates may be completely bizarre and seem to have no relation to
the place where you put the static_assert though!)
*/
#define static_jassert(expression) static_assert (expression);
#else
#ifndef DOXYGEN
namespace juce
{
template <bool b> struct JuceStaticAssert;
template <> struct JuceStaticAssert<true> { static void dummy() {} };
}
#endif
/** A compile-time assertion macro.
If the expression parameter is false, the macro will cause a compile error. (The actual error
message that the compiler generates may be completely bizarre and seem to have no relation to
the place where you put the static_assert though!)
*/
#define static_jassert(expression) juce::JuceStaticAssert<expression>::dummy();
#endif
/** This is a shorthand macro for declaring stubs for a class's copy constructor and operator=.
@@ -207,24 +231,6 @@ namespace juce
static void* operator new (size_t) JUCE_DELETED_FUNCTION; \
static void operator delete (void*) JUCE_DELETED_FUNCTION;
//==============================================================================
#if ! DOXYGEN
#define JUCE_JOIN_MACRO_HELPER(a, b) a ## b
#define JUCE_STRINGIFY_MACRO_HELPER(a) #a
#endif
/** A good old-fashioned C macro concatenation helper.
This combines two items (which may themselves be macros) into a single string,
avoiding the pitfalls of the ## macro operator.
*/
#define JUCE_JOIN_MACRO(item1, item2) JUCE_JOIN_MACRO_HELPER (item1, item2)
/** A handy C macro for stringifying any symbol, rather than just a macro parameter.
*/
#define JUCE_STRINGIFY(item) JUCE_STRINGIFY_MACRO_HELPER (item)
//==============================================================================
#if JUCE_MSVC && ! defined (DOXYGEN)
#define JUCE_WARNING_HELPER(file, line, mess) message(file "(" JUCE_STRINGIFY (line) ") : Warning: " #mess)


+ 3
- 3
libs/juce/source/modules/juce_core/system/juce_StandardHeader.h View File

@@ -34,9 +34,9 @@
See also SystemStats::getJUCEVersion() for a string version.
*/
#define JUCE_MAJOR_VERSION 3
#define JUCE_MINOR_VERSION 2
#define JUCE_BUILDNUMBER 0
#define JUCE_MAJOR_VERSION 4
#define JUCE_MINOR_VERSION 0
#define JUCE_BUILDNUMBER 1
/** Current Juce version number.


+ 10
- 0
libs/juce/source/modules/juce_core/text/juce_CharacterFunctions.cpp View File

@@ -108,6 +108,16 @@ bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) noexcept
return iswalnum ((wint_t) character) != 0;
}
bool CharacterFunctions::isPrintable (const char character) noexcept
{
return (character >= ' ' && character <= '~');
}
bool CharacterFunctions::isPrintable (const juce_wchar character) noexcept
{
return iswprint ((wint_t) character) != 0;
}
int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept
{
unsigned int d = (unsigned int) digit - '0';


+ 10
- 0
libs/juce/source/modules/juce_core/text/juce_CharacterFunctions.h View File

@@ -110,6 +110,16 @@ public:
/** Checks whether a character is alphabetic or numeric. */
static bool isLetterOrDigit (juce_wchar character) noexcept;
/** Checks whether a character is a printable character, i.e. alphabetic, numeric,
a punctuation character or a space.
*/
static bool isPrintable (char character) noexcept;
/** Checks whether a character is a printable character, i.e. alphabetic, numeric,
a punctuation character or a space.
*/
static bool isPrintable (juce_wchar character) noexcept;
/** Returns 0 to 16 for '0' to 'F", or -1 for characters that aren't a legal hex digit. */
static int getHexDigitValue (juce_wchar digit) noexcept;


+ 152
- 27
libs/juce/source/modules/juce_core/text/juce_String.cpp View File

@@ -430,7 +430,22 @@ namespace NumberToStringConverters
return t;
}
static char* numberToString (char* t, unsigned int v) noexcept
static char* numberToString (char* t, const unsigned int v) noexcept
{
return printDigits (t, v);
}
static char* numberToString (char* t, const long n) noexcept
{
if (n >= 0)
return printDigits (t, static_cast<unsigned long> (n));
t = printDigits (t, static_cast<unsigned long> (-(n + 1)) + 1);
*--t = '-';
return t;
}
static char* numberToString (char* t, const unsigned long v) noexcept
{
return printDigits (t, v);
}
@@ -517,6 +532,8 @@ String::String (const short number) : text (NumberToStringConverters::c
String::String (const unsigned short number) : text (NumberToStringConverters::createFromInteger ((unsigned int) number)) {}
String::String (const int64 number) : text (NumberToStringConverters::createFromInteger (number)) {}
String::String (const uint64 number) : text (NumberToStringConverters::createFromInteger (number)) {}
String::String (const long number) : text (NumberToStringConverters::createFromInteger (number)) {}
String::String (const unsigned long number) : text (NumberToStringConverters::createFromInteger (number)) {}
String::String (const float number) : text (NumberToStringConverters::createFromDouble ((double) number, 0)) {}
String::String (const double number) : text (NumberToStringConverters::createFromDouble (number, 0)) {}
@@ -795,34 +812,29 @@ String& String::operator+= (const juce_wchar ch)
}
#endif
String& String::operator+= (const int number)
namespace StringHelpers
{
char buffer [16];
char* end = buffer + numElementsInArray (buffer);
char* start = NumberToStringConverters::numberToString (end, number);
#if (JUCE_STRING_UTF_TYPE == 8)
appendCharPointer (CharPointerType (start), CharPointerType (end));
#else
appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end));
#endif
return *this;
}
template <typename T>
inline String& operationAddAssign (String& str, const T number)
{
char buffer [(sizeof(T) * 8) / 2];
char* end = buffer + numElementsInArray (buffer);
char* start = NumberToStringConverters::numberToString (end, number);
String& String::operator+= (int64 number)
{
char buffer [32];
char* end = buffer + numElementsInArray (buffer);
char* start = NumberToStringConverters::numberToString (end, number);
#if (JUCE_STRING_UTF_TYPE == 8)
str.appendCharPointer (String::CharPointerType (start), String::CharPointerType (end));
#else
str.appendCharPointer (String::CharPointer_ASCII (start), String::CharPointer_ASCII (end));
#endif
#if (JUCE_STRING_UTF_TYPE == 8)
appendCharPointer (CharPointerType (start), CharPointerType (end));
#else
appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end));
#endif
return *this;
return str;
}
}
String& String::operator+= (const int number) { return StringHelpers::operationAddAssign<int> (*this, number); }
String& String::operator+= (const int64 number) { return StringHelpers::operationAddAssign<int64> (*this, number); }
String& String::operator+= (const uint64 number) { return StringHelpers::operationAddAssign<uint64> (*this, number); }
//==============================================================================
JUCE_API String JUCE_CALLTYPE operator+ (const char* const s1, const String& s2) { String s (s1); return s += s2; }
JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t* const s1, const String& s2) { String s (s1); return s += s2; }
@@ -853,11 +865,13 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, StringRef s2)
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int number) { return s1 += number; }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const short number) { return s1 += (int) number; }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const long number) { return s1 += (int) number; }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const unsigned short number) { return s1 += (uint64) number; }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const long number) { return s1 += String (number); }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const unsigned long number) { return s1 += String (number); }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int64 number) { return s1 += String (number); }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const uint64 number) { return s1 += String (number); }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const float number) { return s1 += String (number); }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const double number) { return s1 += String (number); }
JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const uint64 number) { return s1 += String (number); }
JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text)
{
@@ -2174,7 +2188,8 @@ StringRef::StringRef (const String& string) noexcept : text (string.getCharPoin
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
#define STRINGIFY2(X) #X
#define STRINGIFY(X) STRINGIFY2(X)
class StringTests : public UnitTest
{
public:
@@ -2325,6 +2340,116 @@ public:
s2 << StringRef ("def");
expect (s2 == "1234567890xyz123123def");
// int16
{
String numStr (std::numeric_limits<int16>::max());
expect (numStr == "32767");
}
{
String numStr (std::numeric_limits<int16>::min());
expect (numStr == "-32768");
}
{
String numStr;
numStr << std::numeric_limits<int16>::max();
expect (numStr == "32767");
}
{
String numStr;
numStr << std::numeric_limits<int16>::min();
expect (numStr == "-32768");
}
// uint16
{
String numStr (std::numeric_limits<uint16>::max());
expect (numStr == "65535");
}
{
String numStr (std::numeric_limits<uint16>::min());
expect (numStr == "0");
}
{
String numStr;
numStr << std::numeric_limits<uint16>::max();
expect (numStr == "65535");
}
{
String numStr;
numStr << std::numeric_limits<uint16>::min();
expect (numStr == "0");
}
// int32
{
String numStr (std::numeric_limits<int32>::max());
expect (numStr == "2147483647");
}
{
String numStr (std::numeric_limits<int32>::min());
expect (numStr == "-2147483648");
}
{
String numStr;
numStr << std::numeric_limits<int32>::max();
expect (numStr == "2147483647");
}
{
String numStr;
numStr << std::numeric_limits<int32>::min();
expect (numStr == "-2147483648");
}
// uint32
{
String numStr (std::numeric_limits<uint32>::max());
expect (numStr == "4294967295");
}
{
String numStr (std::numeric_limits<uint32>::min());
expect (numStr == "0");
}
// int64
{
String numStr (std::numeric_limits<int64>::max());
expect (numStr == "9223372036854775807");
}
{
String numStr (std::numeric_limits<int64>::min());
expect (numStr == "-9223372036854775808");
}
{
String numStr;
numStr << std::numeric_limits<int64>::max();
expect (numStr == "9223372036854775807");
}
{
String numStr;
numStr << std::numeric_limits<int64>::min();
expect (numStr == "-9223372036854775808");
}
// uint64
{
String numStr (std::numeric_limits<uint64>::max());
expect (numStr == "18446744073709551615");
}
{
String numStr (std::numeric_limits<uint64>::min());
expect (numStr == "0");
}
{
String numStr;
numStr << std::numeric_limits<uint64>::max();
expect (numStr == "18446744073709551615");
}
{
String numStr;
numStr << std::numeric_limits<uint64>::min();
expect (numStr == "0");
}
// size_t
{
String numStr (std::numeric_limits<size_t>::min());
expect (numStr == "0");
}
beginTest ("Numeric conversions");
expect (String::empty.getIntValue() == 0);
expect (String::empty.getDoubleValue() == 0.0);


+ 14
- 0
libs/juce/source/modules/juce_core/text/juce_String.h View File

@@ -210,7 +210,11 @@ public:
/** Appends a decimal number at the end of this string. */
String& operator+= (int numberToAppend);
/** Appends a decimal number at the end of this string. */
String& operator+= (long numberToAppend);
/** Appends a decimal number at the end of this string. */
String& operator+= (int64 numberToAppend);
/** Appends a decimal number at the end of this string. */
String& operator+= (uint64 numberToAppend);
/** Appends a character at the end of this string. */
String& operator+= (char characterToAppend);
/** Appends a character at the end of this string. */
@@ -937,6 +941,16 @@ public:
*/
explicit String (uint64 largeIntegerValue);
/** Creates a string containing this signed long integer as a decimal number.
@see getIntValue, getFloatValue, getDoubleValue, toHexString
*/
explicit String (long decimalInteger);
/** Creates a string containing this unsigned long integer as a decimal number.
@see getIntValue, getFloatValue, getDoubleValue, toHexString
*/
explicit String (unsigned long decimalInteger);
/** Creates a string representing this floating-point number.
@param floatValue the value to convert to a string
@see getDoubleValue, getIntValue


+ 6
- 0
libs/juce/source/modules/juce_core/text/juce_StringArray.cpp View File

@@ -173,6 +173,12 @@ void StringArray::addArray (const StringArray& otherArray, int startIndex, int n
strings.add (otherArray.strings.getReference (startIndex++));
}
void StringArray::mergeArray (const StringArray& otherArray, const bool ignoreCase)
{
for (int i = 0; i < otherArray.size(); ++i)
addIfNotAlreadyThere (otherArray[i], ignoreCase);
}
void StringArray::set (const int index, const String& newString)
{
strings.set (index, newString);


+ 9
- 0
libs/juce/source/modules/juce_core/text/juce_StringArray.h View File

@@ -209,6 +209,15 @@ public:
int startIndex = 0,
int numElementsToAdd = -1);
/** Merges the strings from another array into this one.
This will not add a string that already exists.
@param other the array to add
@param ignoreCase ignore case when merging
*/
void mergeArray (const StringArray& other,
bool ignoreCase = false);
/** Breaks up a string into tokens and adds them to this array.
This will tokenise the given string using whitespace characters as the


+ 41
- 0
libs/juce/source/modules/juce_core/unit_tests/juce_UnitTest.h View File

@@ -157,6 +157,47 @@ public:
expect (result, failureMessage);
}
//==============================================================================
/** Checks that the result of an expression does not throw an exception. */
#define expectDoesNotThrow(expr) \
try \
{ \
(expr); \
expect (true); \
} \
catch (...) \
{ \
expect (false, "Expected: does not throw an exception, Actual: throws."); \
}
/** Checks that the result of an expression throws an exception. */
#define expectThrows(expr) \
try \
{ \
(expr); \
expect (false, "Expected: throws an exception, Actual: does not throw."); \
} \
catch (...) \
{ \
expect (true); \
}
/** Checks that the result of an expression throws an exception of a certain type. */
#define expectThrowsType(expr, type) \
try \
{ \
(expr); \
expect (false, "Expected: throws an exception of type " #type ", Actual: does not throw."); \
} \
catch (type&) \
{ \
expect (true); \
} \
catch (...) \
{ \
expect (false, "Expected: throws an exception of type " #type ", Actual: throws another type."); \
}
//==============================================================================
/** Writes a message to the test log.
This can only be called from within your runTest() method.


+ 1
- 1
libs/juce/source/modules/juce_cryptography/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_cryptography",
"name": "JUCE cryptography classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for various basic cryptography functions, including RSA, Blowfish, MD5, SHA, etc.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 1
- 1
libs/juce/source/modules/juce_data_structures/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_data_structures",
"name": "JUCE data model helper classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for undo/redo management, and smart data structures.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 1
- 1
libs/juce/source/modules/juce_events/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_events",
"name": "JUCE message and event handling classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for running an application's main event loop and sending/receiving messages, timers, etc.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 2
- 0
libs/juce/source/modules/juce_events/native/juce_android_Messaging.cpp View File

@@ -44,6 +44,8 @@ bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* cons
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, deliverMessage, void, (JNIEnv* env, jobject activity, jlong value))
{
setEnv (env);
JUCE_TRY
{
MessageManager::MessageBase* const message = (MessageManager::MessageBase*) (pointer_sized_uint) value;


+ 1
- 1
libs/juce/source/modules/juce_graphics/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_graphics",
"name": "JUCE graphics classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Classes for 2D vector graphics, image loading/saving, font handling, etc.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 12
- 12
libs/juce/source/modules/juce_gui_basics/components/juce_Component.cpp View File

@@ -2384,7 +2384,7 @@ void Component::internalMouseEnter (MouseInputSource source, Point<float> relati
BailOutChecker checker (this);
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
this, this, time, relativePos, time, 0, false);
mouseEnter (me);
@@ -2403,7 +2403,7 @@ void Component::internalMouseExit (MouseInputSource source, Point<float> relativ
BailOutChecker checker (this);
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
this, this, time, relativePos, time, 0, false);
mouseExit (me);
@@ -2416,7 +2416,7 @@ void Component::internalMouseExit (MouseInputSource source, Point<float> relativ
MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseExit, me);
}
void Component::internalMouseDown (MouseInputSource source, Point<float> relativePos, Time time)
void Component::internalMouseDown (MouseInputSource source, Point<float> relativePos, Time time, float pressure)
{
Desktop& desktop = Desktop::getInstance();
BailOutChecker checker (this);
@@ -2435,7 +2435,7 @@ void Component::internalMouseDown (MouseInputSource source, Point<float> relativ
{
// allow blocked mouse-events to go to global listeners..
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
this, this, time, relativePos, time,
pressure, this, this, time, relativePos, time,
source.getNumberOfMultipleClicks(), false);
desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseDown, me);
@@ -2468,7 +2468,7 @@ void Component::internalMouseDown (MouseInputSource source, Point<float> relativ
repaint();
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
this, this, time, relativePos, time,
pressure, this, this, time, relativePos, time,
source.getNumberOfMultipleClicks(), false);
mouseDown (me);
@@ -2492,7 +2492,7 @@ void Component::internalMouseUp (MouseInputSource source, Point<float> relativeP
repaint();
const MouseEvent me (source, relativePos,
oldModifiers, this, this, time,
oldModifiers, MouseInputSource::invalidPressure, this, this, time,
getLocalPoint (nullptr, source.getLastMouseDownPosition()),
source.getLastMouseDownTime(),
source.getNumberOfMultipleClicks(),
@@ -2523,14 +2523,14 @@ void Component::internalMouseUp (MouseInputSource source, Point<float> relativeP
}
}
void Component::internalMouseDrag (MouseInputSource source, Point<float> relativePos, Time time)
void Component::internalMouseDrag (MouseInputSource source, Point<float> relativePos, Time time, float pressure)
{
if (! isCurrentlyBlockedByAnotherModalComponent())
{
BailOutChecker checker (this);
const MouseEvent me (source, relativePos,
source.getCurrentModifiers(), this, this, time,
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
pressure, this, this, time,
getLocalPoint (nullptr, source.getLastMouseDownPosition()),
source.getLastMouseDownTime(),
source.getNumberOfMultipleClicks(),
@@ -2559,7 +2559,7 @@ void Component::internalMouseMove (MouseInputSource source, Point<float> relativ
{
BailOutChecker checker (this);
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
this, this, time, relativePos, time, 0, false);
mouseMove (me);
@@ -2578,7 +2578,7 @@ void Component::internalMouseWheel (MouseInputSource source, Point<float> relati
Desktop& desktop = Desktop::getInstance();
BailOutChecker checker (this);
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
this, this, time, relativePos, time, 0, false);
if (isCurrentlyBlockedByAnotherModalComponent())
@@ -2605,7 +2605,7 @@ void Component::internalMagnifyGesture (MouseInputSource source, Point<float> re
{
if (! isCurrentlyBlockedByAnotherModalComponent())
{
const MouseEvent me (source, relativePos, source.getCurrentModifiers(),
const MouseEvent me (source, relativePos, source.getCurrentModifiers(), MouseInputSource::invalidPressure,
this, this, time, relativePos, time, 0, false);
mouseMagnify (me, amount);


+ 2
- 2
libs/juce/source/modules/juce_gui_basics/components/juce_Component.h View File

@@ -2300,9 +2300,9 @@ private:
//==============================================================================
void internalMouseEnter (MouseInputSource, Point<float>, Time);
void internalMouseExit (MouseInputSource, Point<float>, Time);
void internalMouseDown (MouseInputSource, Point<float>, Time);
void internalMouseDown (MouseInputSource, Point<float>, Time, float);
void internalMouseUp (MouseInputSource, Point<float>, Time, const ModifierKeys oldModifiers);
void internalMouseDrag (MouseInputSource, Point<float>, Time);
void internalMouseDrag (MouseInputSource, Point<float>, Time, float);
void internalMouseMove (MouseInputSource, Point<float>, Time);
void internalMouseWheel (MouseInputSource, Point<float>, Time, const MouseWheelDetails&);
void internalMagnifyGesture (MouseInputSource, Point<float>, Time, float);


+ 2
- 2
libs/juce/source/modules/juce_gui_basics/components/juce_Desktop.cpp View File

@@ -92,7 +92,7 @@ LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept
if (currentLookAndFeel == nullptr)
{
if (defaultLookAndFeel == nullptr)
defaultLookAndFeel = new LookAndFeel_V2();
defaultLookAndFeel = new LookAndFeel_V3();
currentLookAndFeel = defaultLookAndFeel;
}
@@ -246,7 +246,7 @@ void Desktop::sendMouseMove()
const Time now (Time::getCurrentTime());
const MouseEvent me (getMainMouseSource(), pos, ModifierKeys::getCurrentModifiers(),
target, target, now, pos, now, 0, false);
MouseInputSource::invalidPressure, target, target, now, pos, now, 0, false);
if (me.mods.isAnyMouseButtonDown())
mouseListeners.callChecked (checker, &MouseListener::mouseDrag, me);


+ 1
- 1
libs/juce/source/modules/juce_gui_basics/juce_module_info View File

@@ -1,7 +1,7 @@
{
"id": "juce_gui_basics",
"name": "JUCE GUI core classes",
"version": "3.2.0",
"version": "4.0.1",
"description": "Basic user-interface components and related classes.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",


+ 9
- 5
libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseEvent.cpp View File

@@ -25,6 +25,7 @@
MouseEvent::MouseEvent (MouseInputSource inputSource,
Point<float> pos,
ModifierKeys modKeys,
float force,
Component* const eventComp,
Component* const originator,
Time time,
@@ -36,6 +37,7 @@ MouseEvent::MouseEvent (MouseInputSource inputSource,
x (roundToInt (pos.x)),
y (roundToInt (pos.y)),
mods (modKeys),
pressure (force),
eventComponent (eventComp),
originalComponent (originator),
eventTime (time),
@@ -57,22 +59,22 @@ MouseEvent MouseEvent::getEventRelativeTo (Component* const otherComponent) cons
jassert (otherComponent != nullptr);
return MouseEvent (source, otherComponent->getLocalPoint (eventComponent, position),
mods, otherComponent, originalComponent, eventTime,
mods, pressure, otherComponent, originalComponent, eventTime,
otherComponent->getLocalPoint (eventComponent, mouseDownPos),
mouseDownTime, numberOfClicks, wasMovedSinceMouseDown != 0);
}
MouseEvent MouseEvent::withNewPosition (Point<float> newPosition) const noexcept
{
return MouseEvent (source, newPosition, mods, eventComponent, originalComponent,
eventTime, mouseDownPos, mouseDownTime,
return MouseEvent (source, newPosition, mods, pressure, eventComponent,
originalComponent, eventTime, mouseDownPos, mouseDownTime,
numberOfClicks, wasMovedSinceMouseDown != 0);
}
MouseEvent MouseEvent::withNewPosition (Point<int> newPosition) const noexcept
{
return MouseEvent (source, newPosition.toFloat(), mods, eventComponent, originalComponent,
eventTime, mouseDownPos, mouseDownTime,
return MouseEvent (source, newPosition.toFloat(), mods, pressure, eventComponent,
originalComponent, eventTime, mouseDownPos, mouseDownTime,
numberOfClicks, wasMovedSinceMouseDown != 0);
}
@@ -112,6 +114,8 @@ int MouseEvent::getScreenY() const { return getScre
int MouseEvent::getMouseDownScreenX() const { return getMouseDownScreenPosition().x; }
int MouseEvent::getMouseDownScreenY() const { return getMouseDownScreenPosition().y; }
bool MouseEvent::isPressureValid() const noexcept { return pressure > 0.0f && pressure < 1.0f; }
//==============================================================================
static int doubleClickTimeOutMs = 400;


+ 14
- 0
libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseEvent.h View File

@@ -44,6 +44,9 @@ public:
@param source the source that's invoking the event
@param position the position of the mouse, relative to the component that is passed-in
@param modifiers the key modifiers at the time of the event
@param pressure the pressure of the touch or stylus, in the range 0 to 1. Devices that
do not support force information may return 0.0, 1.0, or a negative value,
depending on the platform
@param eventComponent the component that the mouse event applies to
@param originator the component that originally received the event
@param eventTime the time the event happened
@@ -59,6 +62,7 @@ public:
MouseEvent (MouseInputSource source,
Point<float> position,
ModifierKeys modifiers,
float pressure,
Component* eventComponent,
Component* originator,
Time eventTime,
@@ -109,6 +113,13 @@ public:
*/
const ModifierKeys mods;
/** The pressure of the touch or stylus for this event.
The range is 0 (soft) to 1 (hard).
If the input device doesn't provide any pressure data, it may return a negative
value here, or 0.0 or 1.0, depending on the platform.
*/
float pressure;
/** The component that this event applies to.
This is usually the component that the mouse was over at the time, but for mouse-drag
@@ -224,6 +235,9 @@ public:
*/
int getLengthOfMousePress() const noexcept;
/** Returns true if the pressure value for this event is meaningful. */
bool isPressureValid() const noexcept;
//==============================================================================
/** The position of the mouse when the event occurred.


+ 30
- 19
libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp View File

@@ -27,7 +27,7 @@ class MouseInputSourceInternal : private AsyncUpdater
public:
//==============================================================================
MouseInputSourceInternal (const int i, const bool isMouse)
: index (i), isMouseDevice (isMouse),
: index (i), isMouseDevice (isMouse), pressure (0.0f),
isUnboundedMouseModeOn (false), isCursorVisibleUntilOffscreen (false),
lastPeer (nullptr), currentCursorHandle (nullptr),
mouseEventCounter (0), mouseMovedSignificantlySincePressed (false)
@@ -40,17 +40,17 @@ public:
return buttonState.isAnyMouseButtonDown();
}
Component* getComponentUnderMouse() const
Component* getComponentUnderMouse() const noexcept
{
return componentUnderMouse.get();
}
ModifierKeys getCurrentModifiers() const
ModifierKeys getCurrentModifiers() const noexcept
{
return ModifierKeys::getCurrentModifiers().withoutMouseButtons().withFlags (buttonState.getRawFlags());
}
ComponentPeer* getPeer()
ComponentPeer* getPeer() noexcept
{
if (! ComponentPeer::isValidPeer (lastPeer))
lastPeer = nullptr;
@@ -102,6 +102,8 @@ public:
MouseInputSource::setRawMousePosition (ScalingHelpers::scaledScreenPosToUnscaled (p));
}
bool isPressureValid() const noexcept { return pressure > 0.0f && pressure < 1.0f; }
//==============================================================================
#if JUCE_DUMP_MOUSE_EVENTS
#define JUCE_MOUSE_EVENT_DBG(desc) DBG ("Mouse " << desc << " #" << index \
@@ -132,13 +134,13 @@ public:
void sendMouseDown (Component& comp, Point<float> screenPos, Time time)
{
JUCE_MOUSE_EVENT_DBG ("down")
comp.internalMouseDown (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
comp.internalMouseDown (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, pressure);
}
void sendMouseDrag (Component& comp, Point<float> screenPos, Time time)
{
JUCE_MOUSE_EVENT_DBG ("drag")
comp.internalMouseDrag (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time);
comp.internalMouseDrag (MouseInputSource (this), screenPosToLocalPos (comp, screenPos), time, pressure);
}
void sendMouseUp (Component& comp, Point<float> screenPos, Time time, const ModifierKeys oldMods)
@@ -287,15 +289,18 @@ public:
}
//==============================================================================
void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time, const ModifierKeys newMods)
void handleEvent (ComponentPeer& newPeer, Point<float> positionWithinPeer, Time time,
const ModifierKeys newMods, float newPressure)
{
lastTime = time;
const bool pressureChanged = (pressure != newPressure);
pressure = newPressure;
++mouseEventCounter;
const Point<float> screenPos (newPeer.localToGlobal (positionWithinPeer));
if (isDragging() && newMods.isAnyMouseButtonDown())
{
setScreenPos (screenPos, time, false);
setScreenPos (screenPos, time, pressureChanged);
}
else
{
@@ -307,8 +312,9 @@ public:
return; // some modal events have been dispatched, so the current event is now out-of-date
peer = getPeer();
if (peer != nullptr)
setScreenPos (screenPos, time, false);
setScreenPos (screenPos, time, pressureChanged);
}
}
}
@@ -470,6 +476,7 @@ public:
const bool isMouseDevice;
Point<float> lastScreenPos, unboundedMouseOffset; // NB: these are unscaled coords
ModifierKeys buttonState;
float pressure;
bool isUnboundedMouseModeOn, isCursorVisibleUntilOffscreen;
@@ -542,14 +549,16 @@ MouseInputSource& MouseInputSource::operator= (const MouseInputSource& other) no
return *this;
}
bool MouseInputSource::isMouse() const { return pimpl->isMouseDevice; }
bool MouseInputSource::isTouch() const { return ! isMouse(); }
bool MouseInputSource::canHover() const { return isMouse(); }
bool MouseInputSource::hasMouseWheel() const { return isMouse(); }
int MouseInputSource::getIndex() const { return pimpl->index; }
bool MouseInputSource::isDragging() const { return pimpl->isDragging(); }
Point<float> MouseInputSource::getScreenPosition() const { return pimpl->getScreenPosition(); }
ModifierKeys MouseInputSource::getCurrentModifiers() const { return pimpl->getCurrentModifiers(); }
bool MouseInputSource::isMouse() const noexcept { return pimpl->isMouseDevice; }
bool MouseInputSource::isTouch() const noexcept { return ! isMouse(); }
bool MouseInputSource::canHover() const noexcept { return isMouse(); }
bool MouseInputSource::hasMouseWheel() const noexcept { return isMouse(); }
int MouseInputSource::getIndex() const noexcept { return pimpl->index; }
bool MouseInputSource::isDragging() const noexcept { return pimpl->isDragging(); }
Point<float> MouseInputSource::getScreenPosition() const noexcept { return pimpl->getScreenPosition(); }
ModifierKeys MouseInputSource::getCurrentModifiers() const noexcept { return pimpl->getCurrentModifiers(); }
float MouseInputSource::getCurrentPressure() const noexcept { return pimpl->pressure; }
bool MouseInputSource::isPressureValid() const noexcept { return pimpl->isPressureValid(); }
Component* MouseInputSource::getComponentUnderMouse() const { return pimpl->getComponentUnderMouse(); }
void MouseInputSource::triggerFakeMove() const { pimpl->triggerFakeMove(); }
int MouseInputSource::getNumberOfMultipleClicks() const noexcept { return pimpl->getNumberOfMultipleClicks(); }
@@ -567,9 +576,9 @@ void MouseInputSource::revealCursor() { pimpl
void MouseInputSource::forceMouseCursorUpdate() { pimpl->revealCursor (true); }
void MouseInputSource::setScreenPosition (Point<float> p) { pimpl->setScreenPosition (p); }
void MouseInputSource::handleEvent (ComponentPeer& peer, Point<float> pos, int64 time, ModifierKeys mods)
void MouseInputSource::handleEvent (ComponentPeer& peer, Point<float> pos, int64 time, ModifierKeys mods, float pressure)
{
pimpl->handleEvent (peer, pos, Time (time), mods.withOnlyMouseButtons());
pimpl->handleEvent (peer, pos, Time (time), mods.withOnlyMouseButtons(), pressure);
}
void MouseInputSource::handleWheel (ComponentPeer& peer, Point<float> pos, int64 time, const MouseWheelDetails& wheel)
@@ -582,6 +591,8 @@ void MouseInputSource::handleMagnifyGesture (ComponentPeer& peer, Point<float> p
pimpl->handleMagnifyGesture (peer, pos, Time (time), scaleFactor);
}
const float MouseInputSource::invalidPressure = 0.0f;
//==============================================================================
struct MouseInputSource::SourceList : public Timer
{


+ 24
- 9
libs/juce/source/modules/juce_gui_basics/mouse/juce_MouseInputSource.h View File

@@ -60,18 +60,18 @@ public:
//==============================================================================
/** Returns true if this object represents a normal desk-based mouse device. */
bool isMouse() const;
bool isMouse() const noexcept;
/** Returns true if this object represents a source of touch events - i.e. a finger or stylus. */
bool isTouch() const;
bool isTouch() const noexcept;
/** Returns true if this source has an on-screen pointer that can hover over
items without clicking them.
*/
bool canHover() const;
bool canHover() const noexcept;
/** Returns true if this source may have a scroll wheel. */
bool hasMouseWheel() const;
bool hasMouseWheel() const noexcept;
/** Returns this source's index in the global list of possible sources.
If the system only has a single mouse, there will only be a single MouseInputSource
@@ -82,18 +82,28 @@ public:
number 0, and then if a second touch happens while the first is still down, it
will have index 1, etc.
*/
int getIndex() const;
int getIndex() const noexcept;
/** Returns true if this device is currently being pressed. */
bool isDragging() const;
bool isDragging() const noexcept;
/** Returns the last-known screen position of this source. */
Point<float> getScreenPosition() const;
Point<float> getScreenPosition() const noexcept;
/** Returns a set of modifiers that indicate which buttons are currently
held down on this device.
*/
ModifierKeys getCurrentModifiers() const;
ModifierKeys getCurrentModifiers() const noexcept;
/** Returns the device's current touch or pen pressure.
The range is 0 (soft) to 1 (hard).
If the input device doesn't provide any pressure data, it may return a negative
value here, or 0.0 or 1.0, depending on the platform.
*/
float getCurrentPressure() const noexcept;
/** Returns true if the current pressure value is meaningful. */
bool isPressureValid() const noexcept;
/** Returns the component that was last known to be under this pointer. */
Component* getComponentUnderMouse() const;
@@ -164,6 +174,11 @@ public:
/** Attempts to set this mouse pointer's screen position. */
void setScreenPosition (Point<float> newPosition);
/** A default value for pressure, which is used when a device doesn't support it, or for
mouse-moves, mouse-ups, etc.
*/
static const float invalidPressure;
private:
//==============================================================================
friend class ComponentPeer;
@@ -174,7 +189,7 @@ private:
struct SourceList;
explicit MouseInputSource (MouseInputSourceInternal*) noexcept;
void handleEvent (ComponentPeer&, Point<float>, int64 time, ModifierKeys);
void handleEvent (ComponentPeer&, Point<float>, int64 time, ModifierKeys, float);
void handleWheel (ComponentPeer&, Point<float>, int64 time, const MouseWheelDetails&);
void handleMagnifyGesture (ComponentPeer&, Point<float>, int64 time, float scaleFactor);


+ 17
- 11
libs/juce/source/modules/juce_gui_basics/native/juce_android_Windowing.cpp View File

@@ -33,6 +33,8 @@ namespace juce
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, launchApp, void, (JNIEnv* env, jobject activity,
jstring appFile, jstring appDataDir))
{
setEnv (env);
android.initialise (env, activity, appFile, appDataDir);
DBG (SystemStats::getJUCEVersion());
@@ -56,18 +58,24 @@ JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, launchApp, void, (JNIEnv* en
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, suspendApp, void, (JNIEnv* env, jobject activity))
{
setEnv (env);
if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
app->suspended();
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, resumeApp, void, (JNIEnv* env, jobject activity))
{
setEnv (env);
if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
app->resumed();
}
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, quitApp, void, (JNIEnv* env, jobject activity))
{
setEnv (env);
JUCEApplicationBase::appWillTerminateByForce();
android.shutdown (env);
@@ -98,7 +106,6 @@ DECLARE_JNI_CLASS (CanvasMinimal, "android/graphics/Canvas");
METHOD (invalidate, "invalidate", "(IIII)V") \
METHOD (containsPoint, "containsPoint", "(II)Z") \
METHOD (showKeyboard, "showKeyboard", "(Ljava/lang/String;)V") \
METHOD (createGLView, "createGLView", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$OpenGLView;") \
DECLARE_JNI_CLASS (ComponentPeerView, JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView");
#undef JNI_CLASS_MEMBERS
@@ -332,7 +339,7 @@ public:
lastMousePos = pos;
// this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
handleMouseEvent (index, pos, currentModifiers.withoutMouseButtons(), time);
handleMouseEvent (index, pos, currentModifiers.withoutMouseButtons(), MouseInputSource::invalidPressure, time);
if (isValidPeer (this))
handleMouseDragCallback (index, sysPos, time);
@@ -346,8 +353,8 @@ public:
jassert (index < 64);
touchesDown = (touchesDown | (1 << (index & 63)));
currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
handleMouseEvent (index, pos, currentModifiers.withoutMouseButtons()
.withFlags (ModifierKeys::leftButtonModifier), time);
handleMouseEvent (index, pos, currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier),
MouseInputSource::invalidPressure, time);
}
void handleMouseUpCallback (int index, Point<float> pos, int64 time)
@@ -361,7 +368,7 @@ public:
if (touchesDown == 0)
currentModifiers = currentModifiers.withoutMouseButtons();
handleMouseEvent (index, pos, currentModifiers.withoutMouseButtons(), time);
handleMouseEvent (index, pos, currentModifiers.withoutMouseButtons(), MouseInputSource::invalidPressure, time);
}
void handleKeyDownCallback (int k, int kc)
@@ -582,6 +589,7 @@ int64 AndroidComponentPeer::touchesDown = 0;
#define JUCE_VIEW_CALLBACK(returnType, javaMethodName, params, juceMethodInvocation) \
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024ComponentPeerView), javaMethodName, returnType, params) \
{ \
setEnv (env); \
if (AndroidComponentPeer* peer = (AndroidComponentPeer*) (pointer_sized_uint) host) \
peer->juceMethodInvocation; \
}
@@ -601,12 +609,6 @@ ComponentPeer* Component::createNewPeer (int styleFlags, void*)
return new AndroidComponentPeer (*this, styleFlags);
}
jobject createOpenGLView (ComponentPeer* peer)
{
jobject parentView = static_cast<jobject> (peer->getNativeHandle());
return getEnv()->CallObjectMethod (parentView, ComponentPeerView.createGLView);
}
//==============================================================================
bool Desktop::canUseSemiTransparentWindows() noexcept
{
@@ -700,6 +702,8 @@ int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconTy
JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, alertDismissed, void, (JNIEnv* env, jobject activity,
jlong callbackAsLong, jint result))
{
setEnv (env);
if (ModalComponentManager::Callback* callback = (ModalComponentManager::Callback*) callbackAsLong)
{
callback->modalStateFinished (result);
@@ -748,6 +752,8 @@ JUCE_JNI_CALLBACK (JUCE_ANDROID_ACTIVITY_CLASSNAME, setScreenSize, void, (JNIEnv
jint screenWidth, jint screenHeight,
jint dpi))
{
setEnv (env);
android.screenWidth = screenWidth;
android.screenHeight = screenHeight;
android.dpi = dpi;


+ 20
- 3
libs/juce/source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm View File

@@ -767,7 +767,11 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons
{
UITouch* touch = [touches objectAtIndex: i];
#if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
if ([touch phase] == UITouchPhaseStationary && touch.maximumPossibleForce <= 0)
#else
if ([touch phase] == UITouchPhaseStationary)
#endif
continue;
CGPoint p = [touch locationInView: view];
@@ -788,7 +792,9 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons
modsToSend = currentModifiers;
// this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
handleMouseEvent (touchIndex, pos, modsToSend.withoutMouseButtons(), time);
handleMouseEvent (touchIndex, pos, modsToSend.withoutMouseButtons(),
MouseInputSource::invalidPressure, time);
if (! isValidPeer (this)) // (in case this component was deleted by the event)
return;
}
@@ -810,13 +816,24 @@ void UIViewComponentPeer::handleTouches (UIEvent* event, const bool isDown, cons
modsToSend = currentModifiers = currentModifiers.withoutMouseButtons();
}
handleMouseEvent (touchIndex, pos, modsToSend, time);
float pressure = MouseInputSource::invalidPressure;
#if defined (__IPHONE_9_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_9_0
if (touch.maximumPossibleForce > 0)
// NB: other devices return 0 or 1.0 if pressure is unknown, so we'll clip our value to a believable range:
pressure = jlimit (0.0001f, 0.9999f, (float) (touch.force / touch.maximumPossibleForce));
#endif
handleMouseEvent (touchIndex, pos, modsToSend, pressure, time);
if (! isValidPeer (this)) // (in case this component was deleted by the event)
return;
if (isUp || isCancel)
{
handleMouseEvent (touchIndex, Point<float> (-1.0f, -1.0f), modsToSend, time);
handleMouseEvent (touchIndex, Point<float> (-1.0f, -1.0f),
modsToSend, MouseInputSource::invalidPressure, time);
if (! isValidPeer (this))
return;
}


+ 12
- 0
libs/juce/source/modules/juce_gui_basics/native/juce_ios_Windowing.mm View File

@@ -24,6 +24,15 @@
extern bool isIOSAppActive;
struct AppInactivityCallback // NB: careful, this declaration is duplicated in other modules
{
virtual ~AppInactivityCallback() {}
virtual void appBecomingInactive() = 0;
};
// This is an internal list of callbacks (but currently used between modules)
Array<AppInactivityCallback*> appBecomingInactiveCallbacks;
} // (juce namespace)
@interface JuceAppStartupDelegate : NSObject <UIApplicationDelegate>
@@ -89,6 +98,9 @@ extern bool isIOSAppActive;
{
ignoreUnused (application);
isIOSAppActive = false;
for (int i = appBecomingInactiveCallbacks.size(); --i >= 0;)
appBecomingInactiveCallbacks.getReference(i)->appBecomingInactive();
}
@end


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save