Browse Source

Update juce and zynaddsubfx

tags/1.9.7
falkTX 9 years ago
parent
commit
0399a96410
78 changed files with 3458 additions and 946 deletions
  1. +639
    -92
      source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
  2. +0
    -1
      source/modules/juce_audio_basics/juce_audio_basics.cpp
  3. +1
    -1
      source/modules/juce_audio_basics/juce_audio_basics.h
  4. +37
    -6
      source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
  5. +24
    -4
      source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h
  6. +288
    -36
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  7. +45
    -2
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
  8. +294
    -18
      source/modules/juce_audio_devices/native/juce_android_Midi.cpp
  9. +218
    -99
      source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  10. +12
    -6
      source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  11. +1
    -1
      source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
  12. +1
    -1
      source/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp
  13. +2
    -1
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  14. +1
    -0
      source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp
  15. +43
    -19
      source/modules/juce_audio_processors/format_types/juce_VST3Common.h
  16. +48
    -31
      source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  17. +148
    -104
      source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  18. +2
    -0
      source/modules/juce_audio_processors/juce_audio_processors.cpp
  19. +6
    -0
      source/modules/juce_audio_processors/juce_audio_processors.h
  20. +29
    -2
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  21. +113
    -2
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  22. +235
    -54
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  23. +18
    -7
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
  24. +4
    -0
      source/modules/juce_audio_processors/processors/juce_PluginDescription.cpp
  25. +5
    -0
      source/modules/juce_audio_processors/processors/juce_PluginDescription.h
  26. +9
    -0
      source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp
  27. +2
    -1
      source/modules/juce_audio_processors/scanning/juce_KnownPluginList.h
  28. +7
    -2
      source/modules/juce_core/containers/juce_Array.h
  29. +35
    -0
      source/modules/juce_core/files/juce_File.cpp
  30. +21
    -15
      source/modules/juce_core/files/juce_File.h
  31. +2
    -0
      source/modules/juce_core/maths/juce_NormalisableRange.h
  32. +60
    -6
      source/modules/juce_core/memory/juce_ByteOrder.h
  33. +222
    -72
      source/modules/juce_core/native/java/JuceAppActivity.java
  34. +28
    -133
      source/modules/juce_core/native/juce_android_JNIHelpers.h
  35. +11
    -25
      source/modules/juce_core/native/juce_android_SystemStats.cpp
  36. +232
    -0
      source/modules/juce_core/native/juce_android_Threads.cpp
  37. +1
    -1
      source/modules/juce_core/native/juce_linux_CommonFile.cpp
  38. +1
    -1
      source/modules/juce_core/native/juce_linux_Files.cpp
  39. +7
    -2
      source/modules/juce_core/native/juce_mac_Files.mm
  40. +5
    -18
      source/modules/juce_core/native/juce_posix_SharedCode.h
  41. +34
    -2
      source/modules/juce_core/native/juce_win32_Files.cpp
  42. +6
    -0
      source/modules/juce_core/system/juce_CompilerSupport.h
  43. +35
    -29
      source/modules/juce_core/system/juce_PlatformDefs.h
  44. +4
    -4
      source/modules/juce_core/system/juce_StandardHeader.h
  45. +10
    -0
      source/modules/juce_core/text/juce_CharacterFunctions.cpp
  46. +10
    -0
      source/modules/juce_core/text/juce_CharacterFunctions.h
  47. +152
    -27
      source/modules/juce_core/text/juce_String.cpp
  48. +14
    -0
      source/modules/juce_core/text/juce_String.h
  49. +6
    -0
      source/modules/juce_core/text/juce_StringArray.cpp
  50. +9
    -0
      source/modules/juce_core/text/juce_StringArray.h
  51. +41
    -0
      source/modules/juce_core/unit_tests/juce_UnitTest.h
  52. +2
    -0
      source/modules/juce_events/native/juce_android_Messaging.cpp
  53. +1
    -1
      source/modules/juce_graphics/native/juce_android_Fonts.cpp
  54. +12
    -12
      source/modules/juce_gui_basics/components/juce_Component.cpp
  55. +2
    -2
      source/modules/juce_gui_basics/components/juce_Component.h
  56. +2
    -2
      source/modules/juce_gui_basics/components/juce_Desktop.cpp
  57. +9
    -5
      source/modules/juce_gui_basics/mouse/juce_MouseEvent.cpp
  58. +14
    -0
      source/modules/juce_gui_basics/mouse/juce_MouseEvent.h
  59. +30
    -19
      source/modules/juce_gui_basics/mouse/juce_MouseInputSource.cpp
  60. +24
    -9
      source/modules/juce_gui_basics/mouse/juce_MouseInputSource.h
  61. +17
    -11
      source/modules/juce_gui_basics/native/juce_android_Windowing.cpp
  62. +20
    -3
      source/modules/juce_gui_basics/native/juce_ios_UIViewComponentPeer.mm
  63. +12
    -0
      source/modules/juce_gui_basics/native/juce_ios_Windowing.mm
  64. +10
    -5
      source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp
  65. +19
    -4
      source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm
  66. +16
    -12
      source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp
  67. +2
    -2
      source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp
  68. +1
    -1
      source/modules/juce_gui_basics/windows/juce_ComponentPeer.h
  69. +4
    -3
      source/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp
  70. +1
    -0
      source/modules/juce_gui_extra/native/juce_win32_ActiveXComponent.cpp
  71. +2
    -2
      source/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp
  72. +18
    -3
      source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp
  73. +22
    -19
      source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp
  74. +13
    -0
      source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp
  75. +7
    -1
      source/native-plugins/zynaddsubfx/Params/ADnoteParameters.h
  76. +11
    -2
      source/native-plugins/zynaddsubfx/Synth/ADnote.cpp
  77. +1
    -1
      source/native-plugins/zynaddsubfx/UI/ADnoteUI.fl
  78. +8
    -2
      source/native-plugins/zynaddsubfx/UI/Fl_Osc_Dial.cpp

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


+ 0
- 1
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
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"


+ 37
- 6
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
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
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
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


+ 294
- 18
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
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;


+ 12
- 6
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)
{
@@ -352,14 +352,20 @@ public:
int getLatencyFromDevice (AudioObjectPropertyScope scope) const
{
UInt32 lat = 0;
UInt32 size = sizeof (lat);
UInt32 latency = 0;
UInt32 size = sizeof (latency);
AudioObjectPropertyAddress pa;
pa.mElement = kAudioObjectPropertyElementMaster;
pa.mSelector = kAudioDevicePropertyLatency;
pa.mScope = scope;
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &lat);
return (int) lat;
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &latency);
UInt32 safetyOffset = 0;
size = sizeof (safetyOffset);
pa.mSelector = kAudioDevicePropertySafetyOffset;
AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &safetyOffset);
return (int) (latency + safetyOffset);
}
int getBitDepthFromDevice (AudioObjectPropertyScope scope) const
@@ -1945,7 +1951,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
source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp View File

@@ -249,7 +249,7 @@ namespace AiffFileHelpers
data += isGenre ? 118 : 50;
if (data[0] == 0)
if (data < dataEnd && data[0] == 0)
{
if (data + 52 < dataEnd && isValidTag (data + 50)) data += 50;
else if (data + 120 < dataEnd && isValidTag (data + 118)) data += 118;


+ 1
- 1
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;
}


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

@@ -374,6 +374,7 @@ public:
^ ((int) componentDesc.componentSubType)
^ ((int) componentDesc.componentManufacturer);
desc.lastFileModTime = Time();
desc.lastInfoUpdateTime = Time::getCurrentTime();
desc.pluginFormatName = "AudioUnit";
desc.category = AudioUnitFormatHelpers::getCategory (componentDesc.componentType);
desc.manufacturerName = manufacturer;
@@ -1223,7 +1224,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


+ 1
- 0
source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp View File

@@ -220,6 +220,7 @@ public:
desc.fileOrIdentifier = module->file.getFullPathName();
desc.uid = getUID();
desc.lastFileModTime = module->file.getLastModificationTime();
desc.lastInfoUpdateTime = Time::getCurrentTime();
desc.pluginFormatName = "LADSPA";
desc.category = getCategory();
desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String();


+ 43
- 19
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

+ 48
- 31
source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp View File

@@ -110,6 +110,7 @@ static void createPluginDescription (PluginDescription& description,
{
description.fileOrIdentifier = pluginFile.getFullPathName();
description.lastFileModTime = pluginFile.getLastModificationTime();
description.lastInfoUpdateTime = Time::getCurrentTime();
description.manufacturerName = company;
description.name = name;
description.descriptiveName = name;
@@ -1702,7 +1703,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 +1770,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 +2169,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 +2411,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();


+ 148
- 104
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
{
@@ -805,6 +804,7 @@ public:
desc.fileOrIdentifier = module->file.getFullPathName();
desc.uid = getUID();
desc.lastFileModTime = module->file.getLastModificationTime();
desc.lastInfoUpdateTime = Time::getCurrentTime();
desc.pluginFormatName = "VST";
desc.category = getCategory();
@@ -938,6 +938,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 +992,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 +1597,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
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
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"
}


+ 29
- 2
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
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
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
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;


+ 4
- 0
source/modules/juce_audio_processors/processors/juce_PluginDescription.cpp View File

@@ -44,6 +44,7 @@ PluginDescription::PluginDescription (const PluginDescription& other)
version (other.version),
fileOrIdentifier (other.fileOrIdentifier),
lastFileModTime (other.lastFileModTime),
lastInfoUpdateTime (other.lastInfoUpdateTime),
uid (other.uid),
isInstrument (other.isInstrument),
numInputChannels (other.numInputChannels),
@@ -64,6 +65,7 @@ PluginDescription& PluginDescription::operator= (const PluginDescription& other)
uid = other.uid;
isInstrument = other.isInstrument;
lastFileModTime = other.lastFileModTime;
lastInfoUpdateTime = other.lastInfoUpdateTime;
numInputChannels = other.numInputChannels;
numOutputChannels = other.numOutputChannels;
hasSharedContainer = other.hasSharedContainer;
@@ -108,6 +110,7 @@ XmlElement* PluginDescription::createXml() const
e->setAttribute ("uid", String::toHexString (uid));
e->setAttribute ("isInstrument", isInstrument);
e->setAttribute ("fileTime", String::toHexString (lastFileModTime.toMilliseconds()));
e->setAttribute ("infoUpdateTime", String::toHexString (lastInfoUpdateTime.toMilliseconds()));
e->setAttribute ("numInputs", numInputChannels);
e->setAttribute ("numOutputs", numOutputChannels);
e->setAttribute ("isShell", hasSharedContainer);
@@ -129,6 +132,7 @@ bool PluginDescription::loadFromXml (const XmlElement& xml)
uid = xml.getStringAttribute ("uid").getHexValue32();
isInstrument = xml.getBoolAttribute ("isInstrument", false);
lastFileModTime = Time (xml.getStringAttribute ("fileTime").getHexValue64());
lastInfoUpdateTime = Time (xml.getStringAttribute ("infoUpdateTime").getHexValue64());
numInputChannels = xml.getIntAttribute ("numInputs");
numOutputChannels = xml.getIntAttribute ("numOutputs");
hasSharedContainer = xml.getBoolAttribute ("isShell", false);


+ 5
- 0
source/modules/juce_audio_processors/processors/juce_PluginDescription.h View File

@@ -81,6 +81,11 @@ public:
*/
Time lastFileModTime;
/** The last time that this information was updated. This would typically have
been during a scan when this plugin was first tested or found to have changed.
*/
Time lastInfoUpdateTime;
/** A unique ID for the plug-in.
Note that this might not be unique between formats, e.g. a VST and some


+ 9
- 0
source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp View File

@@ -263,6 +263,7 @@ struct PluginSorter
case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareNatural (second->manufacturerName); break;
case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break;
case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break;
case KnownPluginList::sortByInfoUpdateTime: diff = compare (first->lastInfoUpdateTime, second->lastInfoUpdateTime); break;
default: break;
}
@@ -278,6 +279,14 @@ private:
return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false);
}
static int compare (Time a, Time b) noexcept
{
if (a < b) return -1;
if (b < a) return 1;
return 0;
}
const KnownPluginList::SortMethod method;
const int direction;


+ 2
- 1
source/modules/juce_audio_processors/scanning/juce_KnownPluginList.h View File

@@ -136,7 +136,8 @@ public:
sortByCategory,
sortByManufacturer,
sortByFormat,
sortByFileSystemLocation
sortByFileSystemLocation,
sortByInfoUpdateTime
};
//==============================================================================


+ 7
- 2
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
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
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;


+ 2
- 0
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
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)); }


+ 222
- 72
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
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
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
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
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
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
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
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
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
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
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)
//==============================================================================
#if 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, #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)


+ 4
- 4
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.
@@ -52,8 +52,8 @@
//==============================================================================
#include <vector> // included before platform defs to provide a definition of _LIBCPP_VERSION
#include "juce_PlatformDefs.h"
#include "juce_CompilerSupport.h"
#include "juce_PlatformDefs.h"
//==============================================================================
// Now we'll include some common OS headers..


+ 10
- 0
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
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
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
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
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
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
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.


+ 2
- 0
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
source/modules/juce_graphics/native/juce_android_Fonts.cpp View File

@@ -165,7 +165,7 @@ public:
}
AndroidTypeface (const void* data, size_t size)
: Typeface (String(), String())
: Typeface (String (static_cast<uint64> (reinterpret_cast<uintptr_t> (data))), String())
{
JNIEnv* const env = getEnv();


+ 12
- 12
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
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
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);


+ 9
- 5
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
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
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
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
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
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
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


+ 10
- 5
source/modules/juce_gui_basics/native/juce_linux_Windowing.cpp View File

@@ -2215,7 +2215,8 @@ public:
{
currentModifiers = currentModifiers.withFlags (buttonModifierFlag);
toFront (true);
handleMouseEvent (0, getMousePos (buttonPressEvent), currentModifiers, getEventTime (buttonPressEvent));
handleMouseEvent (0, getMousePos (buttonPressEvent), currentModifiers,
MouseInputSource::invalidPressure, getEventTime (buttonPressEvent));
}
void handleButtonPressEvent (const XButtonPressedEvent& buttonPressEvent)
@@ -2253,7 +2254,8 @@ public:
if (dragState.dragging)
handleExternalDragButtonReleaseEvent();
handleMouseEvent (0, getMousePos (buttonRelEvent), currentModifiers, getEventTime (buttonRelEvent));
handleMouseEvent (0, getMousePos (buttonRelEvent), currentModifiers,
MouseInputSource::invalidPressure, getEventTime (buttonRelEvent));
clearLastMousePos();
}
@@ -2267,7 +2269,8 @@ public:
if (dragState.dragging)
handleExternalDragMotionNotify();
handleMouseEvent (0, getMousePos (movedEvent), currentModifiers, getEventTime (movedEvent));
handleMouseEvent (0, getMousePos (movedEvent), currentModifiers,
MouseInputSource::invalidPressure, getEventTime (movedEvent));
}
void handleEnterNotifyEvent (const XEnterWindowEvent& enterEvent)
@@ -2280,7 +2283,8 @@ public:
if (! currentModifiers.isAnyMouseButtonDown())
{
updateKeyModifiers ((int) enterEvent.state);
handleMouseEvent (0, getMousePos (enterEvent), currentModifiers, getEventTime (enterEvent));
handleMouseEvent (0, getMousePos (enterEvent), currentModifiers,
MouseInputSource::invalidPressure, getEventTime (enterEvent));
}
}
@@ -2293,7 +2297,8 @@ public:
|| leaveEvent.mode == NotifyUngrab)
{
updateKeyModifiers ((int) leaveEvent.state);
handleMouseEvent (0, getMousePos (leaveEvent), currentModifiers, getEventTime (leaveEvent));
handleMouseEvent (0, getMousePos (leaveEvent), currentModifiers,
MouseInputSource::invalidPressure, getEventTime (leaveEvent));
}
}


+ 19
- 4
source/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm View File

@@ -587,7 +587,8 @@ public:
sendMouseEvent (ev);
else
// moved into another window which overlaps this one, so trigger an exit
handleMouseEvent (0, Point<float> (-1.0f, -1.0f), currentModifiers, getMouseTime (ev));
handleMouseEvent (0, Point<float> (-1.0f, -1.0f), currentModifiers,
getMousePressure (ev), getMouseTime (ev));
showArrowCursorIfNeeded();
}
@@ -678,7 +679,8 @@ public:
void sendMouseEvent (NSEvent* ev)
{
updateModifiers (ev);
handleMouseEvent (0, getMousePos (ev, view), currentModifiers, getMouseTime (ev));
handleMouseEvent (0, getMousePos (ev, view), currentModifiers,
getMousePressure (ev), getMouseTime (ev));
}
bool handleKeyEvent (NSEvent* ev, bool isKeyDown)
@@ -1080,10 +1082,23 @@ public:
return keyCode;
}
static int64 getMouseTime (NSEvent* e)
static int64 getMouseTime (NSEvent* e) noexcept
{
return (Time::currentTimeMillis() - Time::getMillisecondCounter())
+ (int64) ([e timestamp] * 1000.0);
+ (int64) ([e timestamp] * 1000.0);
}
static float getMousePressure (NSEvent* e) noexcept
{
@try
{
if (e.type != NSMouseEntered && e.type != NSMouseExited)
return (float) e.pressure;
}
@catch (NSException* e) {}
@finally {}
return 0.0f;
}
static Point<float> getMousePos (NSEvent* e, NSView* view)


+ 16
- 12
source/modules/juce_gui_basics/native/juce_win32_Windowing.cpp View File

@@ -1663,9 +1663,9 @@ private:
}
//==============================================================================
void doMouseEvent (Point<float> position)
void doMouseEvent (Point<float> position, float pressure)
{
handleMouseEvent (0, position, currentModifiers, getMouseEventTime());
handleMouseEvent (0, position, currentModifiers, pressure, getMouseEventTime());
}
StringArray getAvailableRenderingEngines() override
@@ -1760,7 +1760,7 @@ private:
if (now >= lastMouseTime + minTimeBetweenMouses)
{
lastMouseTime = now;
doMouseEvent (position);
doMouseEvent (position, MouseInputSource::invalidPressure);
}
}
@@ -1780,7 +1780,7 @@ private:
updateModifiersFromWParam (wParam);
isDragging = true;
doMouseEvent (position);
doMouseEvent (position, MouseInputSource::invalidPressure);
}
}
@@ -1801,7 +1801,7 @@ private:
// NB: under some circumstances (e.g. double-clicking a native title bar), a mouse-up can
// arrive without a mouse-down, so in that case we need to avoid sending a message.
if (wasDragging)
doMouseEvent (position);
doMouseEvent (position, MouseInputSource::invalidPressure);
}
void doCaptureChanged()
@@ -1821,7 +1821,7 @@ private:
void doMouseExit()
{
isMouseOver = false;
doMouseEvent (getCurrentMousePos());
doMouseEvent (getCurrentMousePos(), MouseInputSource::invalidPressure);
}
ComponentPeer* findPeerUnderMouse (Point<float>& localPos)
@@ -1925,6 +1925,7 @@ private:
const int64 time = getMouseEventTime();
const Point<float> pos (globalToLocal (Point<float> (static_cast<float> (TOUCH_COORD_TO_PIXEL (touch.x)),
static_cast<float> (TOUCH_COORD_TO_PIXEL (touch.y)))));
const float pressure = MouseInputSource::invalidPressure;
ModifierKeys modsToSend (currentModifiers);
if (isDown)
@@ -1932,9 +1933,10 @@ private:
currentModifiers = currentModifiers.withoutMouseButtons().withFlags (ModifierKeys::leftButtonModifier);
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.toFloat(), modsToSend.withoutMouseButtons(), time);
if (! isValidPeer (this)) // (in case this component was deleted by the event)
// this forces a mouse-enter/up event, in case for some reason we didn't get a mouse-up before.
handleMouseEvent (touchIndex, pos.toFloat(), modsToSend.withoutMouseButtons(), pressure, time);
if (! isValidPeer (this)) // (in case this component was deleted by the event)
return false;
}
else if (isUp)
@@ -1956,13 +1958,15 @@ private:
currentModifiers = currentModifiers.withoutMouseButtons();
}
handleMouseEvent (touchIndex, pos.toFloat(), modsToSend, time);
handleMouseEvent (touchIndex, pos.toFloat(), modsToSend, pressure, time);
if (! isValidPeer (this)) // (in case this component was deleted by the event)
return false;
if (isUp || isCancel)
{
handleMouseEvent (touchIndex, Point<float> (-10.0f, -10.0f), currentModifiers, time);
handleMouseEvent (touchIndex, Point<float> (-10.0f, -10.0f), currentModifiers, pressure, time);
if (! isValidPeer (this))
return false;
}
@@ -2248,7 +2252,7 @@ private:
if (contains (pos.roundToInt(), false))
{
doMouseEvent (pos);
doMouseEvent (pos, MouseInputSource::invalidPressure);
if (! isValidPeer (this))
return true;


+ 2
- 2
source/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp View File

@@ -85,10 +85,10 @@ bool ComponentPeer::isKioskMode() const
}
//==============================================================================
void ComponentPeer::handleMouseEvent (int touchIndex, Point<float> pos, ModifierKeys newMods, int64 time)
void ComponentPeer::handleMouseEvent (int touchIndex, Point<float> pos, ModifierKeys newMods, float newPressure, int64 time)
{
if (MouseInputSource* mouse = Desktop::getInstance().mouseSources->getOrCreateMouseInputSource (touchIndex))
MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods);
MouseInputSource (*mouse).handleEvent (*this, pos, time, newMods, newPressure);
}
void ComponentPeer::handleMouseWheel (int touchIndex, Point<float> pos, int64 time, const MouseWheelDetails& wheel)


+ 1
- 1
source/modules/juce_gui_basics/windows/juce_ComponentPeer.h View File

@@ -306,7 +306,7 @@ public:
virtual void setAlpha (float newAlpha) = 0;
//==============================================================================
void handleMouseEvent (int touchIndex, Point<float> positionWithinPeer, ModifierKeys newMods, int64 time);
void handleMouseEvent (int touchIndex, Point<float> positionWithinPeer, ModifierKeys newMods, float pressure, int64 time);
void handleMouseWheel (int touchIndex, Point<float> positionWithinPeer, int64 time, const MouseWheelDetails&);
void handleMagnifyGesture (int touchIndex, Point<float> positionWithinPeer, int64 time, float scaleFactor);


+ 4
- 3
source/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp View File

@@ -104,6 +104,7 @@ public:
const Time now (Time::getCurrentTime());
MouseInputSource mouseSource = Desktop::getInstance().getMainMouseSource();
const float pressure = (float) e.pressure;
if (isLeft || isRight) // Only mouse up is sent by the OS, so simulate a down/up
{
@@ -113,17 +114,17 @@ public:
owner.mouseDown (MouseEvent (mouseSource, Point<float>(),
eventMods.withFlags (isLeft ? ModifierKeys::leftButtonModifier
: ModifierKeys::rightButtonModifier),
&owner, &owner, now,
pressure, &owner, &owner, now,
Point<float>(), now, 1, false));
owner.mouseUp (MouseEvent (mouseSource, Point<float>(), eventMods.withoutMouseButtons(),
&owner, &owner, now,
pressure, &owner, &owner, now,
Point<float>(), now, 1, false));
}
else if (type == NSMouseMoved)
{
owner.mouseMove (MouseEvent (mouseSource, Point<float>(), eventMods,
&owner, &owner, now,
pressure, &owner, &owner, now,
Point<float>(), now, 1, false));
}
}


+ 1
- 0
source/modules/juce_gui_extra/native/juce_win32_ActiveXComponent.cpp View File

@@ -189,6 +189,7 @@ namespace ActiveXHelpers
peer->handleMouseEvent (0, Point<int> (GET_X_LPARAM (lParam) + activeXRect.left - peerRect.left,
GET_Y_LPARAM (lParam) + activeXRect.top - peerRect.top).toFloat(),
ModifierKeys::getCurrentModifiersRealtime(),
MouseInputSource::invalidPressure,
getMouseEventTime());
break;


+ 2
- 2
source/modules/juce_gui_extra/native/juce_win32_SystemTrayIcon.cpp View File

@@ -111,8 +111,8 @@ public:
const Time eventTime (getMouseEventTime());
const MouseEvent e (Desktop::getInstance().getMainMouseSource(),
Point<float>(), eventMods, &owner, &owner, eventTime,
Point<float>(), eventTime, 1, false);
Point<float>(), eventMods, MouseInputSource::invalidPressure,
&owner, &owner, eventTime, Point<float>(), eventTime, 1, false);
if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
{


+ 18
- 3
source/native-plugins/zynaddsubfx/Misc/MiddleWare.cpp View File

@@ -877,6 +877,12 @@ rtosc::Ports bankPorts = {

#undef rObject
#define rObject MiddleWareImpl

#ifndef STRINGIFY
#define STRINGIFY2(a) #a
#define STRINGIFY(a) STRINGIFY2(a)
#endif

/*
* BASE/part#/kititem#
* BASE/part#/kit#/adpars/voice#/oscil/\*
@@ -885,15 +891,20 @@ rtosc::Ports bankPorts = {
* BASE/part#/kit#/padpars/oscil/\*
*/
static rtosc::Ports middwareSnoopPorts = {
{"part#16/kit#8/adpars/VoicePar#8/OscilSmp/", 0, &OscilGen::non_realtime_ports,
{"part#" STRINGIFY(NUM_MIDI_PARTS)
"/kit#" STRINGIFY(NUM_KIT_ITEMS) "/adpars/VoicePar#"
STRINGIFY(NUM_VOICES) "/OscilSmp/", 0, &OscilGen::non_realtime_ports,
rBegin;
impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
rEnd},
{"part#16/kit#8/adpars/VoicePar#8/FMSmp/", 0, &OscilGen::non_realtime_ports,
{"part#" STRINGIFY(NUM_MIDI_PARTS)
"/kit#" STRINGIFY(NUM_KIT_ITEMS)
"/adpars/VoicePar#" STRINGIFY(NUM_VOICES) "/FMSmp/", 0, &OscilGen::non_realtime_ports,
rBegin
impl.obj_store.handleOscil(chomp(chomp(chomp(chomp(chomp(msg))))), d);
rEnd},
{"part#16/kit#8/padpars/", 0, &PADnoteParameters::non_realtime_ports,
{"part#" STRINGIFY(NUM_MIDI_PARTS)
"/kit#" STRINGIFY(NUM_KIT_ITEMS) "/padpars/", 0, &PADnoteParameters::non_realtime_ports,
rBegin
impl.obj_store.handlePad(chomp(chomp(chomp(msg))), d);
rEnd},
@@ -1062,6 +1073,10 @@ static rtosc::Ports middlewareReplyPorts = {
impl.loadPart(part, impl.master->bank.ins[program].filename.c_str(), impl.master);
impl.uToB->write(("/part"+to_s(part)+"/Pname").c_str(), "s", impl.master->bank.ins[program].name.c_str());
rEnd},
{"setbank:c", 0, 0,
rBegin;
impl.loadPendingBank(rtosc_argument(msg,0).i, impl.master->bank);
rEnd},
{"undo_pause:", 0, 0, rBegin; impl.recording_undo = false; rEnd},
{"undo_resume:", 0, 0, rBegin; impl.recording_undo = true; rEnd},
{"undo_change", 0, 0,


+ 22
- 19
source/native-plugins/zynaddsubfx/Misc/PresetExtractor.cpp View File

@@ -70,7 +70,6 @@ const rtosc::Ports real_preset_ports =
assert(d.obj);
std::string args = rtosc_argument_string(msg);
d.reply(d.loc, "s", "clipboard paste...");
printf("\nClipboard Paste...\n");
if(args == "s")
presetPaste(mw, rtosc_argument(msg, 0).s, "");
else if(args == "ss")
@@ -146,7 +145,7 @@ class Capture:public rtosc::RtData

virtual void reply(const char *path, const char *args, ...)
{
printf("reply(%p)(%s)(%s)...\n", msgbuf, path, args);
//printf("reply(%p)(%s)(%s)...\n", msgbuf, path, args);
//printf("size is %d\n", sizeof(msgbuf));
va_list va;
va_start(va,args);
@@ -199,7 +198,7 @@ std::string doCopy(MiddleWare &mw, string url, string name)
mw.doReadOnlyOp([&xml, url, name, &mw](){
Master *m = mw.spawnMaster();
//Get the pointer
printf("capture at <%s>\n", (url+"self").c_str());
//printf("capture at <%s>\n", (url+"self").c_str());
T *t = (T*)capture<void*>(m, url+"self");
assert(t);
//Extract Via mxml
@@ -216,6 +215,10 @@ void doPaste(MiddleWare &mw, string url, string type, XMLwrapper &xml, Ts&&... a
//Generate a new object
T *t = new T(std::forward<Ts>(args)...);
//Old workaround for LFO parameters
if(strstr(type.c_str(), "Plfo"))
type = "Plfo";

if(xml.enterbranch(type) == 0)
return;

@@ -227,7 +230,7 @@ void doPaste(MiddleWare &mw, string url, string type, XMLwrapper &xml, Ts&&... a
rtosc_message(buffer, 1024, path.c_str(), "b", sizeof(void*), &t);
if(!Master::ports.apropos(path.c_str()))
fprintf(stderr, "Warning: Missing Paste URL: '%s'\n", path.c_str());
printf("Sending info to '%s'\n", buffer);
//printf("Sending info to '%s'\n", buffer);
mw.transmitMsg(buffer);

//Let the pointer be reclaimed later
@@ -237,7 +240,7 @@ template<class T>
std::string doArrayCopy(MiddleWare &mw, int field, string url, string name)
{
XMLwrapper xml;
printf("Getting info from '%s'<%d>\n", url.c_str(), field);
//printf("Getting info from '%s'<%d>\n", url.c_str(), field);
mw.doReadOnlyOp([&xml, url, field, name, &mw](){
Master *m = mw.spawnMaster();
//Get the pointer
@@ -270,7 +273,7 @@ void doArrayPaste(MiddleWare &mw, int field, string url, string type,
rtosc_message(buffer, 1024, path.c_str(), "bi", sizeof(void*), &t, field);
if(!Master::ports.apropos(path.c_str()))
fprintf(stderr, "Warning: Missing Paste URL: '%s'\n", path.c_str());
printf("Sending info to '%s'<%d>\n", buffer, field);
//printf("Sending info to '%s'<%d>\n", buffer, field);
mw.transmitMsg(buffer);

//Let the pointer be reclaimed later
@@ -285,7 +288,7 @@ void doArrayPaste(MiddleWare &mw, int field, string url, string type,
*/
void doClassPaste(std::string type, std::string type_, MiddleWare &mw, string url, XMLwrapper &data)
{
printf("Class Paste\n");
//printf("Class Paste\n");
if(type == "EnvelopeParams")
doPaste<EnvelopeParams>(mw, url, type_, data);
else if(type == "LFOParams")
@@ -311,7 +314,7 @@ void doClassPaste(std::string type, std::string type_, MiddleWare &mw, string ur

std::string doClassCopy(std::string type, MiddleWare &mw, string url, string name)
{
printf("doClassCopy(%p)\n", mw.spawnMaster()->uToB);
//printf("doClassCopy(%p)\n", mw.spawnMaster()->uToB);
if(type == "EnvelopeParams")
return doCopy<EnvelopeParams>(mw, url, name);
else if(type == "LFOParams")
@@ -361,14 +364,14 @@ std::string getUrlPresetType(std::string url, MiddleWare &mw)
//Get the pointer
result = capture<std::string>(m, url+"preset-type");
});
printf("preset type = %s\n", result.c_str());
//printf("preset type = %s\n", result.c_str());
return result;
}

std::string getUrlType(std::string url)
{
assert(!url.empty());
printf("Searching for '%s'\n", (url+"self").c_str());
//printf("Searching for '%s'\n", (url+"self").c_str());
auto self = Master::ports.apropos((url+"self").c_str());
if(!self)
fprintf(stderr, "Warning: URL Metadata Not Found For '%s'\n", url.c_str());
@@ -409,12 +412,12 @@ void presetCopy(MiddleWare &mw, std::string url, std::string name)
{
(void) name;
doClassCopy(getUrlType(url), mw, url, name);
printf("PresetCopy()\n");
//printf("PresetCopy()\n");
}
void presetPaste(MiddleWare &mw, std::string url, std::string name)
{
(void) name;
printf("PresetPaste()\n");
//printf("PresetPaste()\n");
string data = "";
XMLwrapper xml;
if(name.empty()) {
@@ -433,13 +436,13 @@ void presetPaste(MiddleWare &mw, std::string url, std::string name)
void presetCopyArray(MiddleWare &mw, std::string url, int field, std::string name)
{
(void) name;
printf("PresetArrayCopy()\n");
//printf("PresetArrayCopy()\n");
doClassArrayCopy(getUrlType(url), field, mw, url, name);
}
void presetPasteArray(MiddleWare &mw, std::string url, int field, std::string name)
{
(void) name;
printf("PresetArrayPaste()\n");
//printf("PresetArrayPaste()\n");
string data = "";
XMLwrapper xml;
if(name.empty()) {
@@ -452,7 +455,7 @@ void presetPasteArray(MiddleWare &mw, std::string url, int field, std::string na
if(xml.loadXMLfile(name))
return;
}
printf("Performing Paste...\n");
//printf("Performing Paste...\n");
doClassArrayPaste(getUrlType(url), getUrlPresetType(url, mw), field, mw, url, xml);
}
#if 0
@@ -464,19 +467,19 @@ void presetPaste(std::string url, int)
#endif
void presetDelete(int)
{
printf("PresetDelete()\n");
printf("PresetDelete()<UNIMPLEMENTED>\n");
}
void presetRescan()
{
printf("PresetRescan()\n");
printf("PresetRescan()<UNIMPLEMENTED>\n");
}
std::string presetClipboardType()
{
printf("PresetClipboardType()\n");
printf("PresetClipboardType()<UNIMPLEMENTED>\n");
return "dummy";
}
bool presetCheckClipboardType()
{
printf("PresetCheckClipboardType()\n");
printf("PresetCheckClipboardType()<UNIMPLEMENTED>\n");
return true;
}

+ 13
- 0
source/native-plugins/zynaddsubfx/Params/ADnoteParameters.cpp View File

@@ -111,6 +111,8 @@ static const Ports voicePorts = {
rToggle(PFilterEnabled, "Filter Enable"),
rToggle(PFilterEnvelopeEnabled, "Filter Envelope Enable"),
rToggle(PFilterLfoEnabled, "Filter LFO Enable"),
rParamZyn(PFilterVelocityScale, "Filter Velocity Magnitude"),
rParamZyn(PFilterVelocityScaleFunction, "Filter Velocity Function Shape"),


//Modulator Stuff
@@ -428,6 +430,8 @@ void ADnoteVoiceParam::defaults()
PFilterEnabled = 0;
PFilterEnvelopeEnabled = 0;
PFilterLfoEnabled = 0;
PFilterVelocityScale = 0;
PFilterVelocityScaleFunction = 64;
PFMEnabled = 0;

//I use the internal oscillator (-1)
@@ -664,6 +668,8 @@ void ADnoteVoiceParam::add2XML(XMLwrapper *xml, bool fmoscilused)

if((PFilterEnabled != 0) || (!xml->minimal)) {
xml->beginbranch("FILTER_PARAMETERS");
xml->addpar("velocity_sensing_amplitude", PFilterVelocityScale);
xml->addpar("velocity_sensing", PFilterVelocityScaleFunction);
xml->beginbranch("FILTER");
VoiceFilter->add2XML(xml);
xml->endbranch();
@@ -974,6 +980,8 @@ void ADnoteVoiceParam::paste(ADnoteVoiceParam &a)
RCopy(FilterEnvelope);

copy(PFilterLfoEnabled);
copy(PFilterVelocityScale);
copy(PFilterVelocityScaleFunction);

RCopy(FilterLfo);

@@ -1115,6 +1123,11 @@ void ADnoteVoiceParam::getfromXML(XMLwrapper *xml, unsigned nvoice)
}

if(xml->enterbranch("FILTER_PARAMETERS")) {
PFilterVelocityScale = xml->getpar127("velocity_sensing_amplitude",
PFilterVelocityScale);
PFilterVelocityScaleFunction = xml->getpar127(
"velocity_sensing",
PFilterVelocityScaleFunction);
if(xml->enterbranch("FILTER")) {
VoiceFilter->getfromXML(xml);
xml->exitbranch();


+ 7
- 1
source/native-plugins/zynaddsubfx/Params/ADnoteParameters.h View File

@@ -240,10 +240,16 @@ struct ADnoteVoiceParam {
unsigned char PFilterEnvelopeEnabled;
EnvelopeParams *FilterEnvelope;

/* LFO Envelope */
/* Filter LFO */
unsigned char PFilterLfoEnabled;
LFOParams *FilterLfo;

// filter velocity sensing
unsigned char PFilterVelocityScale;

// filter velocity sensing
unsigned char PFilterVelocityScaleFunction;

/****************************
* MODULLATOR PARAMETERS *
****************************/


+ 11
- 2
source/native-plugins/zynaddsubfx/Synth/ADnote.cpp View File

@@ -317,7 +317,11 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars)
NoteVoicePar[nvoice].FilterLfo = NULL;

NoteVoicePar[nvoice].FilterCenterPitch =
pars.VoicePar[nvoice].VoiceFilter->getfreq();
pars.VoicePar[nvoice].VoiceFilter->getfreq()
+ pars.VoicePar[nvoice].PFilterVelocityScale
/ 127.0f * 6.0f //velocity sensing
* (VelF(velocity,
pars.VoicePar[nvoice].PFilterVelocityScaleFunction) - 1);
NoteVoicePar[nvoice].filterbypass =
pars.VoicePar[nvoice].Pfilterbypass;

@@ -511,7 +515,12 @@ void ADnote::legatonote(LegatoParams lpars)


NoteVoicePar[nvoice].FilterCenterPitch =
pars.VoicePar[nvoice].VoiceFilter->getfreq();
pars.VoicePar[nvoice].VoiceFilter->getfreq()
+ pars.VoicePar[nvoice].PFilterVelocityScale
/ 127.0f * 6.0f //velocity sensing
* (VelF(velocity,
pars.VoicePar[nvoice].PFilterVelocityScaleFunction) - 1);

NoteVoicePar[nvoice].filterbypass =
pars.VoicePar[nvoice].Pfilterbypass;



+ 1
- 1
source/native-plugins/zynaddsubfx/UI/ADnoteUI.fl View File

@@ -764,7 +764,7 @@ o->redraw();}
Fl_Group {} {
label {ADsynth Voice - Filter} open
xywh {250 30 275 75} box FLAT_BOX color 50 align 144
code0 {o->init("", osc_i, loc, "VoiceFilter/");}
code0 {o->init(loc + "PFilter", osc_i, loc, "VoiceFilter/");}
class FilterUI
} {}
Fl_Group voicefilterenvgroup {


+ 8
- 2
source/native-plugins/zynaddsubfx/UI/Fl_Osc_Dial.cpp View File

@@ -111,6 +111,8 @@ void Fl_Osc_Dial::mark_dead(void)
dead = true;
}

#define VEL_PFX "VelocityScale"

void Fl_Osc_Dial::rebase(std::string new_base)
{
if(dead || loc == "/")
@@ -141,8 +143,12 @@ void Fl_Osc_Dial::rebase(std::string new_base)
}

std::string new_loc = new_base.substr(0, match_pos+1);
printf("Moving '%s' to\n", (loc+ext).c_str());
printf(" '%s'\n", (new_loc+ext).c_str());
if (!strncmp(ext.c_str(), VEL_PFX, sizeof(VEL_PFX)-1) &&
strstr(loc.c_str(), "/VoicePar"))
new_loc = new_loc + "PFilter";
// printf("Moving '%s' to\n", (loc+ext).c_str());
// printf(" '%s'\n", (new_loc+ext).c_str());
// printf("Ext: %s\n", ext.c_str());
oscMove(loc+ext, new_loc+ext);
loc = new_loc;
}

Loading…
Cancel
Save