Browse Source

Update to latest juce; Fix crash when reopening lv2 uis

Closes #13
tags/2018-04-16
falkTX 9 years ago
parent
commit
92a617a89a
100 changed files with 8871 additions and 2439 deletions
  1. +1
    -1
      libs/juce/build-juce/AppConfig.h
  2. +7
    -7
      libs/juce/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h
  3. +6
    -2
      libs/juce/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
  4. +1
    -1
      libs/juce/source/modules/juce_audio_basics/effects/juce_FFT.cpp
  5. +97
    -0
      libs/juce/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h
  6. +1
    -49
      libs/juce/source/modules/juce_audio_basics/effects/juce_Reverb.h
  7. +11
    -4
      libs/juce/source/modules/juce_audio_basics/juce_audio_basics.cpp
  8. +11
    -0
      libs/juce/source/modules/juce_audio_basics/juce_audio_basics.h
  9. +4
    -3
      libs/juce/source/modules/juce_audio_basics/juce_module_info
  10. +2
    -2
      libs/juce/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp
  11. +2
    -2
      libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
  12. +374
    -0
      libs/juce/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp
  13. +152
    -0
      libs/juce/source/modules/juce_audio_basics/midi/juce_MidiRPN.h
  14. +2150
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp
  15. +414
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h
  16. +198
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp
  17. +96
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h
  18. +132
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp
  19. +180
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPENote.h
  20. +356
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp
  21. +313
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h
  22. +161
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp
  23. +194
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h
  24. +53
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp
  25. +191
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h
  26. +170
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp
  27. +96
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEValue.h
  28. +302
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp
  29. +132
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZone.h
  30. +346
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp
  31. +129
    -0
      libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h
  32. +1
    -1
      libs/juce/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h
  33. +6
    -2
      libs/juce/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
  34. +1
    -1
      libs/juce/source/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h
  35. +106
    -200
      libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  36. +1
    -5
      libs/juce/source/modules/juce_audio_devices/juce_audio_devices.cpp
  37. +1
    -1
      libs/juce/source/modules/juce_audio_devices/juce_module_info
  38. +1
    -2
      libs/juce/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h
  39. +10
    -0
      libs/juce/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp
  40. +4
    -3
      libs/juce/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h
  41. +1
    -1
      libs/juce/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h
  42. +5
    -3
      libs/juce/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  43. +2
    -2
      libs/juce/source/modules/juce_audio_devices/native/juce_ios_Audio.cpp
  44. +1
    -1
      libs/juce/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp
  45. +4
    -4
      libs/juce/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
  46. +1
    -1
      libs/juce/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp
  47. +1
    -1
      libs/juce/source/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm
  48. +0
    -17
      libs/juce/source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  49. +1
    -1
      libs/juce/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp
  50. +13
    -20
      libs/juce/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp
  51. +1
    -1
      libs/juce/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp
  52. +7
    -7
      libs/juce/source/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp
  53. +5
    -7
      libs/juce/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp
  54. +3
    -3
      libs/juce/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp
  55. +1
    -1
      libs/juce/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
  56. +4
    -4
      libs/juce/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
  57. +2
    -2
      libs/juce/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
  58. +4
    -1
      libs/juce/source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp
  59. +2
    -2
      libs/juce/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp
  60. +7
    -3
      libs/juce/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp
  61. +3
    -3
      libs/juce/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp
  62. +2
    -2
      libs/juce/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
  63. +1
    -1
      libs/juce/source/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp
  64. +2
    -2
      libs/juce/source/modules/juce_audio_formats/format/juce_AudioFormatManager.h
  65. +1
    -1
      libs/juce/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp
  66. +1
    -5
      libs/juce/source/modules/juce_audio_formats/juce_audio_formats.cpp
  67. +1
    -1
      libs/juce/source/modules/juce_audio_formats/juce_module_info
  68. +2
    -2
      libs/juce/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp
  69. +400
    -114
      libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp
  70. +3
    -3
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUBase.cpp
  71. +12
    -7
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUBase.h
  72. +2
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUBuffer.h
  73. +1
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUCarbonViewBase.h
  74. +1
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUCarbonViewControl.h
  75. +0
    -465
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUEffectBase.cpp
  76. +0
    -377
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUEffectBase.h
  77. +0
    -164
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUMIDIEffectBase.cpp
  78. +0
    -104
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUMIDIEffectBase.h
  79. +2
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUOutputElement.cpp
  80. +2
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.cpp
  81. +2
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/CAAUParameter.cpp
  82. +2
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/CADebugMacros.h
  83. +2
    -0
      libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/CarbonEventHandler.cpp
  84. +989
    -374
      libs/juce/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
  85. +1
    -4
      libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode1.cpp
  86. +1
    -4
      libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode2.cpp
  87. +1
    -4
      libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode3.cpp
  88. +2
    -0
      libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode_Header.h
  89. +1
    -4
      libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_MacUtilities.mm
  90. +1
    -4
      libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_WinUtilities.cpp
  91. +12
    -12
      libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp
  92. +6
    -6
      libs/juce/source/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h
  93. +304
    -178
      libs/juce/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
  94. +1
    -4
      libs/juce/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.mm
  95. +246
    -202
      libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp
  96. +1
    -3
      libs/juce/source/modules/juce_audio_plugin_client/juce_module_info
  97. +0
    -12
      libs/juce/source/modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h
  98. +11
    -4
      libs/juce/source/modules/juce_audio_plugin_client/utility/juce_IncludeSystemHeaders.h
  99. +372
    -0
      libs/juce/source/modules/juce_audio_plugin_client/utility/juce_PluginBusUtilities.h
  100. +2
    -5
      libs/juce/source/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp

+ 1
- 1
libs/juce/build-juce/AppConfig.h View File

@@ -139,7 +139,7 @@
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3
*/
#define JUCE_PLUGINHOST_VST 1
#define JUCE_PLUGINHOST_VST 0
/** Config: JUCE_PLUGINHOST_VST3
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be


+ 7
- 7
libs/juce/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h View File

@@ -99,7 +99,7 @@ public:
class Int8
{
public:
inline Int8 (void* d) noexcept : data (static_cast <int8*> (d)) {}
inline Int8 (void* d) noexcept : data (static_cast<int8*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
@@ -124,7 +124,7 @@ public:
class UInt8
{
public:
inline UInt8 (void* d) noexcept : data (static_cast <uint8*> (d)) {}
inline UInt8 (void* d) noexcept : data (static_cast<uint8*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
@@ -149,7 +149,7 @@ public:
class Int16
{
public:
inline Int16 (void* d) noexcept : data (static_cast <uint16*> (d)) {}
inline Int16 (void* d) noexcept : data (static_cast<uint16*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
@@ -174,7 +174,7 @@ public:
class Int24
{
public:
inline Int24 (void* d) noexcept : data (static_cast <char*> (d)) {}
inline Int24 (void* d) noexcept : data (static_cast<char*> (d)) {}
inline void advance() noexcept { data += 3; }
inline void skip (int numSamples) noexcept { data += 3 * numSamples; }
@@ -199,7 +199,7 @@ public:
class Int32
{
public:
inline Int32 (void* d) noexcept : data (static_cast <uint32*> (d)) {}
inline Int32 (void* d) noexcept : data (static_cast<uint32*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
@@ -245,7 +245,7 @@ public:
class Float32
{
public:
inline Float32 (void* d) noexcept : data (static_cast <float*> (d)) {}
inline Float32 (void* d) noexcept : data (static_cast<float*> (d)) {}
inline void advance() noexcept { ++data; }
inline void skip (int numSamples) noexcept { data += numSamples; }
@@ -318,7 +318,7 @@ public:
{
public:
typedef const void VoidType;
static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast <void*> (v); }
static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast<void*> (v); }
enum { isConst = 1 };
};
#endif


+ 6
- 2
libs/juce/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp View File

@@ -815,11 +815,13 @@ void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcep
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num);
#else
union {float f; uint32 i;} signMask;
union { float f; uint32 i; } signMask;
signMask.i = 0x7fffffffUL;
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType mask = Mode::load1 (signMask.f);)
ignoreUnused (signMask);
#endif
}
@@ -834,6 +836,8 @@ void FloatVectorOperations::abs (double* dest, const double* src, int num) noexc
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabs (src[i]), Mode::bit_and (s, mask),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
const Mode::ParallelType mask = Mode::load1 (signMask.d);)
ignoreUnused (signMask);
#endif
}
@@ -1001,7 +1005,7 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab
if (FloatVectorHelpers::isSSE2Available())
_MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF);
#endif
(void) shouldEnable;
ignoreUnused (shouldEnable);
}
//==============================================================================


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

@@ -248,7 +248,7 @@ void FFT::performRealOnlyInverseTransform (float* d) const noexcept
if (scratchSize < maxFFTScratchSpaceToAlloca)
{
performRealOnlyForwardTransform (static_cast<Complex*> (alloca (scratchSize)), d);
performRealOnlyInverseTransform (static_cast<Complex*> (alloca (scratchSize)), d);
}
else
{


+ 97
- 0
libs/juce/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h View File

@@ -0,0 +1,97 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_LINEARSMOOTHEDVALUE_H_INCLUDED
#define JUCE_LINEARSMOOTHEDVALUE_H_INCLUDED
//==============================================================================
/**
Utility class for linearly smoothed values like volume etc. that should
not change abruptly but as a linear ramp, to avoid audio glitches.
*/
//==============================================================================
template<typename FloatType>
class JUCE_API LinearSmoothedValue
{
public:
/** Constructor. */
LinearSmoothedValue() noexcept
: currentValue (0), target (0), step (0), countdown (0), stepsToTarget (0)
{
}
/** Constructor. */
LinearSmoothedValue (FloatType initialValue) noexcept
: currentValue (initialValue), target (initialValue), step (0), countdown (0), stepsToTarget (0)
{
}
//==========================================================================
/** Reset to a new sample rate and ramp length. */
void reset (double sampleRate, double rampLengthInSeconds) noexcept
{
jassert (sampleRate > 0 && rampLengthInSeconds >= 0);
stepsToTarget = (int) std::floor (rampLengthInSeconds * sampleRate);
currentValue = target;
countdown = 0;
}
//==========================================================================
/** Set a new target value. */
void setValue (FloatType newValue) noexcept
{
if (target != newValue)
{
target = newValue;
countdown = stepsToTarget;
if (countdown <= 0)
currentValue = target;
else
step = (target - currentValue) / (FloatType) countdown;
}
}
//==========================================================================
/** Compute the next value. */
FloatType getNextValue() noexcept
{
if (countdown <= 0)
return target;
--countdown;
currentValue += step;
return currentValue;
}
private:
//==========================================================================
FloatType currentValue, target, step;
int countdown, stepsToTarget;
};
#endif // JUCE_LINEARSMOOTHEDVALUE_H_INCLUDED

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

@@ -306,54 +306,6 @@ private:
JUCE_DECLARE_NON_COPYABLE (AllPassFilter)
};
//==============================================================================
class LinearSmoothedValue
{
public:
LinearSmoothedValue() noexcept
: currentValue (0), target (0), step (0), countdown (0), stepsToTarget (0)
{
}
void reset (double sampleRate, double fadeLengthSeconds) noexcept
{
jassert (sampleRate > 0 && fadeLengthSeconds >= 0);
stepsToTarget = (int) std::floor (fadeLengthSeconds * sampleRate);
currentValue = target;
countdown = 0;
}
void setValue (float newValue) noexcept
{
if (target != newValue)
{
target = newValue;
countdown = stepsToTarget;
if (countdown <= 0)
currentValue = target;
else
step = (target - currentValue) / (float) countdown;
}
}
float getNextValue() noexcept
{
if (countdown <= 0)
return target;
--countdown;
currentValue += step;
return currentValue;
}
private:
float currentValue, target, step;
int countdown, stepsToTarget;
JUCE_DECLARE_NON_COPYABLE (LinearSmoothedValue)
};
//==============================================================================
enum { numCombs = 8, numAllPasses = 4, numChannels = 2 };
@@ -363,7 +315,7 @@ private:
CombFilter comb [numChannels][numCombs];
AllPassFilter allPass [numChannels][numAllPasses];
LinearSmoothedValue damping, feedback, dryGain, wetGain1, wetGain2;
LinearSmoothedValue<float> damping, feedback, dryGain, wetGain1, wetGain2;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb)
};


+ 11
- 4
libs/juce/source/modules/juce_audio_basics/juce_audio_basics.cpp View File

@@ -22,7 +22,7 @@
==============================================================================
*/
#if defined (JUCE_AUDIO_BASICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE
#ifdef JUCE_AUDIO_BASICS_H_INCLUDED
/* When you add this cpp file to your project, you mustn't include it in a file where you've
already included any other headers - just put it inside a file on its own, possibly with your config
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
@@ -31,9 +31,6 @@
#error "Incorrect use of JUCE cpp file"
#endif
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "juce_audio_basics.h"
#if JUCE_MINGW && ! defined (__SSE2__)
@@ -90,6 +87,16 @@ namespace juce
#include "midi/juce_MidiKeyboardState.cpp"
#include "midi/juce_MidiMessage.cpp"
#include "midi/juce_MidiMessageSequence.cpp"
#include "midi/juce_MidiRPN.cpp"
#include "mpe/juce_MPEValue.cpp"
#include "mpe/juce_MPENote.cpp"
#include "mpe/juce_MPEZone.cpp"
#include "mpe/juce_MPEZoneLayout.cpp"
#include "mpe/juce_MPEInstrument.cpp"
#include "mpe/juce_MPEMessages.cpp"
#include "mpe/juce_MPESynthesiserBase.cpp"
#include "mpe/juce_MPESynthesiserVoice.cpp"
#include "mpe/juce_MPESynthesiser.cpp"
#include "sources/juce_BufferingAudioSource.cpp"
#include "sources/juce_ChannelRemappingAudioSource.cpp"
#include "sources/juce_IIRFilterAudioSource.cpp"


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

@@ -42,12 +42,23 @@ namespace juce
#include "effects/juce_IIRFilterOld.h"
#include "effects/juce_LagrangeInterpolator.h"
#include "effects/juce_FFT.h"
#include "effects/juce_LinearSmoothedValue.h"
#include "effects/juce_Reverb.h"
#include "midi/juce_MidiMessage.h"
#include "midi/juce_MidiBuffer.h"
#include "midi/juce_MidiMessageSequence.h"
#include "midi/juce_MidiFile.h"
#include "midi/juce_MidiKeyboardState.h"
#include "midi/juce_MidiRPN.h"
#include "mpe/juce_MPEValue.h"
#include "mpe/juce_MPENote.h"
#include "mpe/juce_MPEZone.h"
#include "mpe/juce_MPEZoneLayout.h"
#include "mpe/juce_MPEInstrument.h"
#include "mpe/juce_MPEMessages.h"
#include "mpe/juce_MPESynthesiserBase.h"
#include "mpe/juce_MPESynthesiserVoice.h"
#include "mpe/juce_MPESynthesiser.h"
#include "sources/juce_AudioSource.h"
#include "sources/juce_PositionableAudioSource.h"
#include "sources/juce_BufferingAudioSource.h"


+ 4
- 3
libs/juce/source/modules/juce_audio_basics/juce_module_info View File

@@ -1,12 +1,12 @@
{
"id": "juce_audio_basics",
"name": "JUCE audio and midi data classes",
"version": "4.0.2",
"version": "4.1.0",
"description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
"dependencies": [ { "id": "juce_core", "version": "matching" } ],
"dependencies": [ { "id": "juce_core", "version": "matching" }],
"include": "juce_audio_basics.h",
@@ -14,8 +14,9 @@
{ "file": "juce_audio_basics.mm", "target": "xcode" } ],
"browse": [ "buffers/*",
"midi/*",
"effects/*",
"midi/*",
"mpe/*",
"sources/*",
"synthesisers/*" ],


+ 2
- 2
libs/juce/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp View File

@@ -254,12 +254,12 @@ bool MidiFile::readFrom (InputStream& sourceStream)
if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize))
{
size_t size = data.getSize();
const uint8* d = static_cast <const uint8*> (data.getData());
const uint8* d = static_cast<const uint8*> (data.getData());
short fileType, expectedTracks;
if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks))
{
size -= (size_t) (d - static_cast <const uint8*> (data.getData()));
size -= (size_t) (d - static_cast<const uint8*> (data.getData()));
int track = 0;


+ 2
- 2
libs/juce/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp View File

@@ -672,7 +672,7 @@ bool MidiMessage::isTextMetaEvent() const noexcept
String MidiMessage::getTextFromTextMetaEvent() const
{
const char* const textData = reinterpret_cast <const char*> (getMetaEventData());
const char* const textData = reinterpret_cast<const char*> (getMetaEventData());
return String (CharPointer_UTF8 (textData),
CharPointer_UTF8 (textData + getMetaEventLength()));
}
@@ -982,7 +982,7 @@ String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctav
return s;
}
return String::empty;
return String();
}
double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept


+ 374
- 0
libs/juce/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp View File

@@ -0,0 +1,374 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
MidiRPNDetector::MidiRPNDetector() noexcept
{
}
MidiRPNDetector::~MidiRPNDetector() noexcept
{
}
bool MidiRPNDetector::parseControllerMessage (int midiChannel,
int controllerNumber,
int controllerValue,
MidiRPNMessage& result) noexcept
{
jassert (midiChannel >= 1 && midiChannel <= 16);
jassert (controllerNumber >= 0 && controllerNumber < 128);
jassert (controllerValue >= 0 && controllerValue < 128);
return states[midiChannel - 1].handleController (midiChannel, controllerNumber, controllerValue, result);
}
void MidiRPNDetector::reset() noexcept
{
for (int i = 0; i < 16; ++i)
{
states[i].parameterMSB = 0xff;
states[i].parameterLSB = 0xff;
states[i].resetValue();
states[i].isNRPN = false;
}
}
//==============================================================================
MidiRPNDetector::ChannelState::ChannelState () noexcept
: parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false)
{
}
bool MidiRPNDetector::ChannelState::handleController (int channel,
int controllerNumber,
int value,
MidiRPNMessage& result) noexcept
{
switch (controllerNumber)
{
case 0x62: parameterLSB = uint8 (value); resetValue(); isNRPN = true; break;
case 0x63: parameterMSB = uint8 (value); resetValue(); isNRPN = true; break;
case 0x64: parameterLSB = uint8 (value); resetValue(); isNRPN = false; break;
case 0x65: parameterMSB = uint8 (value); resetValue(); isNRPN = false; break;
case 0x06: valueMSB = uint8 (value); return sendIfReady (channel, result);
case 0x26: valueLSB = uint8 (value); break;
default: break;
}
return false;
}
void MidiRPNDetector::ChannelState::resetValue() noexcept
{
valueMSB = 0xff;
valueLSB = 0xff;
}
//==============================================================================
bool MidiRPNDetector::ChannelState::sendIfReady (int channel, MidiRPNMessage& result) noexcept
{
if (parameterMSB < 0x80 && parameterLSB < 0x80)
{
if (valueMSB < 0x80)
{
result.channel = channel;
result.parameterNumber = (parameterMSB << 7) + parameterLSB;
result.isNRPN = isNRPN;
if (valueLSB < 0x80)
{
result.value = (valueMSB << 7) + valueLSB;
result.is14BitValue = true;
}
else
{
result.value = valueMSB;
result.is14BitValue = false;
}
return true;
}
}
return false;
}
//==============================================================================
MidiBuffer MidiRPNGenerator::generate (MidiRPNMessage message)
{
return generate (message.channel,
message.parameterNumber,
message.value,
message.isNRPN,
message.is14BitValue);
}
MidiBuffer MidiRPNGenerator::generate (int midiChannel,
int parameterNumber,
int value,
bool isNRPN,
bool use14BitValue)
{
jassert (midiChannel > 0 && midiChannel <= 16);
jassert (parameterNumber >= 0 && parameterNumber < 16384);
jassert (value >= 0 && value < (use14BitValue ? 16384 : 128));
uint8 parameterLSB = uint8 (parameterNumber & 0x0000007f);
uint8 parameterMSB = uint8 (parameterNumber >> 7);
uint8 valueLSB = use14BitValue ? uint8 (value & 0x0000007f) : 0x00;
uint8 valueMSB = use14BitValue ? uint8 (value >> 7) : uint8 (value);
uint8 channelByte = uint8 (0xb0 + midiChannel - 1);
MidiBuffer buffer;
buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x62 : 0x64, parameterLSB), 0);
buffer.addEvent (MidiMessage (channelByte, isNRPN ? 0x63 : 0x65, parameterMSB), 0);
// sending the value LSB is optional, but must come before sending the value MSB:
if (use14BitValue)
buffer.addEvent (MidiMessage (channelByte, 0x26, valueLSB), 0);
buffer.addEvent (MidiMessage (channelByte, 0x06, valueMSB), 0);
return buffer;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class MidiRPNDetectorTests : public UnitTest
{
public:
MidiRPNDetectorTests() : UnitTest ("MidiRPNDetector class") {}
void runTest() override
{
beginTest ("7-bit RPN");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (2, 101, 0, rpn));
expect (! detector.parseControllerMessage (2, 100, 7, rpn));
expect (detector.parseControllerMessage (2, 6, 42, rpn));
expectEquals (rpn.channel, 2);
expectEquals (rpn.parameterNumber, 7);
expectEquals (rpn.value, 42);
expect (! rpn.isNRPN);
expect (! rpn.is14BitValue);
}
beginTest ("14-bit RPN");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (1, 100, 44, rpn));
expect (! detector.parseControllerMessage (1, 101, 2, rpn));
expect (! detector.parseControllerMessage (1, 38, 94, rpn));
expect (detector.parseControllerMessage (1, 6, 1, rpn));
expectEquals (rpn.channel, 1);
expectEquals (rpn.parameterNumber, 300);
expectEquals (rpn.value, 222);
expect (! rpn.isNRPN);
expect (rpn.is14BitValue);
}
beginTest ("RPNs on multiple channels simultaneously");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (1, 100, 44, rpn));
expect (! detector.parseControllerMessage (2, 101, 0, rpn));
expect (! detector.parseControllerMessage (1, 101, 2, rpn));
expect (! detector.parseControllerMessage (2, 100, 7, rpn));
expect (! detector.parseControllerMessage (1, 38, 94, rpn));
expect (detector.parseControllerMessage (2, 6, 42, rpn));
expectEquals (rpn.channel, 2);
expectEquals (rpn.parameterNumber, 7);
expectEquals (rpn.value, 42);
expect (! rpn.isNRPN);
expect (! rpn.is14BitValue);
expect (detector.parseControllerMessage (1, 6, 1, rpn));
expectEquals (rpn.channel, 1);
expectEquals (rpn.parameterNumber, 300);
expectEquals (rpn.value, 222);
expect (! rpn.isNRPN);
expect (rpn.is14BitValue);
}
beginTest ("14-bit RPN with value within 7-bit range");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
expect (! detector.parseControllerMessage (16, 101, 0, rpn));
expect (! detector.parseControllerMessage (16, 38, 3, rpn));
expect (detector.parseControllerMessage (16, 6, 0, rpn));
expectEquals (rpn.channel, 16);
expectEquals (rpn.parameterNumber, 0);
expectEquals (rpn.value, 3);
expect (! rpn.isNRPN);
expect (rpn.is14BitValue);
}
beginTest ("invalid RPN (wrong order)");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (2, 6, 42, rpn));
expect (! detector.parseControllerMessage (2, 101, 0, rpn));
expect (! detector.parseControllerMessage (2, 100, 7, rpn));
}
beginTest ("14-bit RPN interspersed with unrelated CC messages");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (16, 3, 80, rpn));
expect (! detector.parseControllerMessage (16, 100, 0 , rpn));
expect (! detector.parseControllerMessage (16, 4, 81, rpn));
expect (! detector.parseControllerMessage (16, 101, 0, rpn));
expect (! detector.parseControllerMessage (16, 5, 82, rpn));
expect (! detector.parseControllerMessage (16, 5, 83, rpn));
expect (! detector.parseControllerMessage (16, 38, 3, rpn));
expect (! detector.parseControllerMessage (16, 4, 84, rpn));
expect (! detector.parseControllerMessage (16, 3, 85, rpn));
expect (detector.parseControllerMessage (16, 6, 0, rpn));
expectEquals (rpn.channel, 16);
expectEquals (rpn.parameterNumber, 0);
expectEquals (rpn.value, 3);
expect (! rpn.isNRPN);
expect (rpn.is14BitValue);
}
beginTest ("14-bit NRPN");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (1, 98, 44, rpn));
expect (! detector.parseControllerMessage (1, 99 , 2, rpn));
expect (! detector.parseControllerMessage (1, 38, 94, rpn));
expect (detector.parseControllerMessage (1, 6, 1, rpn));
expectEquals (rpn.channel, 1);
expectEquals (rpn.parameterNumber, 300);
expectEquals (rpn.value, 222);
expect (rpn.isNRPN);
expect (rpn.is14BitValue);
}
beginTest ("reset");
{
MidiRPNDetector detector;
MidiRPNMessage rpn;
expect (! detector.parseControllerMessage (2, 101, 0, rpn));
detector.reset();
expect (! detector.parseControllerMessage (2, 100, 7, rpn));
expect (! detector.parseControllerMessage (2, 6, 42, rpn));
}
}
};
static MidiRPNDetectorTests MidiRPNDetectorUnitTests;
//==============================================================================
class MidiRPNGeneratorTests : public UnitTest
{
public:
MidiRPNGeneratorTests() : UnitTest ("MidiRPNGenerator class") {}
void runTest() override
{
beginTest ("generating RPN/NRPN");
{
{
MidiBuffer buffer = MidiRPNGenerator::generate (1, 23, 1337, true, true);
expectContainsRPN (buffer, 1, 23, 1337, true, true);
}
{
MidiBuffer buffer = MidiRPNGenerator::generate (16, 101, 34, false, false);
expectContainsRPN (buffer, 16, 101, 34, false, false);
}
{
MidiRPNMessage message = { 16, 101, 34, false, false };
MidiBuffer buffer = MidiRPNGenerator::generate (message);
expectContainsRPN (buffer, message);
}
}
}
private:
//==========================================================================
void expectContainsRPN (const MidiBuffer& midiBuffer,
int channel,
int parameterNumber,
int value,
bool isNRPN,
bool is14BitValue)
{
MidiRPNMessage expected = { channel, parameterNumber, value, isNRPN, is14BitValue };
expectContainsRPN (midiBuffer, expected);
}
//==========================================================================
void expectContainsRPN (const MidiBuffer& midiBuffer, MidiRPNMessage expected)
{
MidiBuffer::Iterator iter (midiBuffer);
MidiMessage midiMessage;
MidiRPNMessage result = MidiRPNMessage();
MidiRPNDetector detector;
int samplePosition; // not actually used, so no need to initialise.
while (iter.getNextEvent (midiMessage, samplePosition))
{
if (detector.parseControllerMessage (midiMessage.getChannel(),
midiMessage.getControllerNumber(),
midiMessage.getControllerValue(),
result))
break;
}
expectEquals (result.channel, expected.channel);
expectEquals (result.parameterNumber, expected.parameterNumber);
expectEquals (result.value, expected.value);
expect (result.isNRPN == expected.isNRPN),
expect (result.is14BitValue == expected.is14BitValue);
}
};
static MidiRPNGeneratorTests MidiRPNGeneratorUnitTests;
#endif // JUCE_UNIT_TESTS

+ 152
- 0
libs/juce/source/modules/juce_audio_basics/midi/juce_MidiRPN.h View File

@@ -0,0 +1,152 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MIDIRPNDETECTOR_H_INCLUDED
#define JUCE_MIDIRPNDETECTOR_H_INCLUDED
//==========================================================================
/** Represents a MIDI RPN (registered parameter number) or NRPN (non-registered
parameter number) message.
*/
struct MidiRPNMessage
{
/** Midi channel of the message, in the range 1 to 16. */
int channel;
/** The 14-bit parameter index, in the range 0 to 16383 (0x3fff). */
int parameterNumber;
/** The parameter value, in the range 0 to 16383 (0x3fff).
If the message contains no value LSB, the value will be in the range
0 to 127 (0x7f).
*/
int value;
/** True if this message is an NRPN; false if it is an RPN. */
bool isNRPN;
/** True if the value uses 14-bit resolution (LSB + MSB); false if
the value is 7-bit (MSB only).
*/
bool is14BitValue;
};
//==============================================================================
/**
Parses a stream of MIDI data to assemble RPN and NRPN messages from their
constituent MIDI CC messages.
The detector uses the following parsing rules: the parameter number
LSB/MSB can be sent/received in either order and must both come before the
parameter value; for the parameter value, LSB always has to be sent/received
before the value MSB, otherwise it will be treated as 7-bit (MSB only).
*/
class JUCE_API MidiRPNDetector
{
public:
/** Constructor. */
MidiRPNDetector() noexcept;
/** Destructor. */
~MidiRPNDetector() noexcept;
/** Resets the RPN detector's internal state, so that it forgets about
previously received MIDI CC messages.
*/
void reset() noexcept;
//==========================================================================
/** Takes the next in a stream of incoming MIDI CC messages and returns true
if it forms the last of a sequence that makes an RPN or NPRN.
If this returns true, then the RPNMessage object supplied will be
filled-out with the message's details.
(If it returns false then the RPNMessage object will be unchanged).
*/
bool parseControllerMessage (int midiChannel,
int controllerNumber,
int controllerValue,
MidiRPNMessage& result) noexcept;
private:
//==========================================================================
struct ChannelState
{
ChannelState() noexcept;
bool handleController (int channel, int controllerNumber,
int value, MidiRPNMessage&) noexcept;
void resetValue() noexcept;
bool sendIfReady (int channel, MidiRPNMessage&) noexcept;
uint8 parameterMSB, parameterLSB, valueMSB, valueLSB;
bool isNRPN;
};
//==========================================================================
ChannelState states[16];
JUCE_LEAK_DETECTOR (MidiRPNDetector)
};
//==============================================================================
/**
Generates an appropriate sequence of MIDI CC messages to represent an RPN
or NRPN message.
This sequence (as a MidiBuffer) can then be directly sent to a MidiOutput.
*/
class JUCE_API MidiRPNGenerator
{
public:
//==========================================================================
/** Generates a MIDI sequence representing the given RPN or NRPN message. */
static MidiBuffer generate (MidiRPNMessage message);
//==========================================================================
/** Generates a MIDI sequence representing an RPN or NRPN message with the
given parameters.
@param channel The MIDI channel of the RPN/NRPN message.
@param parameterNumber The parameter number, in the range 0 to 16383.
@param value The parameter value, in the range 0 to 16383, or
in the range 0 to 127 if sendAs14BitValue is false.
@param isNRPN Whether you need a MIDI RPN or NRPN sequence (RPN is default).
@param use14BitValue If true (default), the value will have 14-bit precision
(two MIDI bytes). If false, instead the value will have
7-bit presision (a single MIDI byte).
*/
static MidiBuffer generate (int channel,
int parameterNumber,
int value,
bool isNRPN = false,
bool use14BitValue = true);
};
#endif // JUCE_MIDIRPNDETECTOR_H_INCLUDED

+ 2150
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp
File diff suppressed because it is too large
View File


+ 414
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h View File

@@ -0,0 +1,414 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPEINSTRUMENT_H_INCLUDED
#define JUCE_MPEINSTRUMENT_H_INCLUDED
//==============================================================================
/*
This class represents an instrument handling MPE.
It has an MPE zone layout and maintans a state of currently
active (playing) notes and the values of their dimensions of expression.
You can trigger and modulate notes:
- by passing MIDI messages with the method processNextMidiEvent;
- by directly calling the methods noteOn, noteOff etc.
The class implements the channel and note management logic specified in
MPE. If you pass it a message, it will know what notes on what
channels (if any) should be affected by that message.
The class has a Listener class with the three callbacks MPENoteAdded,
MPENoteChanged, and MPENoteFinished. Implement such a
Listener class to react to note changes and trigger some functionality for
your application that depends on the MPE note state.
For example, you can use this class to write an MPE visualiser.
If you want to write a real-time audio synth with MPE functionality,
you should instead use the classes MPESynthesiserBase, which adds
the ability to render audio and to manage voices.
@see MPENote, MPEZoneLayout, MPESynthesiser
*/
class JUCE_API MPEInstrument
{
public:
/** Constructor.
This will construct an MPE instrument with initially no MPE zones.
In order to process incoming MIDI, call setZoneLayout, define the layout
via MIDI RPN messages, or set the instrument to legacy mode.
*/
MPEInstrument() noexcept;
/** Destructor. */
virtual ~MPEInstrument();
//==========================================================================
/** Returns the current zone layout of the instrument.
This happens by value, to enforce thread-safety and class invariants.
Note: If the instrument is in legacy mode, the return value of this
method is unspecified.
*/
MPEZoneLayout getZoneLayout() const noexcept;
/** Re-sets the zone layout of the instrument to the one passed in.
As a side effect, this will discard all currently playing notes,
and call noteReleased for all of them.
This will also disable legacy mode in case it was enabled previously.
*/
void setZoneLayout (MPEZoneLayout newLayout);
/** Returns true if the given MIDI channel (1-16) is a note channel in any
of the MPEInstrument's MPE zones; false otherwise.
When in legacy mode, this will return true if the given channel is
contained in the current legacy mode channel range; false otherwise.
*/
bool isNoteChannel (int midiChannel) const noexcept;
/** Returns true if the given MIDI channel (1-16) is a master channel in any
of the MPEInstrument's MPE zones; false otherwise.
When in legacy mode, this will always return false.
*/
bool isMasterChannel (int midiChannel) const noexcept;
//==========================================================================
/** The MPE note tracking mode. In case there is more than one note playing
simultaneously on the same MIDI channel, this determines which of these
notes will be modulated by an incoming MPE message on that channel
(pressure, pitchbend, or timbre).
The default is lastNotePlayedOnChannel.
*/
enum TrackingMode
{
lastNotePlayedOnChannel, //! The most recent note on the channel that is still played (key down and/or sustained)
lowestNoteOnChannel, //! The lowest note (by initialNote) on the channel with the note key still down
highestNoteOnChannel, //! The highest note (by initialNote) on the channel with the note key still down
allNotesOnChannel //! All notes on the channel (key down and/or sustained)
};
/** Set the MPE tracking mode for the pressure dimension. */
void setPressureTrackingMode (TrackingMode modeToUse);
/** Set the MPE tracking mode for the pitchbend dimension. */
void setPitchbendTrackingMode (TrackingMode modeToUse);
/** Set the MPE tracking mode for the timbre dimension. */
void setTimbreTrackingMode (TrackingMode modeToUse);
//==========================================================================
/** Process a MIDI message and trigger the appropriate method calls
(noteOn, noteOff etc.)
You can override this method if you need some special MIDI message
treatment on top of the standard MPE logic implemented here.
*/
virtual void processNextMidiEvent (const MidiMessage& message);
//==========================================================================
/** Request a note-on on the given channel, with the given initial note
number and velocity.
If the message arrives on a valid note channel, this will create a
new MPENote and call the noteAdded callback.
*/
virtual void noteOn (int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity);
/** Request a note-off. If there is a matching playing note, this will
release the note (except if it is sustained by a sustain or sostenuto
pedal) and call the noteReleased callback.
*/
virtual void noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity);
/** Request a pitchbend on the given channel with the given value (in units
of MIDI pitchwheel position).
Internally, this will determine whether the pitchwheel move is a
per-note pitchbend or a master pitchbend (depending on midiChannel),
take the correct per-note or master pitchbend range of the affected MPE
zone, and apply the resulting pitchbend to the affected note(s) (if any).
*/
virtual void pitchbend (int midiChannel, MPEValue pitchbend);
/** Request a pressure change on the given channel with the given value.
This will modify the pressure dimension of the note currently held down
on this channel (if any). If the channel is a zone master channel,
the pressure change will be broadcast to all notes in this zone.
*/
virtual void pressure (int midiChannel, MPEValue value);
/** Request a third dimension (timbre) change on the given channel with the
given value.
This will modify the timbre dimension of the note currently held down
on this channel (if any). If the channel is a zone master channel,
the timbre change will be broadcast to all notes in this zone.
*/
virtual void timbre (int midiChannel, MPEValue value);
/** Request a sustain pedal press or release. If midiChannel is a zone's
master channel, this will act on all notes in that zone; otherwise,
nothing will happen.
*/
virtual void sustainPedal (int midiChannel, bool isDown);
/** Request a sostenuto pedal press or release. If midiChannel is a zone's
master channel, this will act on all notes in that zone; otherwise,
nothing will happen.
*/
virtual void sostenutoPedal (int midiChannel, bool isDown);
/** Discard all currently playing notes.
This will also call the noteReleased listener callback for all of them.
*/
void releaseAllNotes();
//==========================================================================
/** Returns the number of MPE notes currently played by the
instrument.
*/
int getNumPlayingNotes() const noexcept;
/** Returns the note at the given index. If there is no such note, returns
an invalid MPENote. The notes are sorted such that the most recently
added note is the last element.
*/
MPENote getNote (int index) const noexcept;
/** Returns the note currently playing on the given midiChannel with the
specified initial MIDI note number, if there is such a note.
Otherwise, this returns an invalid MPENote
(check with note.isValid() before use!)
*/
MPENote getNote (int midiChannel, int midiNoteNumber) const noexcept;
/** Returns the most recent note that is playing on the given midiChannel
(this will be the note which has received the most recent note-on without
a corresponding note-off), if there is such a note.
Otherwise, this returns an invalid MPENote
(check with note.isValid() before use!)
*/
MPENote getMostRecentNote (int midiChannel) const noexcept;
/** Returns the most recent note that is not the note passed in.
If there is no such note, this returns an invalid MPENote
(check with note.isValid() before use!)
This helper method might be useful for some custom voice handling algorithms.
*/
MPENote getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept;
//==========================================================================
/** Derive from this class to be informed about any changes in the expressive
MIDI notes played by this instrument.
Note: This listener type receives its callbacks immediately, and not
via the message thread (so you might be for example in the MIDI thread).
Therefore you should never do heavy work such as graphics rendering etc.
inside those callbacks.
*/
class Listener
{
public:
/** Constructor. */
Listener();
/** Destructor. */
virtual ~Listener();
/** Implement this callback to be informed whenever a new expressive
MIDI note is triggered.
*/
virtual void noteAdded (MPENote newNote) = 0;
/** Implement this callback to be informed whenever a currently
playing MPE note's pressure value changes.
*/
virtual void notePressureChanged (MPENote changedNote) = 0;
/** Implement this callback to be informed whenever a currently
playing MPE note's pitchbend value changes.
Note: This can happen if the note itself is bent, if there is a
master channel pitchbend event, or if both occur simultaneously.
Call MPENote::getFrequencyInHertz to get the effective note frequency.
*/
virtual void notePitchbendChanged (MPENote changedNote) = 0;
/** Implement this callback to be informed whenever a currently
playing MPE note's timbre value changes.
*/
virtual void noteTimbreChanged (MPENote changedNote) = 0;
/** Implement this callback to be informed whether a currently playing
MPE note's key state (whether the key is down and/or the note is
sustained) has changed.
Note: if the key state changes to MPENote::off, noteReleased is
called instead.
*/
virtual void noteKeyStateChanged (MPENote changedNote) = 0;
/** Implement this callback to be informed whenever an MPE note
is released (either by a note-off message, or by a sustain/sostenuto
pedal release for a note that already received a note-off),
and should therefore stop playing.
*/
virtual void noteReleased (MPENote finishedNote) = 0;
};
//==========================================================================
/** Adds a listener. */
void addListener (Listener* const listenerToAdd) noexcept;
/** Removes a listener. */
void removeListener (Listener* const listenerToRemove) noexcept;
//==========================================================================
/** Puts the instrument into legacy mode.
As a side effect, this will discard all currently playing notes,
and call noteReleased for all of them.
This special zone layout mode is for backwards compatibility with
non-MPE MIDI devices. In this mode, the instrument will ignore the
current MPE zone layout. It will instead take a range of MIDI channels
(default: all channels 1-16) and treat them as note channels, with no
master channel. MIDI channels outside of this range will be ignored.
@param pitchbendRange The note pitchbend range in semitones to use when in legacy mode.
Must be between 0 and 96, otherwise behaviour is undefined.
The default pitchbend range in legacy mode is +/- 2 semitones.
@param channelRange The range of MIDI channels to use for notes when in legacy mode.
The default is to use all MIDI channels (1-16).
To get out of legacy mode, set a new MPE zone layout using setZoneLayout.
*/
void enableLegacyMode (int pitchbendRange = 2,
Range<int> channelRange = Range<int> (1, 17));
/** Returns true if the instrument is in legacy mode, false otherwise. */
bool isLegacyModeEnabled() const noexcept;
/** Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
Range<int> getLegacyModeChannelRange() const noexcept;
/** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
void setLegacyModeChannelRange (Range<int> channelRange);
/** Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
int getLegacyModePitchbendRange() const noexcept;
/** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
void setLegacyModePitchbendRange (int pitchbendRange);
protected:
//==========================================================================
/** This method defines what initial pitchbend value should be used for newly
triggered notes. The default is to use the last pitchbend value
that has been received on the same MIDI channel (or no pitchbend
if no pitchbend messages have been received so far).
Override this method if you need different behaviour.
*/
virtual MPEValue getInitialPitchbendForNoteOn (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOnVelocity) const;
/** This method defines what initial pressure value should be used for newly
triggered notes. The default is to re-use the note-on velocity value.
Override this method if you need different behaviour.
*/
virtual MPEValue getInitialPressureForNoteOn (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOnVelocity) const;
/** This method defines what initial timbre value should be used for newly
triggered notes. The default is to use the last timbre value that has
that has been received on the same MIDI channel (or a neutral centred value
if no pitchbend messages have been received so far).
Override this method if you need different behaviour.
*/
virtual MPEValue getInitialTimbreForNoteOn (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOnVelocity) const;
private:
//==========================================================================
CriticalSection lock;
Array<MPENote> notes;
MPEZoneLayout zoneLayout;
ListenerList<Listener> listeners;
uint8 lastPressureLowerBitReceivedOnChannel[16];
uint8 lastTimbreLowerBitReceivedOnChannel[16];
bool isNoteChannelSustained[16];
struct LegacyMode
{
bool isEnabled;
Range<int> channelRange;
int pitchbendRange;
};
struct MPEDimension
{
MPEDimension() noexcept : trackingMode (lastNotePlayedOnChannel) {}
TrackingMode trackingMode;
MPEValue lastValueReceivedOnChannel[16];
MPEValue MPENote::* value;
MPEValue& getValue (MPENote& note) noexcept { return note.*(value); }
};
LegacyMode legacyMode;
MPEDimension pitchbendDimension, pressureDimension, timbreDimension;
void updateDimension (int midiChannel, MPEDimension&, MPEValue);
void updateDimensionMaster (MPEZone&, MPEDimension&, MPEValue);
void updateDimensionForNote (MPENote&, MPEDimension&, MPEValue);
void callListenersDimensionChanged (MPENote&, MPEDimension&);
void processMidiNoteOnMessage (const MidiMessage&);
void processMidiNoteOffMessage (const MidiMessage&);
void processMidiPitchWheelMessage (const MidiMessage&);
void processMidiChannelPressureMessage (const MidiMessage&);
void processMidiControllerMessage (const MidiMessage&);
void processMidiAllNotesOffMessage (const MidiMessage&);
void handlePressureMSB (int midiChannel, int value) noexcept;
void handlePressureLSB (int midiChannel, int value) noexcept;
void handleTimbreMSB (int midiChannel, int value) noexcept;
void handleTimbreLSB (int midiChannel, int value) noexcept;
void handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto);
MPENote* getNotePtr (int midiChannel, int midiNoteNumber) const noexcept;
MPENote* getNotePtr (int midiChannel, TrackingMode) const noexcept;
MPENote* getLastNotePlayedPtr (int midiChannel) const noexcept;
MPENote* getHighestNotePtr (int midiChannel) const noexcept;
MPENote* getLowestNotePtr (int midiChannel) const noexcept;
void updateNoteTotalPitchbend (MPENote&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPEInstrument)
};
#endif // JUCE_MPE_H_INCLUDED

+ 198
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp View File

@@ -0,0 +1,198 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
MidiBuffer MPEMessages::addZone (MPEZone zone)
{
MidiBuffer buffer (MidiRPNGenerator::generate (zone.getFirstNoteChannel(),
zoneLayoutMessagesRpnNumber,
zone.getNumNoteChannels(),
false, false));
buffer.addEvents (perNotePitchbendRange (zone), 0, -1, 0);
buffer.addEvents (masterPitchbendRange (zone), 0, -1, 0);
return buffer;
}
MidiBuffer MPEMessages::perNotePitchbendRange (MPEZone zone)
{
return MidiRPNGenerator::generate (zone.getFirstNoteChannel(), 0,
zone.getPerNotePitchbendRange(),
false, false);
}
MidiBuffer MPEMessages::masterPitchbendRange (MPEZone zone)
{
return MidiRPNGenerator::generate (zone.getMasterChannel(), 0,
zone.getMasterPitchbendRange(),
false, false);
}
MidiBuffer MPEMessages::clearAllZones()
{
return MidiRPNGenerator::generate (1, zoneLayoutMessagesRpnNumber, 16, false, false);
}
MidiBuffer MPEMessages::setZoneLayout (const MPEZoneLayout& layout)
{
MidiBuffer buffer;
buffer.addEvents (clearAllZones(), 0, -1, 0);
for (int i = 0; i < layout.getNumZones(); ++i)
buffer.addEvents (addZone (*layout.getZoneByIndex (i)), 0, -1, 0);
return buffer;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class MPEMessagesTests : public UnitTest
{
public:
MPEMessagesTests() : UnitTest ("MPEMessages class") {}
void runTest() override
{
beginTest ("add zone");
{
{
MidiBuffer buffer = MPEMessages::addZone (MPEZone (1, 7));
const uint8 expectedBytes[] =
{
0xb1, 0x64, 0x06, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x07, // set up zone
0xb1, 0x64, 0x00, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x30, // per-note pbrange (default = 48)
0xb0, 0x64, 0x00, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x02 // master pbrange (default = 2)
};
testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes));
}
{
MidiBuffer buffer = MPEMessages::addZone (MPEZone (11, 5, 96, 0));
const uint8 expectedBytes[] =
{
0xbb, 0x64, 0x06, 0xbb, 0x65, 0x00, 0xbb, 0x06, 0x05, // set up zone
0xbb, 0x64, 0x00, 0xbb, 0x65, 0x00, 0xbb, 0x06, 0x60, // per-note pbrange (custom)
0xba, 0x64, 0x00, 0xba, 0x65, 0x00, 0xba, 0x06, 0x00 // master pbrange (custom)
};
testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes));
}
}
beginTest ("set per-note pitchbend range");
{
MPEZone zone (3, 7, 96);
MidiBuffer buffer = MPEMessages::perNotePitchbendRange (zone);
const uint8 expectedBytes[] = { 0xb3, 0x64, 0x00, 0xb3, 0x65, 0x00, 0xb3, 0x06, 0x60 };
testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes));
}
beginTest ("set master pitchbend range");
{
MPEZone zone (3, 7, 48, 60);
MidiBuffer buffer = MPEMessages::masterPitchbendRange (zone);
const uint8 expectedBytes[] = { 0xb2, 0x64, 0x00, 0xb2, 0x65, 0x00, 0xb2, 0x06, 0x3c };
testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes));
}
beginTest ("clear all zones");
{
MidiBuffer buffer = MPEMessages::clearAllZones();
const uint8 expectedBytes[] = { 0xb0, 0x64, 0x06, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x10 };
testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes));
}
beginTest ("set complete state");
{
MPEZoneLayout layout;
layout.addZone (MPEZone (1, 7, 96, 0));
layout.addZone (MPEZone (9, 7));
layout.addZone (MPEZone (5, 3));
layout.addZone (MPEZone (5, 4));
layout.addZone (MPEZone (6, 4));
MidiBuffer buffer = MPEMessages::setZoneLayout (layout);
const uint8 expectedBytes[] = {
0xb0, 0x64, 0x06, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x10, // clear all zones
0xb1, 0x64, 0x06, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x03, // set zone 1 (1, 3)
0xb1, 0x64, 0x00, 0xb1, 0x65, 0x00, 0xb1, 0x06, 0x60, // per-note pbrange (custom)
0xb0, 0x64, 0x00, 0xb0, 0x65, 0x00, 0xb0, 0x06, 0x00, // master pbrange (custom)
0xb6, 0x64, 0x06, 0xb6, 0x65, 0x00, 0xb6, 0x06, 0x04, // set zone 2 (6, 4)
0xb6, 0x64, 0x00, 0xb6, 0x65, 0x00, 0xb6, 0x06, 0x30, // per-note pbrange (default = 48)
0xb5, 0x64, 0x00, 0xb5, 0x65, 0x00, 0xb5, 0x06, 0x02 // master pbrange (default = 2)
};
testMidiBuffer (buffer, expectedBytes, sizeof (expectedBytes));
}
}
private:
//==========================================================================
void testMidiBuffer (MidiBuffer& buffer, const uint8* expectedBytes, int expectedBytesSize)
{
uint8 actualBytes[128] = { 0 };
extractRawBinaryData (buffer, actualBytes, sizeof (actualBytes));
expectEquals (std::memcmp (actualBytes, expectedBytes, (std::size_t) expectedBytesSize), 0);
}
//==========================================================================
void extractRawBinaryData (const MidiBuffer& midiBuffer, const uint8* bufferToCopyTo, std::size_t maxBytes)
{
std::size_t pos = 0;
MidiBuffer::Iterator iter (midiBuffer);
MidiMessage midiMessage;
int samplePosition; // Note: not actually used, so no need to initialise.
while (iter.getNextEvent (midiMessage, samplePosition))
{
const uint8* data = midiMessage.getRawData();
std::size_t dataSize = (std::size_t) midiMessage.getRawDataSize();
if (pos + dataSize > maxBytes)
return;
std::memcpy ((void*) (bufferToCopyTo + pos), data, dataSize);
pos += dataSize;
}
}
};
static MPEMessagesTests MPEMessagesUnitTests;
#endif // JUCE_UNIT_TESTS

+ 96
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h View File

@@ -0,0 +1,96 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPEMESSAGES_H_INCLUDED
#define JUCE_MPEMESSAGES_H_INCLUDED
//==============================================================================
/**
This helper class contains the necessary helper functions to generate
MIDI messages that are exclusive to MPE, such as defining
MPE zones and setting per-note and master pitchbend ranges.
You can then send them to your MPE device using
MidiOutput::sendBlockOfMessagesNow.
All other MPE messages like per-note pitchbend, pressure, and third
dimension, are ordinary MIDI messages that should be created using the MidiMessage
class instead. You just need to take care to send them to the appropriate
per-note MIDI channel.
Note: if you are working with an MPEZoneLayout object inside your app,
you should not use the message sequences provided here. Instead, you should
change the zone layout programmatically with the member functions provided in the
MPEZoneLayout class itself. You should also make sure that the Expressive
MIDI zone layout of your C++ code and of the MPE device are kept in sync.
@see MidiMessage, MPEZoneLayout, MPEZone
*/
class JUCE_API MPEMessages
{
public:
/** Returns the sequence of MIDI messages that, if sent to an Expressive
MIDI device, will define a new MPE zone.
*/
static MidiBuffer addZone (MPEZone zone);
/** Returns the sequence of MIDI messages that, if sent to an Expressive
MIDI device, will change the per-note pitchbend range of an
existing MPE zone.
*/
static MidiBuffer perNotePitchbendRange (MPEZone zone);
/** Returns the sequence of MIDI messages that, if sent to an Expressive
MIDI device, will change the master pitchbend range of an
existing MPE zone.
*/
static MidiBuffer masterPitchbendRange (MPEZone zone);
/** Returns the sequence of MIDI messages that, if sent to an Expressive
MIDI device, will erase all currently defined MPE zones.
*/
static MidiBuffer clearAllZones();
/** Returns the sequence of MIDI messages that, if sent to an Expressive
MIDI device, will reset the whole MPE zone layout of the
device to the laoyut passed in. This will first clear all currently
defined MPE zones, then add all zones contained in the
passed-in zone layout, and set their per-note and master pitchbend
ranges to their current values.
*/
static MidiBuffer setZoneLayout (const MPEZoneLayout& layout);
/** The RPN number used for MPE zone layout messages.
Note: This number can change in later versions of MPE.
Pitchbend range messages (both per-note and master) are instead sent
on RPN 0 as in standard MIDI 1.0.
*/
static const int zoneLayoutMessagesRpnNumber = 6;
};
#endif // JUCE_MPEMESSAGES_H_INCLUDED

+ 132
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp View File

@@ -0,0 +1,132 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
namespace
{
uint16 generateNoteID (int midiChannel, int midiNoteNumber) noexcept
{
jassert (midiChannel > 0 && midiChannel <= 16);
jassert (midiNoteNumber >= 0 && midiNoteNumber < 128);
return uint16 ((midiChannel << 7) + midiNoteNumber);
}
}
//==============================================================================
MPENote::MPENote (int midiChannel_,
int initialNote_,
MPEValue noteOnVelocity_,
MPEValue pitchbend_,
MPEValue pressure_,
MPEValue timbre_,
KeyState keyState_) noexcept
: noteID (generateNoteID (midiChannel_, initialNote_)),
midiChannel (uint8 (midiChannel_)),
initialNote (uint8 (initialNote_)),
noteOnVelocity (noteOnVelocity_),
pitchbend (pitchbend_),
pressure (pressure_),
timbre (timbre_),
noteOffVelocity (MPEValue::minValue()),
keyState (keyState_)
{
jassert (keyState != MPENote::off);
jassert (isValid());
}
MPENote::MPENote() noexcept
: noteID (0),
midiChannel (0),
initialNote (0),
noteOnVelocity (MPEValue::minValue()),
pitchbend (MPEValue::centreValue()),
pressure (MPEValue::centreValue()),
timbre (MPEValue::centreValue()),
noteOffVelocity (MPEValue::minValue()),
keyState (MPENote::off)
{
}
//==============================================================================
bool MPENote::isValid() const noexcept
{
return midiChannel > 0 && midiChannel <= 16 && initialNote >= 0 && initialNote <= 127;
}
//==============================================================================
double MPENote::getFrequencyInHertz (double frequencyOfA) const noexcept
{
double pitchInSemitones = double (initialNote) + totalPitchbendInSemitones;
return frequencyOfA * std::pow (2.0, (pitchInSemitones - 69.0) / 12.0);
}
//==============================================================================
bool MPENote::operator== (const MPENote& other) const noexcept
{
jassert (isValid() && other.isValid());
return noteID == other.noteID;
}
bool MPENote::operator!= (const MPENote& other) const noexcept
{
jassert (isValid() && other.isValid());
return noteID != other.noteID;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class MPENoteTests : public UnitTest
{
public:
MPENoteTests() : UnitTest ("MPENote class") {}
//==========================================================================
void runTest() override
{
beginTest ("getFrequencyInHertz");
{
MPENote note;
note.initialNote = 60;
note.totalPitchbendInSemitones = -0.5;
expectEqualsWithinOneCent (note.getFrequencyInHertz(), 254.178);
}
}
private:
//==========================================================================
void expectEqualsWithinOneCent (double frequencyInHertzActual,
double frequencyInHertzExpected)
{
double ratio = frequencyInHertzActual / frequencyInHertzExpected;
double oneCent = 1.0005946;
expect (ratio < oneCent);
expect (ratio > 1.0 / oneCent);
}
};
static MPENoteTests MPENoteUnitTests;
#endif // JUCE_UNIT_TESTS

+ 180
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPENote.h View File

@@ -0,0 +1,180 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPENOTE_H_INCLUDED
#define JUCE_MPENOTE_H_INCLUDED
//==============================================================================
/**
This struct represents a playing MPE note.
A note is identified by a unique ID, or alternatively, by a MIDI channel
and an initial note. It is characterised by five dimensions of continuous
expressive control. Their current values are represented as
MPEValue objects.
@see MPEValue
*/
struct JUCE_API MPENote
{
//==========================================================================
enum KeyState
{
off = 0,
keyDown = 1,
sustained = 2,
keyDownAndSustained = 3
};
//==========================================================================
/** Constructor.
@param midiChannel The MIDI channel of the note, between 2 and 16.
(Channel 1 can never be a note channel in MPE).
@param initialNote The MIDI note number, between 0 and 127.
@param velocity The note-on velocity of the note.
@param pitchbend The initial per-note pitchbend of the note.
@param pressure The initial pressure of the note.
@param timbre The timbre value of the note.
@param keyState The key state of the note (whether the key is down
and/or the note is sustained). This value must not
be MPENote::off, since you are triggering a new note.
(If not specified, the default value will be MPENOte::keyDown.)
*/
MPENote (int midiChannel,
int initialNote,
MPEValue velocity,
MPEValue pitchbend,
MPEValue pressure,
MPEValue timbre,
KeyState keyState = MPENote::keyDown) noexcept;
/** Default constructor.
Constructs an invalid MPE note (a note with the key state MPENote::off
and an invalid MIDI channel. The only allowed use for such a note is to
call isValid() on it; everything else is undefined behaviour.
*/
MPENote() noexcept;
/** Checks whether the MPE note is valid. */
bool isValid() const noexcept;
//==========================================================================
// Invariants that define the note.
/** A unique ID. Useful to distinguish the note from other simultaneously
sounding notes that may use the same note number or MIDI channel.
This should never change during the lifetime of a note object.
*/
uint16 noteID;
/** The MIDI channel which this note uses.
This should never change during the lifetime of an MPENote object.
*/
uint8 midiChannel;
/** The MIDI note number that was sent when the note was triggered.
This should never change during the lifetime of an MPENote object.
*/
uint8 initialNote;
//==========================================================================
// The five dimensions of continuous expressive control
/** The velocity ("strike") of the note-on.
This dimension will stay constant after the note has been turned on.
*/
MPEValue noteOnVelocity;
/** Current per-note pitchbend of the note (in units of MIDI pitchwheel
position). This dimension can be modulated while the note sounds.
Note: This value is not aware of the currently used pitchbend range,
or an additional master pitchbend that may be simultaneously applied.
To compute the actual effective pitchbend of an MPENote, you should
probably use the member totalPitchbendInSemitones instead.
@see totalPitchbendInSemitones, getFrequencyInHertz
*/
MPEValue pitchbend;
/** Current pressure with which the note is held down.
This dimension can be modulated while the note sounds.
*/
MPEValue pressure;
/** Current value of the note's third expressive dimension, tyically
encoding some kind of timbre parameter.
This dimension can be modulated while the note sounds.
*/
MPEValue timbre;
/** The release velocity ("lift") of the note after a note-off has been
received.
This dimension will only have a meaningful value after a note-off has
been received for the note (and keyState is set to MPENote::off or
MPENOte::sustained). Initially, the value is undefined.
*/
MPEValue noteOffVelocity;
//==========================================================================
/** Current effective pitchbend of the note in units of semitones, relative
to initialNote. You should use this to compute the actual effective pitch
of the note. This value is computed and set by an MPEInstrument to the
sum of the per-note pitchbend value (stored in MPEValue::pitchbend)
and the master pitchbend of the MPE zone, weighted with the per-note
pitchbend range and master pitchbend range of the zone, respectively.
@see getFrequencyInHertz
*/
double totalPitchbendInSemitones;
/** Current key state. Indicates whether the note key is currently down (pressed)
and/or the note is sustained (by a sustain or sostenuto pedal).
*/
KeyState keyState;
//==========================================================================
/** Returns the current frequency of the note in Hertz. This is the a sum of
the initialNote and the totalPitchbendInSemitones, converted to Hertz.
*/
double getFrequencyInHertz (double frequencyOfA = 440.0) const noexcept;
/** Returns true if two notes are the same, determined by their unique ID. */
bool operator== (const MPENote& other) const noexcept;
/** Returns true if two notes are different notes, determined by their unique ID. */
bool operator!= (const MPENote& other) const noexcept;
};
#endif // JUCE_MPENOTE_H_INCLUDED

+ 356
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp View File

@@ -0,0 +1,356 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
MPESynthesiser::MPESynthesiser()
{
}
MPESynthesiser::MPESynthesiser (MPEInstrument* instrument) : MPESynthesiserBase (instrument)
{
}
MPESynthesiser::~MPESynthesiser()
{
}
//==============================================================================
void MPESynthesiser::startVoice (MPESynthesiserVoice* voice, MPENote noteToStart)
{
jassert (voice != nullptr);
voice->currentlyPlayingNote = noteToStart;
voice->noteStarted();
}
void MPESynthesiser::stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff)
{
jassert (voice != nullptr);
voice->currentlyPlayingNote = noteToStop;
voice->noteStopped (allowTailOff);
}
//==============================================================================
void MPESynthesiser::noteAdded (MPENote newNote)
{
const ScopedLock sl (voicesLock);
if (MPESynthesiserVoice* voice = findFreeVoice (newNote, shouldStealVoices))
startVoice (voice, newNote);
}
void MPESynthesiser::notePressureChanged (MPENote changedNote)
{
const ScopedLock sl (voicesLock);
for (int i = 0; i < voices.size(); ++i)
{
MPESynthesiserVoice* voice = voices.getUnchecked (i);
if (voice->isCurrentlyPlayingNote (changedNote))
{
voice->currentlyPlayingNote = changedNote;
voice->notePressureChanged();
}
}
}
void MPESynthesiser::notePitchbendChanged (MPENote changedNote)
{
const ScopedLock sl (voicesLock);
for (int i = 0; i < voices.size(); ++i)
{
MPESynthesiserVoice* voice = voices.getUnchecked (i);
if (voice->isCurrentlyPlayingNote (changedNote))
{
voice->currentlyPlayingNote = changedNote;
voice->notePitchbendChanged();
}
}
}
void MPESynthesiser::noteTimbreChanged (MPENote changedNote)
{
const ScopedLock sl (voicesLock);
for (int i = 0; i < voices.size(); ++i)
{
MPESynthesiserVoice* voice = voices.getUnchecked (i);
if (voice->isCurrentlyPlayingNote (changedNote))
{
voice->currentlyPlayingNote = changedNote;
voice->noteTimbreChanged();
}
}
}
void MPESynthesiser::noteKeyStateChanged (MPENote changedNote)
{
const ScopedLock sl (voicesLock);
for (int i = 0; i < voices.size(); ++i)
{
MPESynthesiserVoice* voice = voices.getUnchecked (i);
if (voice->isCurrentlyPlayingNote (changedNote))
{
voice->currentlyPlayingNote = changedNote;
voice->noteKeyStateChanged();
}
}
}
void MPESynthesiser::noteReleased (MPENote finishedNote)
{
const ScopedLock sl (voicesLock);
for (int i = voices.size(); --i >= 0;)
{
MPESynthesiserVoice* const voice = voices.getUnchecked (i);
if (voice->isCurrentlyPlayingNote(finishedNote))
stopVoice (voice, finishedNote, true);
}
}
void MPESynthesiser::setCurrentPlaybackSampleRate (const double newRate)
{
MPESynthesiserBase::setCurrentPlaybackSampleRate (newRate);
const ScopedLock sl (voicesLock);
turnOffAllVoices (false);
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->setCurrentSampleRate (newRate);
}
void MPESynthesiser::handleMidiEvent (const MidiMessage& m)
{
if (m.isController())
handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue());
else if (m.isProgramChange())
handleProgramChange (m.getChannel(), m.getProgramChangeNumber());
MPESynthesiserBase::handleMidiEvent (m);
}
MPESynthesiserVoice* MPESynthesiser::findFreeVoice (MPENote noteToFindVoiceFor, bool stealIfNoneAvailable) const
{
const ScopedLock sl (voicesLock);
for (int i = 0; i < voices.size(); ++i)
{
MPESynthesiserVoice* const voice = voices.getUnchecked (i);
if (! voice->isActive())
return voice;
}
if (stealIfNoneAvailable)
return findVoiceToSteal (noteToFindVoiceFor);
return nullptr;
}
struct MPEVoiceAgeSorter
{
static int compareElements (MPESynthesiserVoice* v1, MPESynthesiserVoice* v2) noexcept
{
return v1->wasStartedBefore (*v2) ? -1 : (v2->wasStartedBefore (*v1) ? 1 : 0);
}
};
MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceFor) const
{
// This voice-stealing algorithm applies the following heuristics:
// - Re-use the oldest notes first
// - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
// apparently you are trying to render audio without having any voices...
jassert (voices.size() > 0);
// These are the voices we want to protect (ie: only steal if unavoidable)
MPESynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
MPESynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase
// this is a list of voices we can steal, sorted by how long they've been running
Array<MPESynthesiserVoice*> usableVoices;
usableVoices.ensureStorageAllocated (voices.size());
for (int i = 0; i < voices.size(); ++i)
{
MPESynthesiserVoice* const voice = voices.getUnchecked (i);
jassert (voice->isActive()); // We wouldn't be here otherwise
MPEVoiceAgeSorter sorter;
usableVoices.addSorted (sorter, voice);
if (! voice->isPlayingButReleased()) // Don't protect released notes
{
const int noteNumber = voice->getCurrentlyPlayingNote().initialNote;
if (low == nullptr || noteNumber < low->getCurrentlyPlayingNote().initialNote)
low = voice;
if (top == nullptr || noteNumber > top->getCurrentlyPlayingNote().initialNote)
top = voice;
}
}
// Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
if (top == low)
top = nullptr;
const int numUsableVoices = usableVoices.size();
// If we want to re-use the voice to trigger a new note,
// then The oldest note that's playing the same note number is ideal.
if (noteToStealVoiceFor.isValid())
{
for (int i = 0; i < numUsableVoices; ++i)
{
MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice->getCurrentlyPlayingNote().initialNote == noteToStealVoiceFor.initialNote)
return voice;
}
}
// Oldest voice that has been released (no finger on it and not held by sustain pedal)
for (int i = 0; i < numUsableVoices; ++i)
{
MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice != low && voice != top && voice->isPlayingButReleased())
return voice;
}
// Oldest voice that doesn't have a finger on it:
for (int i = 0; i < numUsableVoices; ++i)
{
MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice != low && voice != top
&& voice->getCurrentlyPlayingNote().keyState != MPENote::keyDown
&& voice->getCurrentlyPlayingNote().keyState != MPENote::keyDownAndSustained)
return voice;
}
// Oldest voice that isn't protected
for (int i = 0; i < numUsableVoices; ++i)
{
MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);
if (voice != low && voice != top)
return voice;
}
// We've only got "protected" voices now: lowest note takes priority
jassert (low != nullptr);
// Duophonic synth: give priority to the bass note:
if (top != nullptr)
return top;
return low;
}
//==============================================================================
void MPESynthesiser::addVoice (MPESynthesiserVoice* const newVoice)
{
const ScopedLock sl (voicesLock);
newVoice->setCurrentSampleRate (getSampleRate());
voices.add (newVoice);
}
void MPESynthesiser::clearVoices()
{
const ScopedLock sl (voicesLock);
voices.clear();
}
MPESynthesiserVoice* MPESynthesiser::getVoice (const int index) const
{
const ScopedLock sl (voicesLock);
return voices [index];
}
void MPESynthesiser::removeVoice (const int index)
{
const ScopedLock sl (voicesLock);
voices.remove (index);
}
void MPESynthesiser::reduceNumVoices (const int newNumVoices)
{
// we can't possibly get to a negative number of voices...
jassert (newNumVoices >= 0);
const ScopedLock sl (voicesLock);
while (voices.size() > newNumVoices)
{
if (MPESynthesiserVoice* voice = findFreeVoice (MPENote(), true))
voices.removeObject (voice);
else
voices.remove (0); // if there's no voice to steal, kill the oldest voice
}
}
void MPESynthesiser::turnOffAllVoices (bool allowTailOff)
{
// first turn off all voices (it's more efficient to do this immediately
// rather than to go through the MPEInstrument for this).
for (int i = voices.size(); --i >= 0;)
voices.getUnchecked (i)->noteStopped (allowTailOff);
// finally make sure the MPE Instrument also doesn't have any notes anymore.
instrument->releaseAllNotes();
}
//==============================================================================
void MPESynthesiser::renderNextSubBlock (AudioBuffer<float>& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
{
MPESynthesiserVoice* voice = voices.getUnchecked (i);
if (voice->isActive())
voice->renderNextBlock (buffer, startSample, numSamples);
}
}
void MPESynthesiser::renderNextSubBlock (AudioBuffer<double>& buffer, int startSample, int numSamples)
{
for (int i = voices.size(); --i >= 0;)
{
MPESynthesiserVoice* voice = voices.getUnchecked (i);
if (voice->isActive())
voice->renderNextBlock (buffer, startSample, numSamples);
}
}

+ 313
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h View File

@@ -0,0 +1,313 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPESynthesiser_H_INCLUDED
#define JUCE_MPESynthesiser_H_INCLUDED
//==============================================================================
/**
Base class for an MPE-compatible musical device that can play sounds.
This class extends MPESynthesiserBase by adding the concept of voices,
each of which can play a sound triggered by a MPENote that can be modulated
by MPE dimensions like pressure, pitchbend, and timbre, while the note is
sounding.
To create a synthesiser, you'll need to create a subclass of MPESynthesiserVoice
which can play back one of these sounds at a time.
Then you can use the addVoice() methods to give the synthesiser a set of voices
it can use to play notes. If you only give it one voice it will be monophonic -
the more voices it has, the more polyphony it'll have available.
Then repeatedly call the renderNextBlock() method to produce the audio (inherited
from MPESynthesiserBase). The voices will be started, stopped, and modulated
automatically, based on the MPE/MIDI messages that the synthesiser receives.
Before rendering, be sure to call the setCurrentPlaybackSampleRate() to tell it
what the target playback rate is. This value is passed on to the voices so that
they can pitch their output correctly.
@see MPESynthesiserBase, MPESythesiserVoice, MPENote, MPEInstrument
*/
class JUCE_API MPESynthesiser : public MPESynthesiserBase
{
public:
//==========================================================================
/** Constructor.
You'll need to add some voices before it'll make any sound.
@see addVoice
*/
MPESynthesiser();
/** Constructor to pass to the synthesiser a custom MPEInstrument object
to handle the MPE note state, MIDI channel assignment etc.
(in case you need custom logic for this that goes beyond MIDI and MPE).
The synthesiser will take ownership of this object.
@see MPESynthesiserBase, MPEInstrument
*/
MPESynthesiser (MPEInstrument* instrument);
/** Destructor. */
~MPESynthesiser();
//==========================================================================
/** Deletes all voices. */
void clearVoices();
/** Returns the number of voices that have been added. */
int getNumVoices() const noexcept { return voices.size(); }
/** Returns one of the voices that have been added. */
MPESynthesiserVoice* getVoice (int index) const;
/** Adds a new voice to the synth.
All the voices should be the same class of object and are treated equally.
The object passed in will be managed by the synthesiser, which will delete
it later on when no longer needed. The caller should not retain a pointer to the
voice.
*/
void addVoice (MPESynthesiserVoice* newVoice);
/** Deletes one of the voices. */
void removeVoice (int index);
/** Reduces the number of voices to newNumVoices.
This will repeatedly call findVoiceToSteal() and remove that voice, until
the total number of voices equals newNumVoices. If newNumVoices is greater than
or equal to the current number of voices, this method does nothing.
*/
void reduceNumVoices (int newNumVoices);
/** Release all MPE notes and turn off all voices.
If allowTailOff is true, the voices will be allowed to fade out the notes gracefully
(if they can do). If this is false, the notes will all be cut off immediately.
This method is meant to be called by the user, for example to implement
a MIDI panic button in a synth.
*/
virtual void turnOffAllVoices (bool allowTailOff);
//==========================================================================
/** If set to true, then the synth will try to take over an existing voice if
it runs out and needs to play another note.
The value of this boolean is passed into findFreeVoice(), so the result will
depend on the implementation of this method.
*/
void setVoiceStealingEnabled (bool shouldSteal) noexcept { shouldStealVoices = shouldSteal; }
/** Returns true if note-stealing is enabled. */
bool isVoiceStealingEnabled() const noexcept { return shouldStealVoices; }
//==========================================================================
/** Tells the synthesiser what the sample rate is for the audio it's being used to render.
This overrides the implementation in MPESynthesiserBase, to additionally
propagate the new value to the voices so that they can use it to render the correct
pitches.
*/
void setCurrentPlaybackSampleRate (double newRate) override;
//==========================================================================
/** Handle incoming MIDI events.
This method will be called automatically according to the MIDI data passed
into renderNextBlock(), but you can also call it yourself to manually
inject MIDI events.
This implementation forwards program change messages and non-MPE-related
controller messages to handleProgramChange and handleController, respectively,
and then simply calls through to MPESynthesiserBase::handleMidiEvent to deal
with MPE-related MIDI messages used for MPE notes, zones etc.
This method can be overridden further if you need to do custom MIDI
handling on top of what is provided here.
*/
void handleMidiEvent (const MidiMessage&) override;
/** Callback for MIDI controller messages. The default implementation
provided here does nothing; override this method if you need custom
MIDI controller handling on top of MPE.
This method will be called automatically according to the midi data passed into
renderNextBlock().
*/
virtual void handleController (int /*midiChannel*/,
int /*controllerNumber*/,
int /*controllerValue*/) {}
/** Callback for MIDI program change messages. The default implementation
provided here does nothing; override this method if you need to handle
those messages.
This method will be called automatically according to the midi data passed into
renderNextBlock().
*/
virtual void handleProgramChange (int /*midiChannel*/,
int /*programNumber*/) {}
protected:
//==============================================================================
/** Attempts to start playing a new note.
The default method here will find a free voice that is appropriate for
playing the given MPENote, and use that voice to start playing the sound.
If isNoteStealingEnabled returns true (set this by calling setNoteStealingEnabled),
the synthesiser will use the voice stealing algorithm to find a free voice for
the note (if no voices are free otherwise).
This method will be called automatically according to the midi data passed into
renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state
will become inconsistent.
*/
virtual void noteAdded (MPENote newNote) override;
/** Stops playing a note.
This will be called whenever an MPE note is released (either by a note-off message,
or by a sustain/sostenuto pedal release for a note that already received a note-off),
and should therefore stop playing.
This will find any voice that is currently playing finishedNote,
turn its currently playing note off, and call its noteStopped callback.
This method will be called automatically according to the midi data passed into
renderNextBlock(). Do not call it yourself, otherwise the internal MPE note state
will become inconsistent.
*/
virtual void noteReleased (MPENote finishedNote) override;
/** Will find any voice that is currently playing changedNote, update its
currently playing note, and call its notePressureChanged method.
This method will be called automatically according to the midi data passed into
renderNextBlock(). Do not call it yourself.
*/
virtual void notePressureChanged (MPENote changedNote) override;
/** Will find any voice that is currently playing changedNote, update its
currently playing note, and call its notePitchbendChanged method.
This method will be called automatically according to the midi data passed into
renderNextBlock(). Do not call it yourself.
*/
virtual void notePitchbendChanged (MPENote changedNote) override;
/** Will find any voice that is currently playing changedNote, update its
currently playing note, and call its noteTimbreChanged method.
This method will be called automatically according to the midi data passed into
renderNextBlock(). Do not call it yourself.
*/
virtual void noteTimbreChanged (MPENote changedNote) override;
/** Will find any voice that is currently playing changedNote, update its
currently playing note, and call its noteKeyStateChanged method.
This method will be called automatically according to the midi data passed into
renderNextBlock(). Do not call it yourself.
*/
virtual void noteKeyStateChanged (MPENote changedNote) override;
//==========================================================================
/** This will simply call renderNextBlock for each currently active
voice and fill the buffer with the sum.
Override this method if you need to do more work to render your audio.
*/
virtual void renderNextSubBlock (AudioBuffer<float>& outputAudio,
int startSample,
int numSamples) override;
/** This will simply call renderNextBlock for each currently active
voice and fill the buffer with the sum. (souble-precision version)
Override this method if you need to do more work to render your audio.
*/
virtual void renderNextSubBlock (AudioBuffer<double>& outputAudio,
int startSample,
int numSamples) override;
//==========================================================================
/** Searches through the voices to find one that's not currently playing, and
which can play the given MPE note.
If all voices are active and stealIfNoneAvailable is false, this returns
a nullptr. If all voices are active and stealIfNoneAvailable is true,
this will call findVoiceToSteal() to find a voice.
If you need to find a free voice for something else than playing a note
(e.g. for deleting it), you can pass an invalid (default-constructed) MPENote.
*/
virtual MPESynthesiserVoice* findFreeVoice (MPENote noteToFindVoiceFor,
bool stealIfNoneAvailable) const;
/** Chooses a voice that is most suitable for being re-used to play a new
note, or for being deleted by reduceNumVoices.
The default method will attempt to find the oldest voice that isn't the
bottom or top note being played. If that's not suitable for your synth,
you can override this method and do something more cunning instead.
If you pass a valid MPENote for the optional argument, then the note number
of that note will be taken into account for finding the ideal voice to steal.
If you pass an invalid (default-constructed) MPENote instead, this part of
the algorithm will be ignored.
*/
virtual MPESynthesiserVoice* findVoiceToSteal (MPENote noteToStealVoiceFor = MPENote()) const;
/** Starts a specified voice and tells it to play a particular MPENote.
You should never need to call this, it's called internally by
MPESynthesiserBase::instrument via the noteStarted callback,
but is protected in case it's useful for some custom subclasses.
*/
void startVoice (MPESynthesiserVoice* voice, MPENote noteToStart);
/** Stops a given voice and tells it to stop playing a particular MPENote
(which should be the same note it is actually playing).
You should never need to call this, it's called internally by
MPESynthesiserBase::instrument via the noteReleased callback,
but is protected in case it's useful for some custom subclasses.
*/
void stopVoice (MPESynthesiserVoice* voice, MPENote noteToStop, bool allowTailOff);
//==========================================================================
OwnedArray<MPESynthesiserVoice> voices;
private:
//==========================================================================
bool shouldStealVoices;
CriticalSection voicesLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser)
};
#endif // JUCE_MPESynthesiser_H_INCLUDED

+ 161
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp View File

@@ -0,0 +1,161 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
MPESynthesiserBase::MPESynthesiserBase()
: instrument (new MPEInstrument),
sampleRate (0),
minimumSubBlockSize (32)
{
instrument->addListener (this);
}
MPESynthesiserBase::MPESynthesiserBase (MPEInstrument* inst)
: instrument (inst),
sampleRate (0),
minimumSubBlockSize (32)
{
jassert (instrument != nullptr);
instrument->addListener (this);
}
//==============================================================================
MPEZoneLayout MPESynthesiserBase::getZoneLayout() const noexcept
{
return instrument->getZoneLayout();
}
void MPESynthesiserBase::setZoneLayout (MPEZoneLayout newLayout)
{
instrument->setZoneLayout (newLayout);
}
//==============================================================================
void MPESynthesiserBase::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
{
instrument->enableLegacyMode (pitchbendRange, channelRange);
}
bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept
{
return instrument->isLegacyModeEnabled();
}
Range<int> MPESynthesiserBase::getLegacyModeChannelRange() const noexcept
{
return instrument->getLegacyModeChannelRange();
}
void MPESynthesiserBase::setLegacyModeChannelRange (Range<int> channelRange)
{
instrument->setLegacyModeChannelRange (channelRange);
}
int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept
{
return instrument->getLegacyModePitchbendRange();
}
void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange)
{
instrument->setLegacyModePitchbendRange (pitchbendRange);
}
//==============================================================================
void MPESynthesiserBase::handleMidiEvent (const MidiMessage& m)
{
instrument->processNextMidiEvent (m);
}
//==============================================================================
template <typename floatType>
void MPESynthesiserBase::renderNextBlock (AudioBuffer<floatType>& outputAudio,
const MidiBuffer& inputMidi,
int startSample,
int numSamples)
{
// you must set the sample rate before using this!
jassert (sampleRate != 0);
MidiBuffer::Iterator midiIterator (inputMidi);
midiIterator.setNextSamplePosition (startSample);
int midiEventPos;
MidiMessage m;
const ScopedLock sl (renderAudioLock);
while (numSamples > 0)
{
if (! midiIterator.getNextEvent (m, midiEventPos))
{
renderNextSubBlock (outputAudio, startSample, numSamples);
return;
}
const int samplesToNextMidiMessage = midiEventPos - startSample;
if (samplesToNextMidiMessage >= numSamples)
{
renderNextSubBlock (outputAudio, startSample, numSamples);
handleMidiEvent (m);
break;
}
if (samplesToNextMidiMessage < minimumSubBlockSize)
{
handleMidiEvent (m);
continue;
}
renderNextSubBlock (outputAudio, startSample, samplesToNextMidiMessage);
handleMidiEvent (m);
startSample += samplesToNextMidiMessage;
numSamples -= samplesToNextMidiMessage;
}
while (midiIterator.getNextEvent (m, midiEventPos))
handleMidiEvent (m);
}
// explicit instantiation for supported float types:
template void MPESynthesiserBase::renderNextBlock<float> (AudioBuffer<float>&, const MidiBuffer&, int, int);
template void MPESynthesiserBase::renderNextBlock<double> (AudioBuffer<double>&, const MidiBuffer&, int, int);
//==============================================================================
void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate)
{
if (sampleRate != newRate)
{
const ScopedLock sl (renderAudioLock);
instrument->releaseAllNotes();
sampleRate = newRate;
}
}
//==============================================================================
void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples) noexcept
{
jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
minimumSubBlockSize = numSamples;
}

+ 194
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h View File

@@ -0,0 +1,194 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPESynthesiserBase_H_INCLUDED
#define JUCE_MPESynthesiserBase_H_INCLUDED
//==============================================================================
/**
Derive from this class to create a basic audio generator capable of MPE.
Implement the callbacks of MPEInstrument::Listener (noteAdded, notePressureChanged
etc.) to let your audio generator know that MPE notes were triggered, modulated,
or released. What to do inside them, and how that influences your audio generator,
is up to you!
This class uses an instance of MPEInstrument internally to handle the MPE
note state logic.
This class is a very low-level base class for an MPE instrument. If you need
something more sophisticated, have a look at MPESynthesiser. This class extends
MPESynthesiserBase by adding the concept of voices that can play notes,
a voice stealing algorithm, and much more.
@see MPESynthesiser, MPEInstrument
*/
struct JUCE_API MPESynthesiserBase : public MPEInstrument::Listener
{
public:
//==========================================================================
/** Constructor. */
MPESynthesiserBase();
/** Constructor.
If you use this constructor, the synthesiser will take ownership of the
provided instrument object, and will use it internally to handle the
MPE note state logic.
This is useful if you want to use an instance of your own class derived
from MPEInstrument for the MPE logic.
*/
MPESynthesiserBase (MPEInstrument* instrument);
//==========================================================================
/** Returns the synthesiser's internal MPE zone layout.
This happens by value, to enforce thread-safety and class invariants.
*/
MPEZoneLayout getZoneLayout() const noexcept;
/** Re-sets the synthesiser's internal MPE zone layout to the one passed in.
As a side effect, this will discard all currently playing notes,
call noteReleased for all of them, and disable legacy mode (if previously enabled).
*/
void setZoneLayout (MPEZoneLayout newLayout);
//==========================================================================
/** Tells the synthesiser what the sample rate is for the audio it's being
used to render.
*/
virtual void setCurrentPlaybackSampleRate (double sampleRate);
/** 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.
*/
double getSampleRate() const noexcept { return sampleRate; }
//==========================================================================
/** Creates the next block of audio output.
Call this to make sound. This will chop up the AudioBuffer into subBlock
pieces separated by events in the MIDI buffer, and then call
processNextSubBlock on each one of them. In between you will get calls
to noteAdded/Changed/Finished, where you can update parameters that
depend on those notes to use for your audio rendering.
*/
template <typename floatType>
void renderNextBlock (AudioBuffer<floatType>& outputAudio,
const MidiBuffer& inputMidi,
int startSample,
int numSamples);
//==========================================================================
/** Handle incoming MIDI events (called from renderNextBlock).
The default implementation provided here simply forwards everything
to MPEInstrument::processNextMidiEvent, where it is used to update the
MPE notes, zones etc. MIDI messages not relevant for MPE are ignored.
This method can be overridden if you need to do custom MIDI handling
on top of MPE. The MPESynthesiser class overrides this to implement
callbacks for MIDI program changes and non-MPE-related MIDI controller
messages.
*/
virtual void handleMidiEvent (const MidiMessage&);
//==========================================================================
/** Sets a minimum limit on the size to which audio sub-blocks will be divided when rendering.
When rendering, the audio blocks that are passed into renderNextBlock() will be split up
into smaller blocks that lie between all the incoming midi messages, and it is these smaller
sub-blocks that are rendered with multiple calls to renderVoices().
Obviously in a pathological case where there are midi messages on every sample, then
renderVoices() could be called once per sample and lead to poor performance, so this
setting allows you to set a lower limit on the block size.
The default setting is 32, which means that midi messages are accurate to about < 1ms
accuracy, which is probably fine for most purposes, but you may want to increase or
decrease this value for your synth.
*/
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept;
//==========================================================================
/** Puts the synthesiser into legacy mode.
@param pitchbendRange The note pitchbend range in semitones to use when in legacy mode.
Must be between 0 and 96, otherwise behaviour is undefined.
The default pitchbend range in legacy mode is +/- 2 semitones.
@param channelRange The range of MIDI channels to use for notes when in legacy mode.
The default is to use all MIDI channels (1-16).
To get out of legacy mode, set a new MPE zone layout using setZoneLayout.
*/
void enableLegacyMode (int pitchbendRange = 2,
Range<int> channelRange = Range<int> (1, 17));
/** Returns true if the instrument is in legacy mode, false otherwise. */
bool isLegacyModeEnabled() const noexcept;
/** Returns the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
Range<int> getLegacyModeChannelRange() const noexcept;
/** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */
void setLegacyModeChannelRange (Range<int> channelRange);
/** Returns the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
int getLegacyModePitchbendRange() const noexcept;
/** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
void setLegacyModePitchbendRange (int pitchbendRange);
protected:
//==========================================================================
/** Implement this method to render your audio inside.
@see renderNextBlock
*/
virtual void renderNextSubBlock (AudioBuffer<float>& outputAudio,
int startSample,
int numSamples) = 0;
/** Implement this method if you want to render 64-bit audio as well;
otherwise leave blank.
*/
virtual void renderNextSubBlock (AudioBuffer<double>& /*outputAudio*/,
int /*startSample*/,
int /*numSamples*/) {}
protected:
//==========================================================================
/** @internal */
ScopedPointer<MPEInstrument> instrument;
/** @internal */
CriticalSection renderAudioLock;
private:
//==========================================================================
double sampleRate;
int minimumSubBlockSize;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserBase)
};
#endif // JUCE_MPESynthesiserBase_H_INCLUDED

+ 53
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp View File

@@ -0,0 +1,53 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
MPESynthesiserVoice::MPESynthesiserVoice()
: currentSampleRate (0), noteStartTime (0)
{
}
MPESynthesiserVoice::~MPESynthesiserVoice()
{
}
//==============================================================================
bool MPESynthesiserVoice::isCurrentlyPlayingNote (MPENote note) const noexcept
{
return isActive() && currentlyPlayingNote.noteID == note.noteID;
}
bool MPESynthesiserVoice::isPlayingButReleased() const noexcept
{
return isActive() && currentlyPlayingNote.keyState == MPENote::off;
}
bool MPESynthesiserVoice::wasStartedBefore (const MPESynthesiserVoice& other) const noexcept
{
return noteStartTime < other.noteStartTime;
}
void MPESynthesiserVoice::clearCurrentNote() noexcept
{
currentlyPlayingNote = MPENote();
}

+ 191
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h View File

@@ -0,0 +1,191 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPEVoice_H_INCLUDED
#define JUCE_MPEVoice_H_INCLUDED
//==============================================================================
/**
Represents an MPE voice that an MPESynthesiser can use to play a sound.
A voice plays a single sound at a time, and a synthesiser holds an array of
voices so that it can play polyphonically.
@see MPESynthesiser, MPENote
*/
class JUCE_API MPESynthesiserVoice
{
public:
//========================================================================
/** Constructor. */
MPESynthesiserVoice();
/** Destructor. */
virtual ~MPESynthesiserVoice();
/** Returns the MPENote that this voice is currently playing.
Returns an invalid MPENote if no note is playing
(you can check this using MPENote::isValid() or MPEVoice::isActive()).
*/
MPENote getCurrentlyPlayingNote() const noexcept { return currentlyPlayingNote; }
/** Returns true if the voice is currently playing the given MPENote
(as identified by the note's initial note number and MIDI channel).
*/
bool isCurrentlyPlayingNote (MPENote note) const noexcept;
/** Returns true if this voice is currently busy playing a sound.
By default this just checks whether getCurrentlyPlayingNote()
returns a valid MPE note, but can be overridden for more advanced checking.
*/
virtual bool isActive() const { return currentlyPlayingNote.isValid(); }
/** Returns true if a voice is sounding in its release phase. **/
bool isPlayingButReleased() const noexcept;
/** Called by the MPESynthesiser to let the voice know that a new note has started on it.
This will be called during the rendering callback, so must be fast and thread-safe.
*/
virtual void noteStarted() = 0;
/** Called by the MPESynthesiser to let the voice know that its currently playing note has stopped.
This will be called during the rendering callback, so must be fast and thread-safe.
If allowTailOff is false or the voice doesn't want to tail-off, then it must stop all
sound immediately, and must call clearCurrentNote() to reset the state of this voice
and allow the synth to reassign it another sound.
If allowTailOff is true and the voice decides to do a tail-off, then it's allowed to
begin fading out its sound, and it can stop playing until it's finished. As soon as it
finishes playing (during the rendering callback), it must make sure that it calls
clearCurrentNote().
*/
virtual void noteStopped (bool allowTailOff) = 0;
/** Called by the MPESynthesiser to let the voice know that its currently playing note
has changed its pressure value.
This will be called during the rendering callback, so must be fast and thread-safe.
*/
virtual void notePressureChanged() = 0;
/** Called by the MPESynthesiser to let the voice know that its currently playing note
has changed its pitchbend value.
This will be called during the rendering callback, so must be fast and thread-safe.
Note: You can call currentlyPlayingNote.getFrequencyInHertz() to find out the effective frequency
of the note, as a sum of the initial note number, the per-note pitchbend and the master pitchbend.
*/
virtual void notePitchbendChanged() = 0;
/** Called by the MPESynthesiser to let the voice know that its currently playing note
has changed its timbre value.
This will be called during the rendering callback, so must be fast and thread-safe.
*/
virtual void noteTimbreChanged() = 0;
/** Called by the MPESynthesiser to let the voice know that its currently playing note
has changed its key state.
This typically happens when a sustain or sostenuto pedal is pressed or released (on
an MPE channel relevant for this note), or if the note key is lifted while the sustained
or sostenuto pedal is still held down.
This will be called during the rendering callback, so must be fast and thread-safe.
*/
virtual void noteKeyStateChanged() = 0;
/** Renders the next block of data for this voice.
The output audio data must be added to the current contents of the buffer provided.
Only the region of the buffer between startSample and (startSample + numSamples)
should be altered by this method.
If the voice is currently silent, it should just return without doing anything.
If the sound that the voice is playing finishes during the course of this rendered
block, it must call clearCurrentNote(), to tell the synthesiser that it has finished.
The size of the blocks that are rendered can change each time it is called, and may
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 (AudioBuffer<float>& outputBuffer,
int startSample,
int numSamples) = 0;
/** Renders the next block of 64-bit data for this voice.
Support for 64-bit audio is optional. You can choose to not override this method if
you don't need it (the default implementation simply does nothing).
*/
virtual void renderNextBlock (AudioBuffer<double>& /*outputBuffer*/,
int /*startSample*/,
int /*numSamples*/) {}
/** Changes the voice's reference sample rate.
The rate is set so that subclasses know the output rate and can set their pitch
accordingly.
This method is called by the synth, and subclasses can access the current rate with
the currentSampleRate member.
*/
virtual void setCurrentSampleRate (double newRate) { currentSampleRate = newRate; }
/** 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.
*/
double getSampleRate() const noexcept { return currentSampleRate; }
/** Returns true if this voice started playing its current note before the other voice did. */
bool wasStartedBefore (const MPESynthesiserVoice& other) const noexcept;
protected:
//==========================================================================
/** Resets the state of this voice after a sound has finished playing.
The subclass must call this when it finishes playing a note and becomes available
to play new ones.
It must either call it in the stopNote() method, or if the voice is tailing off,
then it should call it later during the renderNextBlock method, as soon as it
finishes its tail-off.
It can also be called at any time during the render callback if the sound happens
to have finished, e.g. if it's playing a sample and the sample finishes.
*/
void clearCurrentNote() noexcept;
//==========================================================================
double currentSampleRate;
MPENote currentlyPlayingNote;
private:
//==========================================================================
friend class MPESynthesiser;
uint32 noteStartTime;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserVoice)
};
#endif // JUCE_MPEVoice_H_INCLUDED

+ 170
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp View File

@@ -0,0 +1,170 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
MPEValue::MPEValue() noexcept : normalisedValue (8192)
{
}
MPEValue::MPEValue (int value) : normalisedValue (value)
{
}
//==============================================================================
MPEValue MPEValue::from7BitInt (int value) noexcept
{
jassert (value >= 0 && value <= 127);
const int valueAs14Bit = value <= 64 ? value << 7 : int (jmap<float> (float (value - 64), 0.0f, 63.0f, 0.0f, 8191.0f)) + 8192;
return MPEValue (valueAs14Bit);
}
MPEValue MPEValue::from14BitInt (int value) noexcept
{
jassert (value >= 0 && value <= 16383);
return MPEValue (value);
}
//==============================================================================
MPEValue MPEValue::minValue() noexcept { return MPEValue::from7BitInt (0); }
MPEValue MPEValue::centreValue() noexcept { return MPEValue::from7BitInt (64); }
MPEValue MPEValue::maxValue() noexcept { return MPEValue::from7BitInt (127); }
int MPEValue::as7BitInt() const noexcept
{
return normalisedValue >> 7;
}
int MPEValue::as14BitInt() const noexcept
{
return normalisedValue;
}
//==============================================================================
float MPEValue::asSignedFloat() const noexcept
{
return (normalisedValue < 8192)
? jmap<float> (float (normalisedValue), 0.0f, 8192.0f, -1.0f, 0.0f)
: jmap<float> (float (normalisedValue), 8192.0f, 16383.0f, 0.0f, 1.0f);
}
float MPEValue::asUnsignedFloat() const noexcept
{
return jmap<float> (float (normalisedValue), 0.0f, 16383.0f, 0.0f, 1.0f);
}
//==============================================================================
bool MPEValue::operator== (const MPEValue& other) const noexcept
{
return normalisedValue == other.normalisedValue;
}
bool MPEValue::operator!= (const MPEValue& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class MPEValueTests : public UnitTest
{
public:
MPEValueTests() : UnitTest ("MPEValue class") {}
void runTest() override
{
beginTest ("comparison operator");
{
MPEValue value1 = MPEValue::from7BitInt (7);
MPEValue value2 = MPEValue::from7BitInt (7);
MPEValue value3 = MPEValue::from7BitInt (8);
expect (value1 == value1);
expect (value1 == value2);
expect (value1 != value3);
}
beginTest ("special values");
{
expectEquals (MPEValue::minValue().as7BitInt(), 0);
expectEquals (MPEValue::minValue().as14BitInt(), 0);
expectEquals (MPEValue::centreValue().as7BitInt(), 64);
expectEquals (MPEValue::centreValue().as14BitInt(), 8192);
expectEquals (MPEValue::maxValue().as7BitInt(), 127);
expectEquals (MPEValue::maxValue().as14BitInt(), 16383);
}
beginTest ("zero/minimum value");
{
expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f);
expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f);
}
beginTest ("maximum value");
{
expectValuesConsistent (MPEValue::from7BitInt (127), 127, 16383, 1.0f, 1.0f);
expectValuesConsistent (MPEValue::from14BitInt (16383), 127, 16383, 1.0f, 1.0f);
}
beginTest ("centre value");
{
expectValuesConsistent (MPEValue::from7BitInt (64), 64, 8192, 0.0f, 0.5f);
expectValuesConsistent (MPEValue::from14BitInt (8192), 64, 8192, 0.0f, 0.5f);
}
beginTest ("value halfway between min and centre");
{
expectValuesConsistent (MPEValue::from7BitInt (32), 32, 4096, -0.5f, 0.25f);
expectValuesConsistent (MPEValue::from14BitInt (4096), 32, 4096, -0.5f, 0.25f);
}
}
private:
//==========================================================================
void expectValuesConsistent (MPEValue value,
int expectedValueAs7BitInt,
int expectedValueAs14BitInt,
float expectedValueAsSignedFloat,
float expectedValueAsUnsignedFloat)
{
expectEquals (value.as7BitInt(), expectedValueAs7BitInt);
expectEquals (value.as14BitInt(), expectedValueAs14BitInt);
expectFloatWithinRelativeError (value.asSignedFloat(), expectedValueAsSignedFloat, 0.0001f);
expectFloatWithinRelativeError (value.asUnsignedFloat(), expectedValueAsUnsignedFloat, 0.0001f);
}
//==========================================================================
void expectFloatWithinRelativeError (float actualValue, float expectedValue, float maxRelativeError)
{
const float maxAbsoluteError = jmax (1.0f, std::fabs (expectedValue)) * maxRelativeError;
expect (std::fabs (expectedValue - actualValue) < maxAbsoluteError);
}
};
static MPEValueTests MPEValueUnitTests;
#endif // JUCE_UNIT_TESTS

+ 96
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEValue.h View File

@@ -0,0 +1,96 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPEVALUE_H_INCLUDED
#define JUCE_MPEVALUE_H_INCLUDED
//==============================================================================
/**
This class represents a single value for any of the MPE
dimensions of control. It supports values with 7-bit or 14-bit resolutions
(corresponding to 1 or 2 MIDI bytes, respectively). It also offers helper
functions to query the value in a variety of representations that can be
useful in an audio or MIDI context.
*/
class JUCE_API MPEValue
{
public:
//==========================================================================
/** Default constructor. Constructs an MPEValue corresponding
to the centre value.
*/
MPEValue() noexcept;
/** Constructs an MPEValue from an integer between 0 and 127
(using 7-bit precision).
*/
static MPEValue from7BitInt (int value) noexcept;
/** Constructs an MPEValue from an integer between 0 and 16383
(using 14-bit precision).
*/
static MPEValue from14BitInt (int value) noexcept;
/** Constructs an MPEValue corresponding to the centre value. */
static MPEValue centreValue() noexcept;
/** Constructs an MPEValue corresponding to the minimum value. */
static MPEValue minValue() noexcept;
/** Constructs an MPEValue corresponding to the maximum value. */
static MPEValue maxValue() noexcept;
/** Retrieves the current value as an integer between 0 and 127.
Information will be lost if the value was initialised with a precision
higher than 7-bit.
*/
int as7BitInt() const noexcept;
/** Retrieves the current value as an integer between 0 and 16383.
Resolution will be lost if the value was initialised with a precision
higher than 14-bit.
*/
int as14BitInt() const noexcept;
/** Retrieves the current value mapped to a float between -1.0f and 1.0f. */
float asSignedFloat() const noexcept;
/** Retrieves the current value mapped to a float between 0.0f and 1.0f. */
float asUnsignedFloat() const noexcept;
/** Returns true if two values are equal. */
bool operator== (const MPEValue& other) const noexcept;
/** Returns true if two values are not equal. */
bool operator!= (const MPEValue& other) const noexcept;
private:
//==========================================================================
MPEValue (int normalisedValue);
int normalisedValue;
};
#endif // JUCE_MPEVALUE_H_INCLUDED

+ 302
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp View File

@@ -0,0 +1,302 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
namespace
{
void checkAndLimitZoneParameters (int minValue,
int maxValue,
int& valueToCheckAndLimit) noexcept
{
if (valueToCheckAndLimit < minValue || valueToCheckAndLimit > maxValue)
{
// if you hit this, one of the parameters you supplied for MPEZone
// was not within the allowed range!
// we fit this back into the allowed range here to maintain a valid
// state for the zone, but probably the resulting zone is not what you
//wanted it to be!
jassertfalse;
valueToCheckAndLimit = jlimit (minValue, maxValue, valueToCheckAndLimit);
}
}
}
//==============================================================================
MPEZone::MPEZone (int masterChannel_,
int numNoteChannels_,
int perNotePitchbendRange_,
int masterPitchbendRange_) noexcept
: masterChannel (masterChannel_),
numNoteChannels (numNoteChannels_),
perNotePitchbendRange (perNotePitchbendRange_),
masterPitchbendRange (masterPitchbendRange_)
{
checkAndLimitZoneParameters (1, 15, masterChannel);
checkAndLimitZoneParameters (1, 16 - masterChannel, numNoteChannels);
checkAndLimitZoneParameters (0, 96, perNotePitchbendRange);
checkAndLimitZoneParameters (0, 96, masterPitchbendRange);
}
//==============================================================================
int MPEZone::getMasterChannel() const noexcept
{
return masterChannel;
}
int MPEZone::getNumNoteChannels() const noexcept
{
return numNoteChannels;
}
int MPEZone::getFirstNoteChannel() const noexcept
{
return masterChannel + 1;
}
int MPEZone::getLastNoteChannel() const noexcept
{
return masterChannel + numNoteChannels;
}
Range<int> MPEZone::getNoteChannelRange() const noexcept
{
return Range<int>::withStartAndLength (getFirstNoteChannel(), getNumNoteChannels());
}
bool MPEZone::isUsingChannel (int channel) const noexcept
{
jassert (channel > 0 && channel <= 16);
return channel >= masterChannel && channel <= masterChannel + numNoteChannels;
}
bool MPEZone::isUsingChannelAsNoteChannel (int channel) const noexcept
{
jassert (channel > 0 && channel <= 16);
return channel > masterChannel && channel <= masterChannel + numNoteChannels;
}
int MPEZone::getPerNotePitchbendRange() const noexcept
{
return perNotePitchbendRange;
}
int MPEZone::getMasterPitchbendRange() const noexcept
{
return masterPitchbendRange;
}
void MPEZone::setPerNotePitchbendRange (int rangeInSemitones) noexcept
{
checkAndLimitZoneParameters (0, 96, rangeInSemitones);
perNotePitchbendRange = rangeInSemitones;
}
void MPEZone::setMasterPitchbendRange (int rangeInSemitones) noexcept
{
checkAndLimitZoneParameters (0, 96, rangeInSemitones);
masterPitchbendRange = rangeInSemitones;
}
//==============================================================================
bool MPEZone::overlapsWith (MPEZone other) const noexcept
{
if (masterChannel == other.masterChannel)
return true;
if (masterChannel > other.masterChannel)
return other.overlapsWith (*this);
return masterChannel + numNoteChannels >= other.masterChannel;
}
//==============================================================================
bool MPEZone::truncateToFit (MPEZone other) noexcept
{
const int masterChannelDiff = other.masterChannel - masterChannel;
// we need at least 2 channels to be left after truncation:
// 1 master channel and 1 note channel. otherwise we can't truncate.
if (masterChannelDiff < 2)
return false;
numNoteChannels = jmin (numNoteChannels, masterChannelDiff - 1);
return true;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class MPEZoneTests : public UnitTest
{
public:
MPEZoneTests() : UnitTest ("MPEZone class") {}
void runTest() override
{
beginTest ("initialisation");
{
{
MPEZone zone (1, 10);
expectEquals (zone.getMasterChannel(), 1);
expectEquals (zone.getNumNoteChannels(), 10);
expectEquals (zone.getFirstNoteChannel(), 2);
expectEquals (zone.getLastNoteChannel(), 11);
expectEquals (zone.getPerNotePitchbendRange(), 48);
expectEquals (zone.getMasterPitchbendRange(), 2);
expect (zone.isUsingChannel (1));
expect (zone.isUsingChannel (2));
expect (zone.isUsingChannel (10));
expect (zone.isUsingChannel (11));
expect (! zone.isUsingChannel (12));
expect (! zone.isUsingChannel (16));
expect (! zone.isUsingChannelAsNoteChannel (1));
expect (zone.isUsingChannelAsNoteChannel (2));
expect (zone.isUsingChannelAsNoteChannel (10));
expect (zone.isUsingChannelAsNoteChannel (11));
expect (! zone.isUsingChannelAsNoteChannel (12));
expect (! zone.isUsingChannelAsNoteChannel (16));
}
{
MPEZone zone (5, 4);
expectEquals (zone.getMasterChannel(), 5);
expectEquals (zone.getNumNoteChannels(), 4);
expectEquals (zone.getFirstNoteChannel(), 6);
expectEquals (zone.getLastNoteChannel(), 9);
expectEquals (zone.getPerNotePitchbendRange(), 48);
expectEquals (zone.getMasterPitchbendRange(), 2);
expect (! zone.isUsingChannel (1));
expect (! zone.isUsingChannel (4));
expect (zone.isUsingChannel (5));
expect (zone.isUsingChannel (6));
expect (zone.isUsingChannel (8));
expect (zone.isUsingChannel (9));
expect (! zone.isUsingChannel (10));
expect (! zone.isUsingChannel (16));
expect (! zone.isUsingChannelAsNoteChannel (5));
expect (zone.isUsingChannelAsNoteChannel (6));
expect (zone.isUsingChannelAsNoteChannel (8));
expect (zone.isUsingChannelAsNoteChannel (9));
expect (! zone.isUsingChannelAsNoteChannel (10));
}
}
beginTest ("getNoteChannelRange");
{
MPEZone zone (2, 10);
Range<int> noteChannelRange = zone.getNoteChannelRange();
expectEquals (noteChannelRange.getStart(), 3);
expectEquals (noteChannelRange.getEnd(), 13);
}
beginTest ("setting master pitchbend range");
{
MPEZone zone (1, 10);
zone.setMasterPitchbendRange (96);
expectEquals (zone.getMasterPitchbendRange(), 96);
zone.setMasterPitchbendRange (0);
expectEquals (zone.getMasterPitchbendRange(), 0);
expectEquals (zone.getPerNotePitchbendRange(), 48);
}
beginTest ("setting per-note pitchbend range");
{
MPEZone zone (1, 10);
zone.setPerNotePitchbendRange (96);
expectEquals (zone.getPerNotePitchbendRange(), 96);
zone.setPerNotePitchbendRange (0);
expectEquals (zone.getPerNotePitchbendRange(), 0);
expectEquals (zone.getMasterPitchbendRange(), 2);
}
beginTest ("checking overlap");
{
testOverlapsWith (1, 10, 1, 10, true);
testOverlapsWith (1, 4, 6, 3, false);
testOverlapsWith (1, 4, 8, 3, false);
testOverlapsWith (2, 10, 2, 8, true);
testOverlapsWith (1, 10, 3, 2, true);
testOverlapsWith (3, 10, 5, 9, true);
}
beginTest ("truncating");
{
testTruncateToFit (1, 10, 3, 10, true, 1, 1);
testTruncateToFit (3, 10, 1, 10, false, 3, 10);
testTruncateToFit (1, 10, 5, 8, true, 1, 3);
testTruncateToFit (5, 8, 1, 10, false, 5, 8);
testTruncateToFit (1, 10, 4, 3, true, 1, 2);
testTruncateToFit (4, 3, 1, 10, false, 4, 3);
testTruncateToFit (1, 3, 5, 3, true, 1, 3);
testTruncateToFit (5, 3, 1, 3, false, 5, 3);
testTruncateToFit (1, 3, 7, 3, true, 1, 3);
testTruncateToFit (7, 3, 1, 3, false, 7, 3);
testTruncateToFit (1, 10, 2, 10, false, 1, 10);
testTruncateToFit (2, 10, 1, 10, false, 2, 10);
}
}
private:
//==========================================================================
void testOverlapsWith (int masterChannelFirst, int numNoteChannelsFirst,
int masterChannelSecond, int numNoteChannelsSecond,
bool expectedRetVal)
{
MPEZone first (masterChannelFirst, numNoteChannelsFirst);
MPEZone second (masterChannelSecond, numNoteChannelsSecond);
expect (first.overlapsWith (second) == expectedRetVal);
expect (second.overlapsWith (first) == expectedRetVal);
}
//==========================================================================
void testTruncateToFit (int masterChannelFirst, int numNoteChannelsFirst,
int masterChannelSecond, int numNoteChannelsSecond,
bool expectedRetVal,
int masterChannelFirstAfter, int numNoteChannelsFirstAfter)
{
MPEZone first (masterChannelFirst, numNoteChannelsFirst);
MPEZone second (masterChannelSecond, numNoteChannelsSecond);
expect (first.truncateToFit (second) == expectedRetVal);
expectEquals (first.getMasterChannel(), masterChannelFirstAfter);
expectEquals (first.getNumNoteChannels(), numNoteChannelsFirstAfter);
}
};
static MPEZoneTests MPEZoneUnitTests;
#endif // JUCE_UNIT_TESTS

+ 132
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZone.h View File

@@ -0,0 +1,132 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPEZONE_H_INCLUDED
#define JUCE_MPEZONE_H_INCLUDED
//==============================================================================
/**
This struct represents an MPE Zone.
An MPE Zone occupies one master MIDI channel and an arbitrary
number of note channels that immediately follow the master channel.
It also defines a pitchbend range (in semitones) to be applied for per-note
pitchbends and master pitchbends, respectively.
@see MPEZoneLayout
*/
struct JUCE_API MPEZone
{
/** Constructor.
Creates an MPE zone with the given master channel and
number of note channels.
@param masterChannel The master MIDI channel of the new zone.
All master (not per-note) messages should be send to this channel.
Must be between 1 and 15. Otherwise, the behaviour
is undefined.
@param numNoteChannels The number of note channels that the new zone
should use. The first note channel will be one higher
than the master channel. The number of note channels
must be at least 1 and no greater than 16 - masterChannel.
Otherwise, the behaviour is undefined.
@param perNotePitchbendRange The per-note pitchbend range in semitones of the new zone.
Must be between 0 and 96. Otherwise the behaviour is undefined.
If unspecified, the default setting of +/- 48 semitones
will be used.
@param masterPitchbendRange The master pitchbend range in semitones of the new zone.
Must be between 0 and 96. Otherwise the behaviour is undefined.
If unspecified, the default setting of +/- 2 semitones
will be used.
*/
MPEZone (int masterChannel,
int numNoteChannels,
int perNotePitchbendRange = 48,
int masterPitchbendRange = 2) noexcept;
/* Returns the MIDI master channel of this zone. */
int getMasterChannel() const noexcept;
/** Returns the number of note channels occupied by this zone. */
int getNumNoteChannels() const noexcept;
/* Returns the MIDI channel number of the lowest-numbered note channel of this zone. */
int getFirstNoteChannel() const noexcept;
/* Returns the MIDI channel number of the highest-numbered note channel of this zone. */
int getLastNoteChannel() const noexcept;
/** Returns the MIDI channel numbers of the note channels of this zone as a Range. */
Range<int> getNoteChannelRange() const noexcept;
/** Returns true if the MIDI channel (in the range 1-16) is used by this zone
either as a note channel or as the master channel; false otherwise. */
bool isUsingChannel (int channel) const noexcept;
/** Returns true if the MIDI channel (in the range 1-16) is used by this zone
as a note channel; false otherwise. */
bool isUsingChannelAsNoteChannel (int channel) const noexcept;
/** Returns the per-note pitchbend range in semitones set for this zone. */
int getPerNotePitchbendRange() const noexcept;
/** Returns the master pitchbend range in semitones set for this zone. */
int getMasterPitchbendRange() const noexcept;
/** Sets the per-note pitchbend range in semitones for this zone. */
void setPerNotePitchbendRange (int rangeInSemitones) noexcept;
/** Sets the master pitchbend range in semitones for this zone. */
void setMasterPitchbendRange (int rangeInSemitones) noexcept;
/** Returns true if the MIDI channels occupied by this zone
overlap with those occupied by the other zone.
*/
bool overlapsWith (MPEZone other) const noexcept;
/** Tries to truncate this zone in such a way that the range of MIDI channels
it occupies do not overlap with the other zone, by reducing this zone's
number of note channels.
@returns true if the truncation succeeded or if no truncation is necessary
because the zones do not overlap. False if the zone cannot be truncated
in a way that would remove the overlap (in this case you need to delete
the zone to remove the overlap).
*/
bool truncateToFit (MPEZone zoneToAvoid) noexcept;
private:
//==========================================================================
int masterChannel;
int numNoteChannels;
int perNotePitchbendRange;
int masterPitchbendRange;
};
#endif // JUCE_MPEZONE_H_INCLUDED

+ 346
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp View File

@@ -0,0 +1,346 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
MPEZoneLayout::MPEZoneLayout() noexcept
{
}
//==============================================================================
bool MPEZoneLayout::addZone (MPEZone newZone)
{
bool noOtherZonesModified = true;
for (int i = zones.size(); --i >= 0;)
{
MPEZone& zone = zones.getReference (i);
if (zone.overlapsWith (newZone))
{
if (! zone.truncateToFit (newZone))
zones.removeRange (i, 1);
// can't use zones.remove (i) because that requires a default c'tor :-(
noOtherZonesModified = false;
}
}
zones.add (newZone);
return noOtherZonesModified;
}
//==============================================================================
int MPEZoneLayout::getNumZones() const noexcept
{
return zones.size();
}
MPEZone* MPEZoneLayout::getZoneByIndex (int index) const noexcept
{
if (zones.size() < index)
return nullptr;
return &(zones.getReference (index));
}
void MPEZoneLayout::clearAllZones()
{
zones.clear();
}
//==============================================================================
void MPEZoneLayout::processNextMidiEvent (const MidiMessage& message)
{
if (! message.isController())
return;
MidiRPNMessage rpn;
if (rpnDetector.parseControllerMessage (message.getChannel(),
message.getControllerNumber(),
message.getControllerValue(),
rpn))
{
processRpnMessage (rpn);
}
}
void MPEZoneLayout::processRpnMessage (MidiRPNMessage rpn)
{
if (rpn.parameterNumber == MPEMessages::zoneLayoutMessagesRpnNumber)
processZoneLayoutRpnMessage (rpn);
else if (rpn.parameterNumber == 0)
processPitchbendRangeRpnMessage (rpn);
}
void MPEZoneLayout::processZoneLayoutRpnMessage (MidiRPNMessage rpn)
{
if (rpn.value < 16)
addZone (MPEZone (rpn.channel - 1, rpn.value));
else
clearAllZones();
}
//==============================================================================
void MPEZoneLayout::processPitchbendRangeRpnMessage (MidiRPNMessage rpn)
{
if (MPEZone* zone = getZoneByFirstNoteChannel (rpn.channel))
{
zone->setPerNotePitchbendRange (rpn.value);
return;
}
if (MPEZone* zone = getZoneByMasterChannel (rpn.channel))
zone->setMasterPitchbendRange (rpn.value);
}
//==============================================================================
void MPEZoneLayout::processNextMidiBuffer (const MidiBuffer& buffer)
{
MidiBuffer::Iterator iter (buffer);
MidiMessage message;
int samplePosition; // not actually used, so no need to initialise.
while (iter.getNextEvent (message, samplePosition))
processNextMidiEvent (message);
}
//==============================================================================
MPEZone* MPEZoneLayout::getZoneByChannel (int channel) const noexcept
{
for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone)
if (zone->isUsingChannel (channel))
return zone;
return nullptr;
}
MPEZone* MPEZoneLayout::getZoneByMasterChannel (int channel) const noexcept
{
for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone)
if (zone->getMasterChannel() == channel)
return zone;
return nullptr;
}
MPEZone* MPEZoneLayout::getZoneByFirstNoteChannel (int channel) const noexcept
{
for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone)
if (zone->getFirstNoteChannel() == channel)
return zone;
return nullptr;
}
MPEZone* MPEZoneLayout::getZoneByNoteChannel (int channel) const noexcept
{
for (MPEZone* zone = zones.begin(); zone != zones.end(); ++zone)
if (zone->isUsingChannelAsNoteChannel (channel))
return zone;
return nullptr;
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
class MPEZoneLayoutTests : public UnitTest
{
public:
MPEZoneLayoutTests() : UnitTest ("MPEZoneLayout class") {}
void runTest() override
{
beginTest ("initialisation");
{
MPEZoneLayout layout;
expectEquals (layout.getNumZones(), 0);
}
beginTest ("adding zones");
{
MPEZoneLayout layout;
expect (layout.addZone (MPEZone (1, 7)));
expectEquals (layout.getNumZones(), 1);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);
expect (layout.addZone (MPEZone (9, 7)));
expectEquals (layout.getNumZones(), 2);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);
expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9);
expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7);
expect (! layout.addZone (MPEZone (5, 3)));
expectEquals (layout.getNumZones(), 3);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9);
expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7);
expectEquals (layout.getZoneByIndex (2)->getMasterChannel(), 5);
expectEquals (layout.getZoneByIndex (2)->getNumNoteChannels(), 3);
expect (! layout.addZone (MPEZone (5, 4)));
expectEquals (layout.getNumZones(), 2);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 5);
expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 4);
expect (! layout.addZone (MPEZone (6, 4)));
expectEquals (layout.getNumZones(), 2);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 6);
expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 4);
}
beginTest ("querying zones");
{
MPEZoneLayout layout;
layout.addZone (MPEZone (2, 5));
layout.addZone (MPEZone (9, 4));
expect (layout.getZoneByMasterChannel (1) == nullptr);
expect (layout.getZoneByMasterChannel (2) != nullptr);
expect (layout.getZoneByMasterChannel (3) == nullptr);
expect (layout.getZoneByMasterChannel (8) == nullptr);
expect (layout.getZoneByMasterChannel (9) != nullptr);
expect (layout.getZoneByMasterChannel (10) == nullptr);
expectEquals (layout.getZoneByMasterChannel (2)->getNumNoteChannels(), 5);
expectEquals (layout.getZoneByMasterChannel (9)->getNumNoteChannels(), 4);
expect (layout.getZoneByFirstNoteChannel (2) == nullptr);
expect (layout.getZoneByFirstNoteChannel (3) != nullptr);
expect (layout.getZoneByFirstNoteChannel (4) == nullptr);
expect (layout.getZoneByFirstNoteChannel (9) == nullptr);
expect (layout.getZoneByFirstNoteChannel (10) != nullptr);
expect (layout.getZoneByFirstNoteChannel (11) == nullptr);
expectEquals (layout.getZoneByFirstNoteChannel (3)->getNumNoteChannels(), 5);
expectEquals (layout.getZoneByFirstNoteChannel (10)->getNumNoteChannels(), 4);
expect (layout.getZoneByNoteChannel (2) == nullptr);
expect (layout.getZoneByNoteChannel (3) != nullptr);
expect (layout.getZoneByNoteChannel (4) != nullptr);
expect (layout.getZoneByNoteChannel (6) != nullptr);
expect (layout.getZoneByNoteChannel (7) != nullptr);
expect (layout.getZoneByNoteChannel (8) == nullptr);
expect (layout.getZoneByNoteChannel (9) == nullptr);
expect (layout.getZoneByNoteChannel (10) != nullptr);
expect (layout.getZoneByNoteChannel (11) != nullptr);
expect (layout.getZoneByNoteChannel (12) != nullptr);
expect (layout.getZoneByNoteChannel (13) != nullptr);
expect (layout.getZoneByNoteChannel (14) == nullptr);
expectEquals (layout.getZoneByNoteChannel (5)->getNumNoteChannels(), 5);
expectEquals (layout.getZoneByNoteChannel (13)->getNumNoteChannels(), 4);
}
beginTest ("clear all zones");
{
MPEZoneLayout layout;
expect (layout.addZone (MPEZone (1, 7)));
expect (layout.addZone (MPEZone (10, 2)));
layout.clearAllZones();
expectEquals (layout.getNumZones(), 0);
}
beginTest ("process MIDI buffers");
{
MPEZoneLayout layout;
MidiBuffer buffer;
buffer = MPEMessages::addZone (MPEZone (1, 7));
layout.processNextMidiBuffer (buffer);
expectEquals (layout.getNumZones(), 1);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);
buffer = MPEMessages::addZone (MPEZone (9, 7));
layout.processNextMidiBuffer (buffer);
expectEquals (layout.getNumZones(), 2);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);
expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9);
expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7);
MPEZone zone (1, 10);
buffer = MPEMessages::addZone (zone);
layout.processNextMidiBuffer (buffer);
expectEquals (layout.getNumZones(), 1);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 10);
zone.setPerNotePitchbendRange (33);
zone.setMasterPitchbendRange (44);
buffer = MPEMessages::masterPitchbendRange (zone);
buffer.addEvents (MPEMessages::perNotePitchbendRange (zone), 0, -1, 0);
layout.processNextMidiBuffer (buffer);
expectEquals (layout.getZoneByIndex (0)->getPerNotePitchbendRange(), 33);
expectEquals (layout.getZoneByIndex (0)->getMasterPitchbendRange(), 44);
}
beginTest ("process individual MIDI messages");
{
MPEZoneLayout layout;
layout.processNextMidiEvent (MidiMessage (0x80, 0x59, 0xd0)); // unrelated note-off msg
layout.processNextMidiEvent (MidiMessage (0xb1, 0x64, 0x06)); // RPN part 1
layout.processNextMidiEvent (MidiMessage (0xb1, 0x65, 0x00)); // RPN part 2
layout.processNextMidiEvent (MidiMessage (0xb8, 0x0b, 0x66)); // unrelated CC msg
layout.processNextMidiEvent (MidiMessage (0xb1, 0x06, 0x03)); // RPN part 3
layout.processNextMidiEvent (MidiMessage (0x90, 0x60, 0x00)); // unrelated note-on msg
expectEquals (layout.getNumZones(), 1);
expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
}
}
};
static MPEZoneLayoutTests MPEZoneLayoutUnitTests;
#endif // JUCE_UNIT_TESTS

+ 129
- 0
libs/juce/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h View File

@@ -0,0 +1,129 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_MPEZONELAYOUT_H_INCLUDED
#define JUCE_MPEZONELAYOUT_H_INCLUDED
//==============================================================================
/**
This class represents the current MPE zone layout of a device
capable of handling MPE.
Use the MPEMessages helper class to convert the zone layout represented
by this object to MIDI message sequences that you can send to an Expressive
MIDI device to set its zone layout, add zones etc.
@see MPEZone, MPEInstrument
*/
class JUCE_API MPEZoneLayout
{
public:
/** Default constructor.
This will create a layout with no MPE zones.
You can add an MPE zone using the method addZone.
*/
MPEZoneLayout() noexcept;
/** Adds a new MPE zone to the layout.
@param newZone The zone to add.
@return true if the zone was added without modifying any other zones
added previously to the same zone layout object (if any);
false if any existing MPE zones had to be truncated
or deleted entirely in order to to add this new zone.
(Note: the zone itself will always be added with the channel bounds
that were specified; this will not fail.)
*/
bool addZone (MPEZone newZone);
/** Removes all currently present MPE zones. */
void clearAllZones();
/** Pass incoming MIDI messages to an object of this class if you want the
zone layout to properly react to MPE RPN messages like an
MPE device.
MPEMessages::rpnNumber will add or remove zones; RPN 0 will
set the per-note or master pitchbend ranges.
Any other MIDI messages will be ignored by this class.
@see MPEMessages
*/
void processNextMidiEvent (const MidiMessage& message);
/** Pass incoming MIDI buffers to an object of this class if you want the
zone layout to properly react to MPE RPN messages like an
MPE device.
MPEMessages::rpnNumber will add or remove zones; RPN 0 will
set the per-note or master pitchbend ranges.
Any other MIDI messages will be ignored by this class.
@see MPEMessages
*/
void processNextMidiBuffer (const MidiBuffer& buffer);
/** Returns the current number of MPE zones. */
int getNumZones() const noexcept;
/** Returns a pointer to the MPE zone at the given index,
or nullptr if there is no such zone.
*/
MPEZone* getZoneByIndex (int index) const noexcept;
/** Returns a pointer to the zone which uses the specified channel (1-16),
or nullptr if there is no such zone.
*/
MPEZone* getZoneByChannel (int midiChannel) const noexcept;
/** Returns a pointer to the zone which has the specified channel (1-16)
as its master channel, or nullptr if there is no such zone.
*/
MPEZone* getZoneByMasterChannel (int midiChannel) const noexcept;
/** Returns a pointer to the zone which has the specified channel (1-16)
as its first note channel, or nullptr if there is no such zone.
*/
MPEZone* getZoneByFirstNoteChannel (int midiChannel) const noexcept;
/** Returns a pointer to the zone which has the specified channel (1-16)
as one of its note channels, or nullptr if there is no such zone.
*/
MPEZone* getZoneByNoteChannel (int midiChannel) const noexcept;
private:
//==========================================================================
Array<MPEZone> zones;
MidiRPNDetector rpnDetector;
void processRpnMessage (MidiRPNMessage);
void processZoneLayoutRpnMessage (MidiRPNMessage);
void processPitchbendRangeRpnMessage (MidiRPNMessage);
};
#endif // JUCE_MPEZONELAYOUT_H_INCLUDED

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

@@ -71,7 +71,7 @@ public:
virtual bool isLooping() const = 0;
/** Tells the source whether you'd like it to play in a loop. */
virtual void setLooping (bool shouldLoop) { (void) shouldLoop; }
virtual void setLooping (bool shouldLoop) { ignoreUnused (shouldLoop); }
};


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

@@ -114,6 +114,7 @@ void Synthesiser::clearVoices()
SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice)
{
const ScopedLock sl (lock);
newVoice->setCurrentPlaybackSampleRate (sampleRate);
return voices.add (newVoice);
}
@@ -511,13 +512,13 @@ void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown)
void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/)
{
(void) midiChannel;
ignoreUnused (midiChannel);
jassert (midiChannel > 0 && midiChannel <= 16);
}
void Synthesiser::handleProgramChange (int midiChannel, int programNumber)
{
(void) midiChannel; (void) programNumber;
ignoreUnused (midiChannel, programNumber);
jassert (midiChannel > 0 && midiChannel <= 16);
}
@@ -557,6 +558,9 @@ SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
// - Re-use the oldest notes first
// - Protect the lowest & topmost notes, even if sustained, but not if they've been released.
// apparently you are trying to render audio without having any voices...
jassert (voices.size() > 0);
// These are the voices we want to protect (ie: only steal if unavoidable)
SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h View File

@@ -151,7 +151,7 @@ private:
File volumeDir;
Array<File> tracks;
int currentReaderTrack;
ScopedPointer <AudioFormatReader> reader;
ScopedPointer<AudioFormatReader> reader;
AudioCDReader (const File& volume);
#elif JUCE_WINDOWS


+ 106
- 200
libs/juce/source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp View File

@@ -86,6 +86,65 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
};
//==============================================================================
// This is an AudioTransportSource which will own it's assigned source
struct AudioSourceOwningTransportSource : public AudioTransportSource
{
AudioSourceOwningTransportSource (PositionableAudioSource* s) : source (s)
{
AudioTransportSource::setSource (s);
}
~AudioSourceOwningTransportSource()
{
setSource (nullptr);
}
private:
ScopedPointer<PositionableAudioSource> source;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource)
};
//==============================================================================
// An AudioSourcePlayer which will remove itself from the AudioDeviceManager's
// callback list once it finishes playing its source
struct AutoRemovingSourcePlayer : public AudioSourcePlayer,
private Timer
{
AutoRemovingSourcePlayer (AudioDeviceManager& dm, AudioTransportSource* ts, bool ownSource)
: manager (dm), transportSource (ts, ownSource)
{
jassert (ts != nullptr);
manager.addAudioCallback (this);
AudioSourcePlayer::setSource (transportSource);
startTimerHz (10);
}
~AutoRemovingSourcePlayer()
{
setSource (nullptr);
manager.removeAudioCallback (this);
}
void timerCallback() override
{
if (getCurrentSource() == nullptr || ! transportSource->isPlaying())
delete this;
}
void audioDeviceStopped() override
{
AudioSourcePlayer::audioDeviceStopped();
setSource (nullptr);
}
private:
AudioDeviceManager& manager;
OptionalScopedPointer<AudioTransportSource> transportSource;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer)
};
//==============================================================================
AudioDeviceManager::AudioDeviceManager()
@@ -103,8 +162,11 @@ AudioDeviceManager::~AudioDeviceManager()
{
currentAudioDevice = nullptr;
defaultMidiOutput = nullptr;
}
for (int i = 0; i < callbacks.size(); ++i)
if (AutoRemovingSourcePlayer* p = dynamic_cast<AutoRemovingSourcePlayer*> (callbacks.getUnchecked(i)))
delete p;
}
//==============================================================================
void AudioDeviceManager::createDeviceTypesIfNeeded()
@@ -926,143 +988,16 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
}
}
//==============================================================================
// This is an AudioTransportSource which will own it's assigned source
class AudioSourceOwningTransportSource : public AudioTransportSource
{
public:
AudioSourceOwningTransportSource() {}
~AudioSourceOwningTransportSource() { setSource (nullptr); }
void setSource (PositionableAudioSource* newSource)
{
if (src != newSource)
{
ScopedPointer<PositionableAudioSource> oldSourceDeleter (src);
src = newSource;
// tell the base class about the new source before deleting the old one
AudioTransportSource::setSource (newSource);
}
}
private:
ScopedPointer<PositionableAudioSource> src;
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)
{
AudioSource* oldSource = getCurrentSource();
if (AudioTransportSource* oldTransport = dynamic_cast<AudioTransportSource*> (oldSource))
oldTransport->removeChangeListener (this);
if (newSource != nullptr)
newSource->addChangeListener (this);
AudioSourcePlayer::setSource (newSource);
if (deleteWhenDone)
delete oldSource;
}
private:
// only allow myself to be deleted when my audio callback has been removed
~AutoRemovingSourcePlayer()
{
setSource (nullptr);
}
AudioDeviceManager& manager;
bool deleteWhenDone, hasAddedCallback, recursiveEntry;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer)
};
//==============================================================================
// An AudioSource which simply outputs a buffer
class AudioSampleBufferSource : public PositionableAudioSource
class AudioSampleBufferSource : public PositionableAudioSource
{
public:
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool shouldLoop, bool ownBuffer)
: position (0),
buffer (audioBuffer),
looping (shouldLoop),
deleteWhenDone (ownBuffer)
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer)
: buffer (audioBuffer, ownBuffer),
position (0), looping (false)
{}
~AudioSampleBufferSource()
{
if (deleteWhenDone)
delete buffer;
}
//==============================================================================
void setNextReadPosition (int64 newPosition) override
{
@@ -1074,69 +1009,45 @@ public:
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;
}
int64 getNextReadPosition() const override { return static_cast<int64> (position); }
int64 getTotalLength() const override { return static_cast<int64> (buffer->getNumSamples()); }
void setLooping (bool shouldLoop) override
{
looping = shouldLoop;
}
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 prepareToPlay (int, double) override {}
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));
bufferToFill.clearActiveBufferRegion();
for (ch = 0; ch < maxOutChannels; ch++)
{
int inChannel = ch % maxInChannels;
const int bufferSize = buffer->getNumSamples();
const int samplesNeeded = bufferToFill.numSamples;
const int samplesToCopy = jmin (bufferSize - position, samplesNeeded);
if (max > 0)
bufferToFill.buffer->copyFrom (ch, bufferToFill.startSample, *buffer, inChannel, position, max);
}
if (samplesToCopy > 0)
{
const int maxInChannels = buffer->getNumChannels();
const int maxOutChannels = jmin (bufferToFill.buffer->getNumChannels(), jmax (maxInChannels, 2));
for (; ch < bufferToFill.buffer->getNumChannels(); ++ch)
bufferToFill.buffer->clear (ch, bufferToFill.startSample, bufferToFill.numSamples);
for (int i = 0; i < maxOutChannels; ++i)
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer,
i % maxInChannels, position, samplesToCopy);
}
position += max;
position += samplesNeeded;
if (looping)
position = position % buffer->getNumSamples();
position %= bufferSize;
}
private:
//==============================================================================
OptionalScopedPointer<AudioSampleBuffer> buffer;
int position;
AudioSampleBuffer* buffer;
bool looping, deleteWhenDone;
bool looping;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource)
};
@@ -1165,38 +1076,38 @@ void AudioDeviceManager::playSound (const void* resourceData, size_t resourceSiz
void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFinished)
{
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true);
if (reader != nullptr)
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true);
}
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished)
{
if (buffer != nullptr)
playSound (new AudioSampleBufferSource (buffer, 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;
AudioTransportSource* transport = dynamic_cast<AudioTransportSource*> (audioSource);
if (transport == nullptr)
{
if (deleteWhenFinished)
{
AudioSourceOwningTransportSource* owningTransportSource = new AudioSourceOwningTransportSource();
owningTransportSource->setSource (audioSource);
transportSource = owningTransportSource;
transport = new AudioSourceOwningTransportSource (audioSource);
}
else
{
transportSource = new AudioTransportSource;
transportSource->setSource (audioSource);
transport = new AudioTransportSource();
transport->setSource (audioSource);
deleteWhenFinished = true;
}
// recursively call myself
playSound (transportSource, true);
transportSource->start();
}
transport->start();
new AutoRemovingSourcePlayer (*this, transport, deleteWhenFinished);
}
else
{
@@ -1205,11 +1116,6 @@ void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool d
}
}
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished)
{
playSound (new AudioSampleBufferSource (buffer, false, deleteWhenFinished), true);
}
void AudioDeviceManager::playTestSound()
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();


+ 1
- 5
libs/juce/source/modules/juce_audio_devices/juce_audio_devices.cpp View File

@@ -22,7 +22,7 @@
==============================================================================
*/
#if defined (JUCE_AUDIO_DEVICES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE
#ifdef JUCE_AUDIO_DEVICES_H_INCLUDED
/* When you add this cpp file to your project, you mustn't include it in a file where you've
already included any other headers - just put it inside a file on its own, possibly with your config
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
@@ -31,10 +31,6 @@
#error "Incorrect use of JUCE cpp file"
#endif
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../juce_core/native/juce_BasicNativeHeaders.h"
#include "juce_audio_devices.h"


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

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


+ 1
- 2
libs/juce/source/modules/juce_audio_devices/midi_io/juce_MidiInput.h View File

@@ -74,8 +74,7 @@ public:
int numBytesSoFar,
double timestamp)
{
// (this bit is just to avoid compiler warnings about unused variables)
(void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp;
ignoreUnused (source, messageData, numBytesSoFar, timestamp);
}
};


+ 10
- 0
libs/juce/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp View File

@@ -40,6 +40,16 @@ MidiOutput::MidiOutput(const String& midiName)
{
}
void MidiOutput::sendBlockOfMessagesNow (const MidiBuffer& buffer)
{
MidiBuffer::Iterator i (buffer);
MidiMessage message;
int samplePosition; // Note: not actually used, so no need to initialise.
while (i.getNextEvent (message, samplePosition))
sendMessageNow (message);
}
void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer,
const double millisecondCounterToStartAt,
double samplesPerSecondForBuffer)


+ 4
- 3
libs/juce/source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h View File

@@ -87,11 +87,12 @@ public:
/** Returns the name of this device. */
const String& getName() const noexcept { return name; }
/** Makes this device output a midi message.
@see MidiMessage
*/
/** Sends out a MIDI message immediately. */
void sendMessageNow (const MidiMessage& message);
/** Sends out a sequence of MIDI messages immediately. */
void sendBlockOfMessagesNow (const MidiBuffer& buffer);
//==============================================================================
/** This lets you supply a block of messages that will be sent out at some point
in the future.


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h View File

@@ -51,7 +51,7 @@ public:
void pushMidiData (const void* inputData, int numBytes, double time,
UserDataType* input, CallbackType& callback)
{
const uint8* d = static_cast <const uint8*> (inputData);
const uint8* d = static_cast<const uint8*> (inputData);
while (numBytes > 0)
{


+ 5
- 3
libs/juce/source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp View File

@@ -22,6 +22,8 @@
==============================================================================
*/
#undef check
const char* const openSLTypeName = "Android OpenSL";
bool isOpenSLAvailable()
@@ -43,7 +45,7 @@ public:
{
// OpenSL has piss-poor support for determining latency, so the only way I can find to
// get a number for this is by asking the AudioTrack/AudioRecord classes..
AndroidAudioIODevice javaDevice (String::empty);
AndroidAudioIODevice javaDevice (deviceName);
// this is a total guess about how to calculate the latency, but seems to vaguely agree
// with the devices I've tested.. YMMV
@@ -550,7 +552,7 @@ private:
static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) noexcept
{
jassert (queue == static_cast<Player*> (context)->playerBufferQueue); (void) queue;
jassert (queue == static_cast<Player*> (context)->playerBufferQueue); ignoreUnused (queue);
static_cast<Player*> (context)->bufferList.bufferReturned();
}
@@ -685,7 +687,7 @@ private:
static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) noexcept
{
jassert (queue == static_cast<Recorder*> (context)->recorderBufferQueue); (void) queue;
jassert (queue == static_cast<Recorder*> (context)->recorderBufferQueue); ignoreUnused (queue);
static_cast<Recorder*> (context)->bufferList.bufferReturned();
}


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

@@ -393,13 +393,13 @@ private:
static void interruptionListenerCallback (void* client, UInt32 interruptionType)
{
const Array <iOSAudioIODevice*>& activeDevices = static_cast <AudioSessionHolder*> (client)->activeDevices;
const Array<iOSAudioIODevice*>& activeDevices = static_cast<AudioSessionHolder*> (client)->activeDevices;
for (int i = activeDevices.size(); --i >= 0;)
activeDevices.getUnchecked(i)->interruptionListener (interruptionType);
}
Array <iOSAudioIODevice*> activeDevices;
Array<iOSAudioIODevice*> activeDevices;
};
static AudioSessionHolder& getSessionHolder()


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp View File

@@ -993,7 +993,7 @@ public:
{
jassert (hasScanned); // need to call scanForDevices() before doing this
if (ALSAAudioIODevice* d = dynamic_cast <ALSAAudioIODevice*> (device))
if (ALSAAudioIODevice* d = dynamic_cast<ALSAAudioIODevice*> (device))
return asInput ? inputIds.indexOf (d->inputId)
: outputIds.indexOf (d->outputId);


+ 4
- 4
libs/juce/source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp View File

@@ -388,7 +388,7 @@ private:
if (callback != nullptr)
{
if ((numActiveInChans + numActiveOutChans) > 0)
callback->audioDeviceIOCallback (const_cast <const float**> (inChans.getData()), numActiveInChans,
callback->audioDeviceIOCallback (const_cast<const float**> (inChans.getData()), numActiveInChans,
outChans, numActiveOutChans, numSamples);
}
else
@@ -437,7 +437,7 @@ private:
static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg)
{
if (JackAudioIODevice* device = static_cast <JackAudioIODevice*> (arg))
if (JackAudioIODevice* device = static_cast<JackAudioIODevice*> (arg))
device->updateActivePorts();
}
@@ -470,7 +470,7 @@ private:
AudioIODeviceCallback* callback;
CriticalSection callbackLock;
HeapBlock <float*> inChans, outChans;
HeapBlock<float*> inChans, outChans;
int totalNumberOfInputChannels;
int totalNumberOfOutputChannels;
Array<void*> inputPorts, outputPorts;
@@ -557,7 +557,7 @@ public:
{
jassert (hasScanned); // need to call scanForDevices() before doing this
if (JackAudioIODevice* d = dynamic_cast <JackAudioIODevice*> (device))
if (JackAudioIODevice* d = dynamic_cast<JackAudioIODevice*> (device))
return asInput ? inputIds.indexOf (d->inputId)
: outputIds.indexOf (d->outputId);


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp View File

@@ -136,7 +136,7 @@ private:
HeapBlock<pollfd> pfd ((size_t) numPfds);
snd_seq_poll_descriptors (seqHandle, pfd, (unsigned int) numPfds, POLLIN);
HeapBlock <uint8> buffer (maxEventSize);
HeapBlock<uint8> buffer (maxEventSize);
while (! threadShouldExit())
{


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm View File

@@ -169,7 +169,7 @@ void AudioCDReader::refreshTrackLengths()
{
XmlDocument doc (toc);
const char* error = CDReaderHelpers::getTrackOffsets (doc, trackStartSamples);
(void) error; // could be logged..
ignoreUnused (error); // could be logged..
lengthInSamples = trackStartSamples.getLast() - trackStartSamples.getFirst();
}


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

@@ -155,9 +155,7 @@ public:
outputLatency (0),
bitDepth (32),
callback (nullptr),
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
audioProcID (0),
#endif
deviceID (id),
started (false),
sampleRate (0),
@@ -625,11 +623,7 @@ public:
if (deviceID != 0)
{
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
if (OK (AudioDeviceAddIOProc (deviceID, audioIOProc, this)))
#else
if (OK (AudioDeviceCreateIOProcID (deviceID, audioIOProc, this, &audioProcID)))
#endif
{
if (OK (AudioDeviceStart (deviceID, audioIOProc)))
{
@@ -637,12 +631,8 @@ public:
}
else
{
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc));
#else
OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
audioProcID = 0;
#endif
}
}
}
@@ -669,13 +659,8 @@ public:
&& ! leaveInterruptRunning)
{
OK (AudioDeviceStop (deviceID, audioIOProc));
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
OK (AudioDeviceRemoveIOProc (deviceID, audioIOProc));
#else
OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
audioProcID = 0;
#endif
started = false;
@@ -793,9 +778,7 @@ public:
Array<double> sampleRates;
Array<int> bufferSizes;
AudioIODeviceCallback* callback;
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
AudioDeviceIOProcID audioProcID;
#endif
private:
CriticalSection callbackLock;


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp View File

@@ -37,7 +37,7 @@ namespace CoreMidiHelpers
Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err));
#endif
(void) lineNum;
ignoreUnused (lineNum);
return false;
}


+ 13
- 20
libs/juce/source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp View File

@@ -67,7 +67,7 @@ namespace ASIODebugging
#else
static void dummyLog() {}
#define JUCE_ASIO_LOG(msg) ASIODebugging::dummyLog()
#define JUCE_ASIO_LOG_ERROR(msg, errNum) (void) errNum; ASIODebugging::dummyLog()
#define JUCE_ASIO_LOG_ERROR(msg, errNum) ignoreUnused (errNum); ASIODebugging::dummyLog()
#endif
}
@@ -690,31 +690,24 @@ public:
JUCE_ASIO_LOG ("showing control panel");
bool done = false;
insideControlPanelModalLoop = true;
JUCE_TRY
{
// are there are devices that need to be closed before showing their control panel?
// close();
insideControlPanelModalLoop = true;
const uint32 started = Time::getMillisecondCounter();
const uint32 started = Time::getMillisecondCounter();
if (asioObject != nullptr)
{
asioObject->controlPanel();
if (asioObject != nullptr)
{
asioObject->controlPanel();
const int spent = (int) Time::getMillisecondCounter() - (int) started;
const int spent = (int) Time::getMillisecondCounter() - (int) started;
JUCE_ASIO_LOG ("spent: " + String (spent));
JUCE_ASIO_LOG ("spent: " + String (spent));
if (spent > 300)
{
shouldUsePreferredSize = true;
done = true;
}
if (spent > 300)
{
shouldUsePreferredSize = true;
done = true;
}
}
JUCE_CATCH_ALL
insideControlPanelModalLoop = false;
return done;
@@ -785,7 +778,7 @@ private:
HeapBlock<ASIOSampleFormat> inputFormat, outputFormat;
WaitableEvent event1;
HeapBlock <float> tempBuffer;
HeapBlock<float> tempBuffer;
int volatile bufferIndex, numActiveInputChans, numActiveOutputChans;
bool deviceIsOpen, isStarted, buffersCreated;


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp View File

@@ -367,7 +367,7 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples)
hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4));
HeapBlock <byte> buffer (bytesPerBlock);
HeapBlock<byte> buffer (bytesPerBlock);
AudioSampleBuffer sourceBuffer (2, samplesPerBlock);
int samplesDone = 0;


+ 7
- 7
libs/juce/source/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp View File

@@ -1027,7 +1027,7 @@ AudioCDReader::AudioCDReader (void* handle_)
AudioCDReader::~AudioCDReader()
{
using namespace CDReaderHelpers;
CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
delete device;
}
@@ -1035,7 +1035,7 @@ bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int sta
int64 startSampleInFile, int numSamples)
{
using namespace CDReaderHelpers;
CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
bool ok = true;
@@ -1131,7 +1131,7 @@ bool AudioCDReader::isCDStillPresent() const
{
using namespace CDReaderHelpers;
TOC toc = { 0 };
return static_cast <CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc);
return static_cast<CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc);
}
void AudioCDReader::refreshTrackLengths()
@@ -1142,7 +1142,7 @@ void AudioCDReader::refreshTrackLengths()
TOC toc = { 0 };
if (static_cast <CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc))
if (static_cast<CDDeviceWrapper*> (handle)->deviceHandle.readTOC (&toc))
{
int numTracks = 1 + toc.lastTrack - toc.firstTrack;
@@ -1174,7 +1174,7 @@ int AudioCDReader::getLastIndex() const
int AudioCDReader::getIndexAt (int samplePos)
{
using namespace CDReaderHelpers;
CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
const int frameNeeded = samplePos / samplesPerFrame;
@@ -1255,7 +1255,7 @@ Array<int> AudioCDReader::findIndexesInTrack (const int trackNumber)
if (needToScan)
{
CDDeviceWrapper* const device = static_cast <CDDeviceWrapper*> (handle);
CDDeviceWrapper* const device = static_cast<CDDeviceWrapper*> (handle);
int pos = trackStart;
int last = -1;
@@ -1305,5 +1305,5 @@ Array<int> AudioCDReader::findIndexesInTrack (const int trackNumber)
void AudioCDReader::ejectDisk()
{
using namespace CDReaderHelpers;
static_cast <CDDeviceWrapper*> (handle)->deviceHandle.openDrawer (true);
static_cast<CDDeviceWrapper*> (handle)->deviceHandle.openDrawer (true);
}

+ 5
- 7
libs/juce/source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp View File

@@ -182,11 +182,9 @@ namespace DSoundLogging
}
}
#define CATCH JUCE_CATCH_EXCEPTION
#define JUCE_DS_LOG(a) DSoundLogging::logMessage(a);
#define JUCE_DS_LOG_ERROR(a) DSoundLogging::logError(a, __LINE__);
#else
#define CATCH JUCE_CATCH_ALL
#define JUCE_DS_LOG(a)
#define JUCE_DS_LOG_ERROR(a)
#endif
@@ -251,7 +249,7 @@ public:
{
JUCE_DS_LOG ("closing output: " + name);
HRESULT hr = pOutputBuffer->Stop();
JUCE_DS_LOG_ERROR (hr); (void) hr;
JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr);
pOutputBuffer->Release();
pOutputBuffer = nullptr;
@@ -354,7 +352,7 @@ public:
hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */);
if (SUCCEEDED (hr))
return String::empty;
return String();
}
}
}
@@ -534,7 +532,7 @@ public:
{
JUCE_DS_LOG ("closing input: " + name);
HRESULT hr = pInputBuffer->Stop();
JUCE_DS_LOG_ERROR (hr); (void) hr;
JUCE_DS_LOG_ERROR (hr); ignoreUnused (hr);
pInputBuffer->Release();
pInputBuffer = nullptr;
@@ -597,7 +595,7 @@ public:
hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */);
if (SUCCEEDED (hr))
return String::empty;
return String();
}
}
@@ -1226,7 +1224,7 @@ public:
{
jassert (hasScanned); // need to call scanForDevices() before doing this
if (DSoundAudioIODevice* const d = dynamic_cast <DSoundAudioIODevice*> (device))
if (DSoundAudioIODevice* const d = dynamic_cast<DSoundAudioIODevice*> (device))
return asInput ? d->inputDeviceIndex
: d->outputDeviceIndex;


+ 3
- 3
libs/juce/source/modules/juce_audio_devices/native/juce_win32_Midi.cpp View File

@@ -116,7 +116,7 @@ public:
static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR midiMessage, DWORD_PTR timeStamp)
{
MidiInCollector* const collector = reinterpret_cast <MidiInCollector*> (dwInstance);
MidiInCollector* const collector = reinterpret_cast<MidiInCollector*> (dwInstance);
if (activeMidiCollectors.contains (collector))
{
@@ -270,8 +270,8 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call
}
}
ScopedPointer <MidiInput> in (new MidiInput (name));
ScopedPointer <MidiInCollector> collector (new MidiInCollector (in, *callback));
ScopedPointer<MidiInput> in (new MidiInput (name));
ScopedPointer<MidiInCollector> collector (new MidiInCollector (in, *callback));
HMIDIIN h;
MMRESULT err = midiInOpen (&h, deviceId,


+ 1
- 1
libs/juce/source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp View File

@@ -32,7 +32,7 @@ namespace WasapiClasses
void logFailure (HRESULT hr)
{
(void) hr;
ignoreUnused (hr);
jassert (hr != (HRESULT) 0x800401f0); // If you hit this, it means you're trying to call from
// a thread which hasn't been initialised with CoInitialize().


+ 4
- 4
libs/juce/source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp View File

@@ -531,7 +531,7 @@ public:
}
else if (type == chunkName ("INST"))
{
HeapBlock <InstChunk> inst;
HeapBlock<InstChunk> inst;
inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1);
input->read (inst, (int) length);
inst->copyTo (metadataValues);
@@ -715,7 +715,7 @@ private:
using namespace AiffFileHelpers;
const bool couldSeekOk = output->setPosition (headerPosition);
(void) couldSeekOk;
ignoreUnused (couldSeekOk);
// if this fails, you've given it an output stream that can't seek! It needs
// to be able to seek back to write the header
@@ -972,7 +972,7 @@ bool AiffAudioFormat::canHandleFile (const File& f)
AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails)
{
ScopedPointer <AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream));
ScopedPointer<AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream));
if (w->sampleRate > 0 && w->numChannels > 0)
return w.release();
@@ -1003,7 +1003,7 @@ AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out,
const StringPairArray& metadataValues,
int /*qualityOptionIndex*/)
{
if (getPossibleBitDepths().contains (bitsPerSample))
if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, (unsigned int) bitsPerSample, metadataValues);
return nullptr;


+ 2
- 2
libs/juce/source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp View File

@@ -433,7 +433,7 @@ public:
memcpy (buffer + 18, info.md5sum, 16);
const bool seekOk = output->setPosition (4);
(void) seekOk;
ignoreUnused (seekOk);
// if this fails, you've given it an output stream that can't seek! It needs
// to be able to seek back to write the header
@@ -536,7 +536,7 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out,
const StringPairArray& /*metadataValues*/,
int qualityOptionIndex)
{
if (getPossibleBitDepths().contains (bitsPerSample))
if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
{
ScopedPointer<FlacWriter> w (new FlacWriter (out, sampleRate, numberOfChannels,
(uint32) bitsPerSample, qualityOptionIndex));


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

@@ -110,7 +110,7 @@ private:
if (cp.start (processArgs))
{
const String childOutput (cp.readAllProcessOutput());
DBG (childOutput); (void) childOutput;
DBG (childOutput); ignoreUnused (childOutput);
cp.waitForProcessToFinish (10000);
return tempMP3.getFile().getSize() > 0;
@@ -205,6 +205,9 @@ AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* stream
const StringPairArray& metadataValues,
int qualityOptionIndex)
{
if (streamToWriteTo == nullptr)
return nullptr;
int vbr = 4;
int cbr = 0;


+ 2
- 2
libs/juce/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp View File

@@ -1613,7 +1613,7 @@ private:
headerParsed = sideParsed = dataParsed = isFreeFormat = wasFreeFormat = false;
lastFrameSize = -1;
needToSyncBitStream = true;
frameSize = sideInfoSize = dataSize = frameSize = bitIndex = 0;
frameSize = sideInfoSize = dataSize = bitIndex = 0;
lastFrameSizeNoPadding = bufferSpaceIndex = 0;
bufferPointer = bufferSpace[bufferSpaceIndex] + 512;
synthBo = 1;
@@ -3020,7 +3020,7 @@ public:
}
const int numToCopy = jmin (decodedEnd - decodedStart, numSamples);
float* const* const dst = reinterpret_cast <float**> (destSamples);
float* const* const dst = reinterpret_cast<float**> (destSamples);
memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, sizeof (float) * (size_t) numToCopy);
if (numDestChannels > 1 && dst[1] != nullptr)


+ 7
- 3
libs/juce/source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp View File

@@ -428,7 +428,7 @@ private:
const String s (metadata [name]);
if (s.isNotEmpty())
vorbis_comment_add_tag (&vc, vorbisName, const_cast <char*> (s.toRawUTF8()));
vorbis_comment_add_tag (&vc, vorbisName, const_cast<char*> (s.toRawUTF8()));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter)
@@ -483,8 +483,12 @@ AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out,
const StringPairArray& metadataValues,
int qualityOptionIndex)
{
ScopedPointer <OggWriter> w (new OggWriter (out, sampleRate, numChannels,
(unsigned int) bitsPerSample, qualityOptionIndex, metadataValues));
if (out == nullptr)
return nullptr;
ScopedPointer<OggWriter> w (new OggWriter (out, sampleRate, numChannels,
(unsigned int) bitsPerSample,
qualityOptionIndex, metadataValues));
return w->ok ? w.release() : nullptr;
}


+ 3
- 3
libs/juce/source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp View File

@@ -142,7 +142,7 @@ public:
if (err != noErr)
return;
HeapBlock <AudioChannelLayout> qt_audio_channel_layout;
HeapBlock<AudioChannelLayout> qt_audio_channel_layout;
qt_audio_channel_layout.calloc (output_layout_size, 1);
MovieAudioExtractionGetProperty (extractor,
@@ -322,8 +322,8 @@ private:
Thread::ThreadID lastThreadId;
MovieAudioExtractionRef extractor;
AudioStreamBasicDescription inputStreamDesc;
HeapBlock <AudioBufferList> bufferList;
HeapBlock <char> dataBuffer;
HeapBlock<AudioBufferList> bufferList;
HeapBlock<char> dataBuffer;
Handle dataHandle;
//==============================================================================


+ 2
- 2
libs/juce/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp View File

@@ -845,7 +845,7 @@ namespace WavFileHelpers
static MemoryBlock createFrom (const StringPairArray& values)
{
const String ISRC (values.getValue (WavAudioFormat::ISRC, String::empty));
const String ISRC (values.getValue (WavAudioFormat::ISRC, String()));
MemoryOutputStream xml;
if (ISRC.isNotEmpty())
@@ -1606,7 +1606,7 @@ AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sa
unsigned int numChannels, int bitsPerSample,
const StringPairArray& metadataValues, int /*qualityOptionIndex*/)
{
if (getPossibleBitDepths().contains (bitsPerSample))
if (out != nullptr && getPossibleBitDepths().contains (bitsPerSample))
return new WavAudioFormatWriter (out, sampleRate, (unsigned int) numChannels,
(unsigned int) bitsPerSample, metadataValues);


+ 1
- 1
libs/juce/source/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp View File

@@ -151,7 +151,7 @@ AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* audioFileSt
// use them to open a file!
jassert (getNumKnownFormats() > 0);
ScopedPointer <InputStream> in (audioFileStream);
ScopedPointer<InputStream> in (audioFileStream);
if (in != nullptr)
{


+ 2
- 2
libs/juce/source/modules/juce_audio_formats/format/juce_AudioFormatManager.h View File

@@ -126,8 +126,8 @@ public:
The stream that is passed-in must be capable of being repositioned so
that all the formats can have a go at opening it.
If none of the registered formats can open the stream, it'll return 0. If it
returns a reader, it's the caller's responsibility to delete the reader.
If none of the registered formats can open the stream, it'll return nullptr.
If it returns a reader, it's the caller's responsibility to delete the reader.
*/
AudioFormatReader* createReaderFor (InputStream* audioFileStream);


+ 1
- 1
libs/juce/source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp View File

@@ -95,7 +95,7 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader,
}
}
if (! write (const_cast <const int**> (buffers), numToDo))
if (! write (const_cast<const int**> (buffers), numToDo))
return false;
numSamplesToRead -= numToDo;


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

@@ -22,7 +22,7 @@
==============================================================================
*/
#if defined (JUCE_AUDIO_FORMATS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE
#ifdef JUCE_AUDIO_FORMATS_H_INCLUDED
/* When you add this cpp file to your project, you mustn't include it in a file where you've
already included any other headers - just put it inside a file on its own, possibly with your config
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
@@ -31,10 +31,6 @@
#error "Incorrect use of JUCE cpp file"
#endif
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../juce_core/native/juce_BasicNativeHeaders.h"
#include "juce_audio_formats.h"


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

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


+ 2
- 2
libs/juce/source/modules/juce_audio_formats/sampler/juce_Sampler.cpp View File

@@ -93,7 +93,7 @@ void SamplerVoice::startNote (const int midiNoteNumber,
SynthesiserSound* s,
const int /*currentPitchWheelPosition*/)
{
if (const SamplerSound* const sound = dynamic_cast <const SamplerSound*> (s))
if (const SamplerSound* const sound = dynamic_cast<const SamplerSound*> (s))
{
pitchRatio = pow (2.0, (midiNoteNumber - sound->midiRootNote) / 12.0)
* sound->sourceSampleRate / getSampleRate();
@@ -152,7 +152,7 @@ void SamplerVoice::controllerMoved (const int /*controllerNumber*/,
//==============================================================================
void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples)
{
if (const SamplerSound* const playingSound = static_cast <SamplerSound*> (getCurrentlyPlayingSound().get()))
if (const SamplerSound* const playingSound = static_cast<SamplerSound*> (getCurrentlyPlayingSound().get()))
{
const float* const inL = playingSound->data->getReadPointer (0);
const float* const inR = playingSound->data->getNumChannels() > 1


+ 400
- 114
libs/juce/source/modules/juce_audio_plugin_client/AAX/juce_AAX_Wrapper.cpp View File

@@ -22,21 +22,14 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_AAX && (JUCE_INCLUDED_AAX_IN_MM || defined (_WIN32) || defined (_WIN64))
#ifdef _MSC_VER
#include <windows.h>
#else
#include <Cocoa/Cocoa.h>
#endif
#include "../utility/juce_IncludeSystemHeaders.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_PluginBusUtilities.h"
#undef Component
#ifdef __clang__
@@ -98,6 +91,8 @@
#pragma comment(lib, JucePlugin_AAXLibs_path JUCE_AAX_LIB_PATH JUCE_AAX_LIB JUCE_AAX_LIB_SUFFIX ".lib")
#endif
#undef check
using juce::Component;
const int32_t juceChunkType = 'juce';
@@ -107,7 +102,7 @@ struct AAXClasses
{
static void check (AAX_Result result)
{
jassert (result == AAX_SUCCESS); (void) result;
jassert (result == AAX_SUCCESS); ignoreUnused (result);
}
static int getParamIndexFromID (AAX_CParamID paramID) noexcept
@@ -120,47 +115,75 @@ struct AAXClasses
return AAX::IsParameterIDEqual (paramID, cDefaultMasterBypassID) != 0;
}
static AAX_EStemFormat getFormatForChans (const int numChans) noexcept
static AAX_EStemFormat getFormatForAudioChannelSet (const AudioChannelSet& set, bool ignoreLayout) noexcept
{
switch (numChans)
// if the plug-in ignores layout, it is ok to convert between formats only by their numchannnels
if (ignoreLayout)
{
case 0: return AAX_eStemFormat_None;
case 1: return AAX_eStemFormat_Mono;
case 2: return AAX_eStemFormat_Stereo;
case 3: return AAX_eStemFormat_LCR;
case 4: return AAX_eStemFormat_Quad;
case 5: return AAX_eStemFormat_5_0;
case 6: return AAX_eStemFormat_5_1;
case 7: return AAX_eStemFormat_7_0_DTS;
case 8: return AAX_eStemFormat_7_1_DTS;
default: jassertfalse; break;
switch (set.size())
{
case 0: return AAX_eStemFormat_None;
case 1: return AAX_eStemFormat_Mono;
case 2: return AAX_eStemFormat_Stereo;
case 3: return AAX_eStemFormat_LCR;
case 4: return AAX_eStemFormat_Quad;
case 5: return AAX_eStemFormat_5_0;
case 6: return AAX_eStemFormat_5_1;
case 7: return AAX_eStemFormat_7_0_DTS;
case 8: return AAX_eStemFormat_7_1_DTS;
default:
break;
}
return AAX_eStemFormat_INT32_MAX;
}
return AAX_eStemFormat_None;
if (set == AudioChannelSet::disabled()) return AAX_eStemFormat_None;
if (set == AudioChannelSet::mono()) return AAX_eStemFormat_Mono;
if (set == AudioChannelSet::stereo()) return AAX_eStemFormat_Stereo;
if (set == AudioChannelSet::createLCR()) return AAX_eStemFormat_LCR;
if (set == AudioChannelSet::createLCRS()) return AAX_eStemFormat_LCRS;
if (set == AudioChannelSet::quadraphonic()) return AAX_eStemFormat_Quad;
if (set == AudioChannelSet::create5point0()) return AAX_eStemFormat_5_0;
if (set == AudioChannelSet::create5point1()) return AAX_eStemFormat_5_1;
if (set == AudioChannelSet::create6point0()) return AAX_eStemFormat_6_0;
if (set == AudioChannelSet::create6point1()) return AAX_eStemFormat_6_1;
if (set == AudioChannelSet::create7point0()) return AAX_eStemFormat_7_0_DTS;
if (set == AudioChannelSet::create7point1()) return AAX_eStemFormat_7_1_DTS;
if (set == AudioChannelSet::createFront7point0()) return AAX_eStemFormat_7_0_SDDS;
if (set == AudioChannelSet::createFront7point1()) return AAX_eStemFormat_7_1_SDDS;
return AAX_eStemFormat_INT32_MAX;
}
static int getNumChannelsForStemFormat (AAX_EStemFormat format) noexcept
static AudioChannelSet channelSetFromStemFormat (AAX_EStemFormat format, bool ignoreLayout) noexcept
{
switch (format)
if (! ignoreLayout)
{
case AAX_eStemFormat_None: return 0;
case AAX_eStemFormat_Mono: return 1;
case AAX_eStemFormat_Stereo: return 2;
case AAX_eStemFormat_LCR: return 3;
case AAX_eStemFormat_LCRS:
case AAX_eStemFormat_Quad: return 4;
case AAX_eStemFormat_5_0: return 5;
case AAX_eStemFormat_5_1:
case AAX_eStemFormat_6_0: return 6;
case AAX_eStemFormat_6_1:
case AAX_eStemFormat_7_0_SDDS:
case AAX_eStemFormat_7_0_DTS: return 7;
case AAX_eStemFormat_7_1_SDDS:
case AAX_eStemFormat_7_1_DTS: return 8;
default: jassertfalse; break;
switch (format)
{
case AAX_eStemFormat_None: return AudioChannelSet::disabled();
case AAX_eStemFormat_Mono: return AudioChannelSet::mono();
case AAX_eStemFormat_Stereo: return AudioChannelSet::stereo();
case AAX_eStemFormat_LCR: return AudioChannelSet::createLCR();
case AAX_eStemFormat_LCRS: return AudioChannelSet::createLCRS();
case AAX_eStemFormat_Quad: return AudioChannelSet::quadraphonic();
case AAX_eStemFormat_5_0: return AudioChannelSet::create5point0();
case AAX_eStemFormat_5_1: return AudioChannelSet::create5point1();
case AAX_eStemFormat_6_0: return AudioChannelSet::create6point0();
case AAX_eStemFormat_6_1: return AudioChannelSet::create6point1();
case AAX_eStemFormat_7_0_SDDS: return AudioChannelSet::createFront7point0();
case AAX_eStemFormat_7_0_DTS: return AudioChannelSet::create7point0();
case AAX_eStemFormat_7_1_SDDS: return AudioChannelSet::createFront7point1();
case AAX_eStemFormat_7_1_DTS: return AudioChannelSet::create7point1();
default:
break;
}
return AudioChannelSet::disabled();
}
return 0;
return AudioChannelSet::discreteChannels (jmax (0, static_cast<int> (AAX_STEM_FORMAT_CHANNEL_COUNT (format))));
}
static const char* getSpeakerArrangementString (AAX_EStemFormat format) noexcept
@@ -220,16 +243,17 @@ struct AAXClasses
int32_t* bufferSize;
int32_t* bypass;
#if JucePlugin_WantsMidiInput
#if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
AAX_IMIDINode* midiNodeIn;
#endif
#if JucePlugin_ProducesMidiOutput
#if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
AAX_IMIDINode* midiNodeOut;
#endif
PluginInstanceInfo* pluginInstance;
int32_t* isPrepared;
int32_t* sideChainBuffers;
};
struct JUCEAlgorithmIDs
@@ -241,26 +265,28 @@ struct AAXClasses
bufferSize = AAX_FIELD_INDEX (JUCEAlgorithmContext, bufferSize),
bypass = AAX_FIELD_INDEX (JUCEAlgorithmContext, bypass),
#if JucePlugin_WantsMidiInput
#if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
midiNodeIn = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeIn),
#endif
#if JucePlugin_ProducesMidiOutput
#if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
midiNodeOut = AAX_FIELD_INDEX (JUCEAlgorithmContext, midiNodeOut),
#endif
pluginInstance = AAX_FIELD_INDEX (JUCEAlgorithmContext, pluginInstance),
preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared)
preparedFlag = AAX_FIELD_INDEX (JUCEAlgorithmContext, isPrepared),
sideChainBuffers = AAX_FIELD_INDEX (JUCEAlgorithmContext, sideChainBuffers)
};
};
#if JucePlugin_WantsMidiInput
#if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
static AAX_IMIDINode* getMidiNodeIn (const JUCEAlgorithmContext& c) noexcept { return c.midiNodeIn; }
#else
static AAX_IMIDINode* getMidiNodeIn (const JUCEAlgorithmContext&) noexcept { return nullptr; }
#endif
#if JucePlugin_ProducesMidiOutput
#if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
AAX_IMIDINode* midiNodeOut;
static AAX_IMIDINode* getMidiNodeOut (const JUCEAlgorithmContext& c) noexcept { return c.midiNodeOut; }
#else
@@ -438,12 +464,16 @@ struct AAXClasses
public AudioProcessorListener
{
public:
JuceAAX_Processor() : sampleRate (0), lastBufferSize (1024), maxBufferSize (1024)
JuceAAX_Processor() : pluginInstance (createPluginFilterOfType (AudioProcessor::wrapperType_AAX)),
busUtils (*pluginInstance, false),
sampleRate (0), lastBufferSize (1024), maxBufferSize (1024),
hasSidechain (false)
{
pluginInstance = createPluginFilterOfType (AudioProcessor::wrapperType_AAX);
pluginInstance->setPlayHead (this);
pluginInstance->addListener (this);
busUtils.findAllCompatibleLayouts();
AAX_CEffectParameters::GetNumberOfChunks (&juceChunkIndex);
}
@@ -451,9 +481,13 @@ struct AAXClasses
AAX_Result EffectInit() override
{
AAX_Result err;
check (Controller()->GetSampleRate (&sampleRate));
preparePlugin();
if ((err = preparePlugin()) != AAX_SUCCESS)
return err;
addBypassParameter();
addAudioProcessorParameters();
@@ -800,39 +834,63 @@ struct AAXClasses
return AAX_CEffectParameters::NotificationReceived (type, data, size);
}
void process (const float* const* inputs, float* const* outputs, const int bufferSize,
const bool bypass, AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut)
const float* getAudioBufferForInput (const float* const* inputs, const int sidechain, const int mainNumIns, int idx) const noexcept
{
const int numIns = pluginInstance->getNumInputChannels();
const int numOuts = pluginInstance->getNumOutputChannels();
jassert (idx < (mainNumIns + 1));
if (numOuts >= numIns)
{
for (int i = 0; i < numIns; ++i)
memcpy (outputs[i], inputs[i], (size_t) bufferSize * sizeof (float));
if (idx < mainNumIns)
return inputs[idx];
return (sidechain != -1 ? inputs[sidechain] : sideChainBuffer.getData());
}
void process (const float* const* inputs, float* const* outputs, const int sideChainBufferIdx,
const int bufferSize, const bool bypass,
AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut)
{
const int numIns = pluginInstance->getTotalNumInputChannels();
const int numOuts = pluginInstance->getTotalNumOutputChannels();
process (outputs, numOuts, bufferSize, bypass, midiNodeIn, midiNodesOut);
if (pluginInstance->isSuspended())
{
for (int i = 0; i < numOuts; ++i)
FloatVectorOperations::clear (outputs[i], bufferSize);
}
else
{
if (channelList.size() <= numIns)
channelList.insertMultiple (-1, nullptr, 1 + numIns - channelList.size());
float** channels = channelList.getRawDataPointer();
const int mainNumIns = numIns > 0 ? pluginInstance->busArrangement.inputBuses.getReference (0).channels.size() : 0;
const int sidechain = busUtils.getNumEnabledBuses (true) >= 2 ? sideChainBufferIdx : -1;
for (int i = 0; i < numOuts; ++i)
if (numOuts >= numIns)
{
memcpy (outputs[i], inputs[i], (size_t) bufferSize * sizeof (float));
channels[i] = outputs[i];
for (int i = 0; i < numIns; ++i)
memcpy (outputs[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float));
process (outputs, numOuts, bufferSize, bypass, midiNodeIn, midiNodesOut);
}
else
{
if (channelList.size() <= numIns)
channelList.insertMultiple (-1, nullptr, 1 + numIns - channelList.size());
float** channels = channelList.getRawDataPointer();
for (int i = 0; i < numOuts; ++i)
{
memcpy (outputs[i], getAudioBufferForInput (inputs, sidechain, mainNumIns, i), (size_t) bufferSize * sizeof (float));
channels[i] = outputs[i];
}
for (int i = numOuts; i < numIns; ++i)
channels[i] = const_cast<float*> (inputs[i]);
for (int i = numOuts; i < numIns; ++i)
channels[i] = const_cast<float*> (getAudioBufferForInput (inputs, sidechain, mainNumIns, i));
process (channels, numIns, bufferSize, bypass, midiNodeIn, midiNodesOut);
process (channels, numIns, bufferSize, bypass, midiNodeIn, midiNodesOut);
}
}
}
bool supportsSidechain() const noexcept { return hasSidechain; };
private:
void process (float* const* channels, const int numChans, const int bufferSize,
const bool bypass, AAX_IMIDINode* midiNodeIn, AAX_IMIDINode* midiNodesOut)
@@ -841,10 +899,9 @@ struct AAXClasses
midiBuffer.clear();
(void) midiNodeIn;
(void) midiNodesOut;
ignoreUnused (midiNodeIn, midiNodesOut);
#if JucePlugin_WantsMidiInput
#if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
{
AAX_CMidiStream* const midiStream = midiNodeIn->GetNodeBuffer();
const uint32_t numMidiEvents = midiStream->mBufferSize;
@@ -864,9 +921,7 @@ struct AAXClasses
if (lastBufferSize != bufferSize)
{
lastBufferSize = bufferSize;
pluginInstance->setPlayConfigDetails (pluginInstance->getNumInputChannels(),
pluginInstance->getNumOutputChannels(),
sampleRate, bufferSize);
pluginInstance->setRateAndBufferSizeDetails (sampleRate, bufferSize);
if (bufferSize > maxBufferSize)
{
@@ -877,6 +932,7 @@ struct AAXClasses
// value during initialisation.
pluginInstance->prepareToPlay (sampleRate, bufferSize);
maxBufferSize = bufferSize;
sideChainBuffer.realloc (static_cast<size_t> (maxBufferSize));
}
}
@@ -888,7 +944,7 @@ struct AAXClasses
pluginInstance->processBlock (buffer, midiBuffer);
}
#if JucePlugin_ProducesMidiOutput
#if JucePlugin_ProducesMidiOutput || JucePlugin_IsMidiEffect
{
const juce::uint8* midiEventData;
int midiEventSize, midiEventPosition;
@@ -911,8 +967,6 @@ struct AAXClasses
}
}
}
#else
(void) midiNodesOut;
#endif
}
@@ -964,36 +1018,70 @@ struct AAXClasses
}
}
void preparePlugin()
AAX_Result preparePlugin()
{
AudioProcessor& audioProcessor = getPluginInstance();
#if JucePlugin_IsMidiEffect
// MIDI effect plug-ins do not support any audio channels
jassert (audioProcessor.busArrangement.getTotalNumInputChannels() == 0
&& audioProcessor.busArrangement.getTotalNumOutputChannels() == 0);
#else
AAX_EStemFormat inputStemFormat = AAX_eStemFormat_None;
check (Controller()->GetInputStemFormat (&inputStemFormat));
const int numberOfInputChannels = getNumChannelsForStemFormat (inputStemFormat);
AAX_EStemFormat outputStemFormat = AAX_eStemFormat_None;
check (Controller()->GetOutputStemFormat (&outputStemFormat));
const int numberOfOutputChannels = getNumChannelsForStemFormat (outputStemFormat);
AudioProcessor& audioProcessor = getPluginInstance();
const AudioChannelSet inputSet = channelSetFromStemFormat (inputStemFormat, busUtils.busIgnoresLayout (true, 0));
const AudioChannelSet outputSet = channelSetFromStemFormat (outputStemFormat, busUtils.busIgnoresLayout (false, 0));
audioProcessor.setSpeakerArrangement (getSpeakerArrangementString (inputStemFormat),
getSpeakerArrangementString (outputStemFormat));
if ( (inputSet == AudioChannelSet::disabled() && inputStemFormat != AAX_eStemFormat_None)
|| (outputSet == AudioChannelSet::disabled() && outputStemFormat != AAX_eStemFormat_None))
return AAX_ERROR_UNIMPLEMENTED;
audioProcessor.setPlayConfigDetails (numberOfInputChannels, numberOfOutputChannels, sampleRate, lastBufferSize);
bool success = true;
if (busUtils.getBusCount (true) > 0)
success = audioProcessor.setPreferredBusArrangement (true, 0, inputSet);
if (success && busUtils.getBusCount (false) > 0)
success = audioProcessor.setPreferredBusArrangement (false, 0, outputSet);
// This should never happen as the plugin reported that this layout is supported
jassert (success);
hasSidechain = enableAuxBusesForCurrentFormat (busUtils, inputSet, outputSet);
if (hasSidechain)
sideChainBuffer.realloc (static_cast<size_t> (maxBufferSize));
// recheck the format
if ( (busUtils.getBusCount (true) > 0 && busUtils.getChannelSet (true, 0) != inputSet)
|| (busUtils.getBusCount (false) > 0 && busUtils.getChannelSet (false, 0) != outputSet)
|| (hasSidechain && busUtils.getNumChannels(true, 1) != 1))
return AAX_ERROR_UNIMPLEMENTED;
#endif
audioProcessor.setRateAndBufferSizeDetails (sampleRate, maxBufferSize);
audioProcessor.prepareToPlay (sampleRate, lastBufferSize);
maxBufferSize = lastBufferSize;
check (Controller()->SetSignalLatency (audioProcessor.getLatencySamples()));
return AAX_SUCCESS;
}
ScopedJuceInitialiser_GUI libraryInitialiser;
ScopedPointer<AudioProcessor> pluginInstance;
PluginBusUtilities busUtils;
MidiBuffer midiBuffer;
Array<float*> channelList;
int32_t juceChunkIndex;
AAX_CSampleRate sampleRate;
int lastBufferSize, maxBufferSize;
bool hasSidechain;
HeapBlock<float> sideChainBuffer;
struct ChunkMemoryBlock : public ReferenceCountedObject
{
@@ -1044,6 +1132,25 @@ struct AAXClasses
JUCE_DECLARE_NON_COPYABLE (IndexAsParamID)
};
//==============================================================================
struct AAXFormatConfiguration
{
AAXFormatConfiguration() noexcept
: inputFormat (AAX_eStemFormat_None), outputFormat (AAX_eStemFormat_None) {}
AAXFormatConfiguration (AAX_EStemFormat inFormat, AAX_EStemFormat outFormat) noexcept
: inputFormat (inFormat), outputFormat (outFormat) {}
AAX_EStemFormat inputFormat, outputFormat;
bool operator== (const AAXFormatConfiguration other) const noexcept { return (inputFormat == other.inputFormat) && (outputFormat == other.outputFormat); }
bool operator< (const AAXFormatConfiguration other) const noexcept
{
return (inputFormat == other.inputFormat) ? (outputFormat < other.outputFormat) : (inputFormat < other.inputFormat);
}
};
//==============================================================================
static void AAX_CALLBACK algorithmProcessCallback (JUCEAlgorithmContext* const instancesBegin[],
const void* const instancesEnd)
@@ -1052,32 +1159,124 @@ struct AAXClasses
{
const JUCEAlgorithmContext& i = **iter;
i.pluginInstance->parameters.process (i.inputChannels, i.outputChannels,
int sideChainBufferIdx = i.pluginInstance->parameters.supportsSidechain() && i.sideChainBuffers != nullptr
? static_cast<int> (*i.sideChainBuffers)
: -1;
i.pluginInstance->parameters.process (i.inputChannels, i.outputChannels, sideChainBufferIdx,
*(i.bufferSize), *(i.bypass) != 0,
getMidiNodeIn(i), getMidiNodeOut(i));
}
}
static bool enableAuxBusesForCurrentFormat (PluginBusUtilities& busUtils, const AudioChannelSet& inputLayout,
const AudioChannelSet& outputLayout)
{
const int numOutBuses = busUtils.getBusCount (false);
const int numInputBuses = busUtils.getBusCount(true);
if (numOutBuses > 1)
{
PluginBusUtilities::ScopedBusRestorer layoutRestorer (busUtils);
// enable all possible output buses
for (int busIdx = 1; busIdx < busUtils.getBusCount (false); ++busIdx)
{
AudioChannelSet layout = busUtils.getChannelSet (false, busIdx);
// bus disabled by default? try to enable it with the default layout
if (layout == AudioChannelSet::disabled())
{
layout = busUtils.getDefaultLayoutForBus (false, busIdx);
busUtils.processor.setPreferredBusArrangement (false, busIdx, layout);
}
}
// changing output buses may have changed main bus layout
bool success = true;
if (numInputBuses > 0)
success = busUtils.processor.setPreferredBusArrangement (true, 0, inputLayout);
if (success)
success = busUtils.processor.setPreferredBusArrangement (false, 0, outputLayout);
// was the above successful
if (success && (numInputBuses == 0 || busUtils.getChannelSet (true, 0) == inputLayout)
&& busUtils.getChannelSet (false, 0) == outputLayout)
layoutRestorer.release();
}
// does the plug-in have side-chain support? Check the following:
// 1) does it have an input bus with index = 1 which supports mono
// 2) can all other input buses be disabled
// 3) does the format of the main buses not change when enabling the first bus
if (numInputBuses > 1)
{
bool success = true;
bool hasSidechain = false;
if (const AudioChannelSet* set = busUtils.getSupportedBusLayouts (true, 1).getDefaultLayoutForChannelNum (1))
hasSidechain = busUtils.processor.setPreferredBusArrangement (true, 1, *set);
if (! hasSidechain)
success = busUtils.processor.setPreferredBusArrangement (true, 1, AudioChannelSet::disabled());
// AAX requires your processor's first sidechain to be either mono or that
// it can be disabled
jassert(success);
// disable all other input buses
for (int busIdx = 2; busIdx < numInputBuses; ++busIdx)
{
success = busUtils.processor.setPreferredBusArrangement (true, busIdx, AudioChannelSet::disabled());
// AAX can only have a single side-chain input. Therefore, your processor must either
// only have a single side-chain input or allow disabling all other side-chains
jassert (success);
}
if (hasSidechain)
{
if (busUtils.getBusCount (false) == 0 || busUtils.getBusCount (true) == 0 ||
(busUtils.getChannelSet (true, 0) == inputLayout && busUtils.getChannelSet (false, 0) == outputLayout))
return true;
// restore the old layout
if (busUtils.getBusCount(true) > 0)
busUtils.processor.setPreferredBusArrangement (true, 0, inputLayout);
if (busUtils.getBusCount (false) > 0)
busUtils.processor.setPreferredBusArrangement (false, 0, outputLayout);
}
}
return false;
}
//==============================================================================
static void createDescriptor (AAX_IComponentDescriptor& desc, int channelConfigIndex,
int numInputs, int numOutputs)
static void createDescriptor (AAX_IComponentDescriptor& desc, int configIndex, PluginBusUtilities& busUtils,
const AudioChannelSet& inputLayout, const AudioChannelSet& outputLayout,
const AAX_EStemFormat aaxInputFormat, const AAX_EStemFormat aaxOutputFormat)
{
check (desc.AddAudioIn (JUCEAlgorithmIDs::inputChannels));
check (desc.AddAudioOut (JUCEAlgorithmIDs::outputChannels));
check (desc.AddAudioBufferLength (JUCEAlgorithmIDs::bufferSize));
check (desc.AddDataInPort (JUCEAlgorithmIDs::bypass, sizeof (int32_t)));
#if JucePlugin_WantsMidiInput
#if JucePlugin_WantsMidiInput || JucePlugin_IsMidiEffect
check (desc.AddMIDINode (JUCEAlgorithmIDs::midiNodeIn, AAX_eMIDINodeType_LocalInput,
JucePlugin_Name, 0xffff));
#endif
#if JucePlugin_ProducesMidiOutput
#if JucePlugin_ProducesMidiOutput || JucePlugin_IsSynth || JucePlugin_IsMidiEffect
check (desc.AddMIDINode (JUCEAlgorithmIDs::midiNodeOut, AAX_eMIDINodeType_LocalOutput,
JucePlugin_Name " Out", 0xffff));
#endif
check (desc.AddPrivateData (JUCEAlgorithmIDs::pluginInstance, sizeof (PluginInstanceInfo)));
check (desc.AddPrivateData (JUCEAlgorithmIDs::preparedFlag, sizeof (int32_t)));
// Create a property map
AAX_IPropertyMap* const properties = desc.NewPropertyMap();
@@ -1092,15 +1291,15 @@ struct AAXClasses
properties->AddProperty (AAX_eProperty_CanBypass, true);
#endif
properties->AddProperty (AAX_eProperty_InputStemFormat, getFormatForChans (numInputs));
properties->AddProperty (AAX_eProperty_OutputStemFormat, getFormatForChans (numOutputs));
properties->AddProperty (AAX_eProperty_InputStemFormat, static_cast<AAX_CPropertyValue> (aaxInputFormat));
properties->AddProperty (AAX_eProperty_OutputStemFormat, static_cast<AAX_CPropertyValue> (aaxOutputFormat));
// This value needs to match the RTAS wrapper's Type ID, so that
// the host knows that the RTAS/AAX plugins are equivalent.
properties->AddProperty (AAX_eProperty_PlugInID_Native, 'jcaa' + channelConfigIndex);
properties->AddProperty (AAX_eProperty_PlugInID_Native, 'jcaa' + configIndex);
#if ! JucePlugin_AAXDisableAudioSuite
properties->AddProperty (AAX_eProperty_PlugInID_AudioSuite, 'jyaa' + channelConfigIndex);
properties->AddProperty (AAX_eProperty_PlugInID_AudioSuite, 'jyaa' + configIndex);
#endif
#if JucePlugin_AAXDisableMultiMono
@@ -1109,50 +1308,137 @@ struct AAXClasses
properties->AddProperty (AAX_eProperty_Constraint_MultiMonoSupport, true);
#endif
if (enableAuxBusesForCurrentFormat (busUtils, inputLayout, outputLayout))
{
check (desc.AddSideChainIn (JUCEAlgorithmIDs::sideChainBuffers));
properties->AddProperty (AAX_eProperty_SupportsSideChainInput, true);
}
// add the output buses
// This is incrdibly dumb: the output bus format must be well defined
// for every main bus in/out format pair. This means that there cannot
// be two configurations with different aux formats but
// identical main bus in/out formats.
for (int busIdx = 1; busIdx < busUtils.getBusCount (false); ++busIdx)
{
AudioChannelSet outBusLayout = busUtils.getChannelSet (false, busIdx);
if (outBusLayout != AudioChannelSet::disabled())
{
AAX_EStemFormat auxFormat = getFormatForAudioChannelSet (outBusLayout, busUtils.busIgnoresLayout (false, busIdx));
if (auxFormat != AAX_eStemFormat_INT32_MAX && auxFormat != AAX_eStemFormat_None)
{
const String& name = busUtils.processor.busArrangement.outputBuses.getReference (busIdx).name;
check (desc.AddAuxOutputStem (0, static_cast<int32_t> (auxFormat), name.toRawUTF8()));
}
}
}
// this assertion should be covered by the assertions above
// if not please report a bug
jassert (busUtils.getNumEnabledBuses (true) <= 2);
check (desc.AddProcessProc_Native (algorithmProcessCallback, properties));
}
static void getPlugInDescription (AAX_IEffectDescriptor& descriptor)
{
ScopedPointer<AudioProcessor> plugin = createPluginFilterOfType (AudioProcessor::wrapperType_AAX);
PluginBusUtilities busUtils (*plugin, false);
busUtils.findAllCompatibleLayouts();
descriptor.AddName (JucePlugin_Desc);
descriptor.AddName (JucePlugin_Name);
descriptor.AddCategory (JucePlugin_AAXCategory);
#ifdef JucePlugin_AAXPageTableFile
// optional page table setting - define this macro in your AppConfig.h if you
// want to set this value - see Avid documentation for details about its format.
// optional page table setting - define this macro in your project if you want
// to set this value - see Avid documentation for details about its format.
descriptor.AddResourceInfo (AAX_eResourceType_PageTable, JucePlugin_AAXPageTableFile);
#endif
check (descriptor.AddProcPtr ((void*) JuceAAX_GUI::Create, kAAX_ProcPtrID_Create_EffectGUI));
check (descriptor.AddProcPtr ((void*) JuceAAX_Processor::Create, kAAX_ProcPtrID_Create_EffectParameters));
#ifdef JucePlugin_PreferredChannelConfigurations_AAX
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations_AAX };
#else
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
#endif
SortedSet<AAXFormatConfiguration> aaxFormats;
SortedSet<AudioChannelSet> inLayouts = busUtils.getBusCount (true) > 0 ? busUtils.getSupportedBusLayouts (true, 0).supportedLayouts : SortedSet<AudioChannelSet>();
SortedSet<AudioChannelSet> outLayouts = busUtils.getBusCount (false) > 0 ? busUtils.getSupportedBusLayouts (false, 0).supportedLayouts : SortedSet<AudioChannelSet>();
const int numConfigs = numElementsInArray (channelConfigs);
const int numIns = inLayouts. size();
const int numOuts = outLayouts.size();
#if JucePlugin_IsMidiEffect
// MIDI effect plug-ins do not support any audio channels
jassert (numIns == 0 && numOuts == 0);
if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
{
createDescriptor (*desc, 0, busUtils,
AudioChannelSet::disabled(), AudioChannelSet::disabled(),
AAX_eStemFormat_Mono, AAX_eStemFormat_Mono);
check (descriptor.AddComponent (desc));
}
// You need to actually add some configurations to the JucePlugin_PreferredChannelConfigurations
// value in your JucePluginCharacteristics.h file..
jassert (numConfigs > 0);
#else
int configIndex = 0;
for (int i = 0; i < numConfigs; ++i)
for (int inIdx = 0; inIdx < jmax (numIns, 1); ++inIdx)
{
if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
for (int outIdx = 0; outIdx < jmax (numOuts, 1); ++outIdx)
{
const int numIns = channelConfigs [i][0];
const int numOuts = channelConfigs [i][1];
bool success = true;
if (numIns > 0)
success = busUtils.processor.setPreferredBusArrangement (true, 0, inLayouts.getReference (inIdx));
if (numOuts > 0 && success)
success = busUtils.processor.setPreferredBusArrangement (false, 0, outLayouts.getReference (outIdx));
// We should never hit this assertion: PluginBusUtilities reported this as supported.
// Please report this as a bug!
jassert (success);
AudioChannelSet inLayout = numIns > 0 ? busUtils.getChannelSet (true, 0) : AudioChannelSet();
AudioChannelSet outLayout = numOuts > 0 ? busUtils.getChannelSet (false, 0) : AudioChannelSet();
// if we can't set both in AND out formats simultaneously then ignore this format!
if (numIns > 0 && numOuts > 0 && (inLayout != inLayouts.getReference (inIdx) || (outLayout != outLayouts.getReference (outIdx))))
continue;
AAX_EStemFormat aaxInFormat = getFormatForAudioChannelSet (inLayout, busUtils.busIgnoresLayout (true, 0));
AAX_EStemFormat aaxOutFormat = getFormatForAudioChannelSet (outLayout, busUtils.busIgnoresLayout (false, 0));
// does AAX support this layout?
if (aaxInFormat == AAX_eStemFormat_INT32_MAX || aaxOutFormat == AAX_eStemFormat_INT32_MAX)
continue;
if (numIns <= 8 && numOuts <= 8) // AAX doesn't seem to handle more than 8 chans
// AAX requires a single input if this plug-in is a synth
#if JucePlugin_IsSynth
if (numIns == 0)
aaxInFormat = aaxOutFormat;
#endif
if (aaxInFormat == AAX_eStemFormat_None && aaxOutFormat == AAX_eStemFormat_None)
continue;
AAXFormatConfiguration aaxFormat (aaxInFormat, aaxOutFormat);
if (aaxFormats.indexOf (aaxFormat) < 0)
{
createDescriptor (*desc, i, numIns, numOuts);
check (descriptor.AddComponent (desc));
aaxFormats.add (aaxFormat);
if (AAX_IComponentDescriptor* const desc = descriptor.NewComponentDescriptor())
{
createDescriptor (*desc, configIndex++, busUtils, inLayout, outLayout, aaxInFormat, aaxOutFormat);
check (descriptor.AddComponent (desc));
}
}
}
}
// You don't have any supported layouts
jassert (configIndex > 0);
#endif
}
};


+ 3
- 3
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUBase.cpp View File

@@ -1450,14 +1450,14 @@ OSStatus AUBase::DoRender( AudioUnitRenderActionFlags & ioActionFlags,
}
ca_require (!UsesFixedBlockSize() || inFramesToProcess == GetMaxFramesPerSlice(), ParamErr);
AUOutputElement *output = GetOutput(inBusNumber); // will throw if non-existant
if (output->GetStreamFormat().NumberChannelStreams() != ioData.mNumberBuffers) {
AUOutputElement *output = GetScope(kAudioUnitScope_Output).GetNumberOfElements() > 0 ? GetOutput(inBusNumber) : NULL; // will throw if non-existant
if (output != NULL && output->GetStreamFormat().NumberChannelStreams() != ioData.mNumberBuffers) {
DebugMessageN4("%s:%d ioData.mNumberBuffers=%u, output->GetStreamFormat().NumberChannelStreams()=%u; kAudio_ParamError",
__FILE__, __LINE__, (unsigned)ioData.mNumberBuffers, (unsigned)output->GetStreamFormat().NumberChannelStreams());
goto ParamErr;
}
unsigned expectedBufferByteSize = inFramesToProcess * output->GetStreamFormat().mBytesPerFrame;
unsigned expectedBufferByteSize = output != NULL ? inFramesToProcess * output->GetStreamFormat().mBytesPerFrame : 0;
for (unsigned ibuf = 0; ibuf < ioData.mNumberBuffers; ++ibuf) {
AudioBuffer &buf = ioData.mBuffers[ibuf];
if (buf.mData != NULL) {


+ 12
- 7
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUBase.h View File

@@ -50,6 +50,7 @@
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include <TargetConditionals.h>
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#if TARGET_OS_MAC
#include <pthread.h>
@@ -802,14 +803,18 @@ private:
UInt32 inNumberFrames,
AudioBufferList & ioData)
{
if (ioData.mBuffers[0].mData == NULL || (theOutput->WillAllocateBuffer() && Outputs().GetNumberOfElements() > 1))
// will render into cache buffer
theOutput->PrepareBuffer(inNumberFrames);
else
// will render into caller's buffer
theOutput->SetBufferList(ioData);
if (theOutput != NULL)
{
if (ioData.mBuffers[0].mData == NULL || (theOutput->WillAllocateBuffer() && Outputs().GetNumberOfElements() > 1))
// will render into cache buffer
theOutput->PrepareBuffer(inNumberFrames);
else
// will render into caller's buffer
theOutput->SetBufferList(ioData);
}
OSStatus result = RenderBus(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames);
if (result == noErr) {
if (result == noErr && theOutput != NULL) {
if (ioData.mBuffers[0].mData == NULL) {
theOutput->CopyBufferListTo(ioData);
AUTRACE(kCATrace_AUBaseDoRenderBus, mComponentInstance, inNumberFrames, (intptr_t)theOutput->GetBufferList().mBuffers[0].mData, 0, *(UInt32 *)ioData.mBuffers[0].mData);


+ 2
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUBuffer.h View File

@@ -47,6 +47,8 @@
#ifndef __AUBuffer_h__
#define __AUBuffer_h__
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#include <TargetConditionals.h>
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <AudioUnit/AudioUnit.h>


+ 1
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUCarbonViewBase.h View File

@@ -48,6 +48,7 @@
#define __AUCarbonViewBase_h__
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#include <vector>
#include "AUCarbonViewControl.h"


+ 1
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUCarbonViewControl.h View File

@@ -47,6 +47,7 @@
#ifndef __AUCarbonViewControl_h__
#define __AUCarbonViewControl_h__
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#include <Carbon/Carbon.h>
#include <AudioUnit/AudioUnitCarbonView.h>
#include <AudioToolbox/AudioUnitUtilities.h>


+ 0
- 465
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUEffectBase.cpp View File

@@ -1,465 +0,0 @@
/*
File: AUEffectBase.cpp
Abstract: AUEffectBase.h
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#include "AUEffectBase.h"
/*
This class does not deal as well as it should with N-M effects...
The problem areas are (if the channels don't match):
ProcessInPlace if the channels don't match - there will be problems if InputChan != OutputChan
Bypass - its just passing the buffers through when not processing them
This will be fixed in a future update...
*/
//_____________________________________________________________________________
//
AUEffectBase::AUEffectBase( AudioComponentInstance audioUnit,
bool inProcessesInPlace ) :
AUBase(audioUnit, 1, 1), // 1 in bus, 1 out bus
mBypassEffect(false),
mParamSRDep (false),
mProcessesInPlace(inProcessesInPlace),
mMainOutput(NULL), mMainInput(NULL)
#if TARGET_OS_IPHONE
, mOnlyOneKernel(false)
#endif
{
}
//_____________________________________________________________________________
//
AUEffectBase::~AUEffectBase()
{
Cleanup();
}
//_____________________________________________________________________________
//
void AUEffectBase::Cleanup()
{
for (KernelList::iterator it = mKernelList.begin(); it != mKernelList.end(); ++it)
delete *it;
mKernelList.clear();
mMainOutput = NULL;
mMainInput = NULL;
}
//_____________________________________________________________________________
//
OSStatus AUEffectBase::Initialize()
{
// get our current numChannels for input and output
SInt16 auNumInputs = (SInt16) GetInput(0)->GetStreamFormat().mChannelsPerFrame;
SInt16 auNumOutputs = (SInt16) GetOutput(0)->GetStreamFormat().mChannelsPerFrame;
// does the unit publish specific information about channel configurations?
const AUChannelInfo *auChannelConfigs = NULL;
UInt32 numIOconfigs = SupportedNumChannels(&auChannelConfigs);
if ((numIOconfigs > 0) && (auChannelConfigs != NULL))
{
bool foundMatch = false;
for (UInt32 i = 0; (i < numIOconfigs) && !foundMatch; ++i)
{
SInt16 configNumInputs = auChannelConfigs[i].inChannels;
SInt16 configNumOutputs = auChannelConfigs[i].outChannels;
if ((configNumInputs < 0) && (configNumOutputs < 0))
{
// unit accepts any number of channels on input and output
if (((configNumInputs == -1) && (configNumOutputs == -2))
|| ((configNumInputs == -2) && (configNumOutputs == -1)))
{
foundMatch = true;
// unit accepts any number of channels on input and output IFF they are the same number on both scopes
}
else if (((configNumInputs == -1) && (configNumOutputs == -1)) && (auNumInputs == auNumOutputs))
{
foundMatch = true;
// unit has specified a particular number of channels on both scopes
}
else
continue;
}
else
{
// the -1 case on either scope is saying that the unit doesn't care about the
// number of channels on that scope
bool inputMatch = (auNumInputs == configNumInputs) || (configNumInputs == -1);
bool outputMatch = (auNumOutputs == configNumOutputs) || (configNumOutputs == -1);
if (inputMatch && outputMatch)
foundMatch = true;
}
}
if (!foundMatch)
return kAudioUnitErr_FormatNotSupported;
}
else
{
// there is no specifically published channel info
// so for those kinds of effects, the assumption is that the channels (whatever their number)
// should match on both scopes
if ((auNumOutputs != auNumInputs) || (auNumOutputs == 0))
{
return kAudioUnitErr_FormatNotSupported;
}
}
MaintainKernels();
mMainOutput = GetOutput(0);
mMainInput = GetInput(0);
const CAStreamBasicDescription& format = GetStreamFormat(kAudioUnitScope_Output, 0);
format.IdentifyCommonPCMFormat(mCommonPCMFormat, NULL);
mBytesPerFrame = format.mBytesPerFrame;
return noErr;
}
OSStatus AUEffectBase::Reset( AudioUnitScope inScope,
AudioUnitElement inElement)
{
for (KernelList::iterator it = mKernelList.begin(); it != mKernelList.end(); ++it) {
AUKernelBase *kernel = *it;
if (kernel != NULL)
kernel->Reset();
}
return AUBase::Reset(inScope, inElement);
}
OSStatus AUEffectBase::GetPropertyInfo (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
UInt32 & outDataSize,
Boolean & outWritable)
{
if (inScope == kAudioUnitScope_Global) {
switch (inID) {
case kAudioUnitProperty_BypassEffect:
outWritable = true;
outDataSize = sizeof (UInt32);
return noErr;
case kAudioUnitProperty_InPlaceProcessing:
outWritable = true;
outDataSize = sizeof (UInt32);
return noErr;
}
}
return AUBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
}
OSStatus AUEffectBase::GetProperty (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
void * outData)
{
if (inScope == kAudioUnitScope_Global) {
switch (inID) {
case kAudioUnitProperty_BypassEffect:
*((UInt32*)outData) = (IsBypassEffect() ? 1 : 0);
return noErr;
case kAudioUnitProperty_InPlaceProcessing:
*((UInt32*)outData) = (mProcessesInPlace ? 1 : 0);
return noErr;
}
}
return AUBase::GetProperty (inID, inScope, inElement, outData);
}
OSStatus AUEffectBase::SetProperty( AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
const void * inData,
UInt32 inDataSize)
{
if (inScope == kAudioUnitScope_Global) {
switch (inID) {
case kAudioUnitProperty_BypassEffect:
{
if (inDataSize < sizeof(UInt32))
return kAudioUnitErr_InvalidPropertyValue;
bool tempNewSetting = *((UInt32*)inData) != 0;
// we're changing the state of bypass
if (tempNewSetting != IsBypassEffect())
{
if (!tempNewSetting && IsBypassEffect() && IsInitialized()) // turning bypass off and we're initialized
Reset(0, 0);
SetBypassEffect (tempNewSetting);
}
return noErr;
}
case kAudioUnitProperty_InPlaceProcessing:
mProcessesInPlace = (*((UInt32*)inData) != 0);
return noErr;
}
}
return AUBase::SetProperty (inID, inScope, inElement, inData, inDataSize);
}
void AUEffectBase::MaintainKernels()
{
#if TARGET_OS_IPHONE
UInt32 nKernels = mOnlyOneKernel ? 1 : GetNumberOfChannels();
#else
UInt32 nKernels = GetNumberOfChannels();
#endif
if (mKernelList.size() < nKernels) {
mKernelList.reserve(nKernels);
for (UInt32 i = (UInt32)mKernelList.size(); i < nKernels; ++i)
mKernelList.push_back(NewKernel());
} else {
while (mKernelList.size() > nKernels) {
AUKernelBase *kernel = mKernelList.back();
delete kernel;
mKernelList.pop_back();
}
}
for(unsigned int i = 0; i < nKernels; i++ )
{
if(mKernelList[i]) {
mKernelList[i]->SetChannelNum (i);
}
}
}
bool AUEffectBase::StreamFormatWritable( AudioUnitScope scope,
AudioUnitElement element)
{
return IsInitialized() ? false : true;
}
OSStatus AUEffectBase::ChangeStreamFormat( AudioUnitScope inScope,
AudioUnitElement inElement,
const CAStreamBasicDescription & inPrevFormat,
const CAStreamBasicDescription & inNewFormat)
{
OSStatus result = AUBase::ChangeStreamFormat(inScope, inElement, inPrevFormat, inNewFormat);
if (result == noErr)
{
// for the moment this only dependency we know about
// where a parameter's range may change is with the sample rate
// and effects are only publishing parameters in the global scope!
if (GetParamHasSampleRateDependency() && fnotequal(inPrevFormat.mSampleRate, inNewFormat.mSampleRate))
PropertyChanged(kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0);
}
return result;
}
// ____________________________________________________________________________
//
// This method is called (potentially repeatedly) by ProcessForScheduledParams()
// in order to perform the actual DSP required for this portion of the entire buffer
// being processed. The entire buffer can be divided up into smaller "slices"
// according to the timestamps on the scheduled parameters...
//
OSStatus AUEffectBase::ProcessScheduledSlice( void *inUserData,
UInt32 inStartFrameInBuffer,
UInt32 inSliceFramesToProcess,
UInt32 inTotalBufferFrames )
{
ScheduledProcessParams &sliceParams = *((ScheduledProcessParams*)inUserData);
AudioUnitRenderActionFlags &actionFlags = *sliceParams.actionFlags;
AudioBufferList &inputBufferList = *sliceParams.inputBufferList;
AudioBufferList &outputBufferList = *sliceParams.outputBufferList;
UInt32 channelSize = inSliceFramesToProcess * mBytesPerFrame;
// fix the size of the buffer we're operating on before we render this slice of time
for(unsigned int i = 0; i < inputBufferList.mNumberBuffers; i++ ) {
inputBufferList.mBuffers[i].mDataByteSize = inputBufferList.mBuffers[i].mNumberChannels * channelSize;
}
for(unsigned int i = 0; i < outputBufferList.mNumberBuffers; i++ ) {
outputBufferList.mBuffers[i].mDataByteSize = outputBufferList.mBuffers[i].mNumberChannels * channelSize;
}
// process the buffer
OSStatus result = ProcessBufferLists(actionFlags, inputBufferList, outputBufferList, inSliceFramesToProcess );
// we just partially processed the buffers, so increment the data pointers to the next part of the buffer to process
for(unsigned int i = 0; i < inputBufferList.mNumberBuffers; i++ ) {
inputBufferList.mBuffers[i].mData =
(char *)inputBufferList.mBuffers[i].mData + inputBufferList.mBuffers[i].mNumberChannels * channelSize;
}
for(unsigned int i = 0; i < outputBufferList.mNumberBuffers; i++ ) {
outputBufferList.mBuffers[i].mData =
(char *)outputBufferList.mBuffers[i].mData + outputBufferList.mBuffers[i].mNumberChannels * channelSize;
}
return result;
}
// ____________________________________________________________________________
//
OSStatus AUEffectBase::Render( AudioUnitRenderActionFlags &ioActionFlags,
const AudioTimeStamp & inTimeStamp,
UInt32 nFrames)
{
if (!HasInput(0))
return kAudioUnitErr_NoConnection;
OSStatus result = noErr;
result = mMainInput->PullInput(ioActionFlags, inTimeStamp, 0 /* element */, nFrames);
if (result == noErr)
{
if(ProcessesInPlace() && mMainOutput->WillAllocateBuffer())
{
mMainOutput->SetBufferList(mMainInput->GetBufferList() );
}
if (ShouldBypassEffect())
{
// leave silence bit alone
if(!ProcessesInPlace() )
{
mMainInput->CopyBufferContentsTo (mMainOutput->GetBufferList());
}
}
else
{
if(mParamList.size() == 0 )
{
// this will read/write silence bit
result = ProcessBufferLists(ioActionFlags, mMainInput->GetBufferList(), mMainOutput->GetBufferList(), nFrames);
}
else
{
// deal with scheduled parameters...
AudioBufferList &inputBufferList = mMainInput->GetBufferList();
AudioBufferList &outputBufferList = mMainOutput->GetBufferList();
ScheduledProcessParams processParams;
processParams.actionFlags = &ioActionFlags;
processParams.inputBufferList = &inputBufferList;
processParams.outputBufferList = &outputBufferList;
// divide up the buffer into slices according to scheduled params then
// do the DSP for each slice (ProcessScheduledSlice() called for each slice)
result = ProcessForScheduledParams( mParamList,
nFrames,
&processParams );
// fixup the buffer pointers to how they were before we started
UInt32 channelSize = nFrames * mBytesPerFrame;
for(unsigned int i = 0; i < inputBufferList.mNumberBuffers; i++ ) {
UInt32 size = inputBufferList.mBuffers[i].mNumberChannels * channelSize;
inputBufferList.mBuffers[i].mData = (char *)inputBufferList.mBuffers[i].mData - size;
inputBufferList.mBuffers[i].mDataByteSize = size;
}
for(unsigned int i = 0; i < outputBufferList.mNumberBuffers; i++ ) {
UInt32 size = outputBufferList.mBuffers[i].mNumberChannels * channelSize;
outputBufferList.mBuffers[i].mData = (char *)outputBufferList.mBuffers[i].mData - size;
outputBufferList.mBuffers[i].mDataByteSize = size;
}
}
}
if ( (ioActionFlags & kAudioUnitRenderAction_OutputIsSilence) && !ProcessesInPlace() )
{
AUBufferList::ZeroBuffer(mMainOutput->GetBufferList() );
}
}
return result;
}
OSStatus AUEffectBase::ProcessBufferLists(
AudioUnitRenderActionFlags & ioActionFlags,
const AudioBufferList & inBuffer,
AudioBufferList & outBuffer,
UInt32 inFramesToProcess )
{
if (ShouldBypassEffect())
return noErr;
// interleaved (or mono)
switch (mCommonPCMFormat) {
case CAStreamBasicDescription::kPCMFormatFloat32 :
ProcessBufferListsT<Float32>(ioActionFlags, inBuffer, outBuffer, inFramesToProcess);
break;
case CAStreamBasicDescription::kPCMFormatFixed824 :
ProcessBufferListsT<SInt32>(ioActionFlags, inBuffer, outBuffer, inFramesToProcess);
break;
case CAStreamBasicDescription::kPCMFormatInt16 :
ProcessBufferListsT<SInt16>(ioActionFlags, inBuffer, outBuffer, inFramesToProcess);
break;
default :
throw CAException(kAudio_UnimplementedError);
}
return noErr;
}
Float64 AUEffectBase::GetSampleRate()
{
return GetOutput(0)->GetStreamFormat().mSampleRate;
}
UInt32 AUEffectBase::GetNumberOfChannels()
{
return GetOutput(0)->GetStreamFormat().mChannelsPerFrame;
}

+ 0
- 377
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUEffectBase.h View File

@@ -1,377 +0,0 @@
/*
File: AUEffectBase.h
Abstract: Part of CoreAudio Utility Classes
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#ifndef __AUEffectBase_h__
#define __AUEffectBase_h__
#include "AUBase.h"
#include "AUSilentTimeout.h"
#include "CAException.h"
class AUKernelBase;
// Base class for an effect with one input stream, one output stream,
// any number of channels.
/*! @class AUEffectBase */
class AUEffectBase : public AUBase {
public:
/*! @ctor AUEffectBase */
AUEffectBase( AudioComponentInstance audioUnit,
bool inProcessesInPlace = true );
/*! @dtor ~AUEffectBase */
~AUEffectBase();
/*! @method Initialize */
virtual OSStatus Initialize();
/*! @method Cleanup */
virtual void Cleanup();
/*! @method Reset */
virtual OSStatus Reset( AudioUnitScope inScope,
AudioUnitElement inElement);
/*! @method GetPropertyInfo */
virtual OSStatus GetPropertyInfo (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
UInt32 & outDataSize,
Boolean & outWritable);
/*! @method GetProperty */
virtual OSStatus GetProperty (AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
void * outData);
/*! @method SetProperty */
virtual OSStatus SetProperty(AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
const void * inData,
UInt32 inDataSize);
/*! @method StreamFormatWritable */
virtual bool StreamFormatWritable (AudioUnitScope scope,
AudioUnitElement element);
/*! @method ChangeStreamFormat */
virtual OSStatus ChangeStreamFormat (
AudioUnitScope inScope,
AudioUnitElement inElement,
const CAStreamBasicDescription & inPrevFormat,
const CAStreamBasicDescription & inNewFormat);
/*! @method Render */
virtual OSStatus Render(AudioUnitRenderActionFlags & ioActionFlags,
const AudioTimeStamp & inTimeStamp,
UInt32 inNumberFrames);
// our virtual methods
// If your unit processes N to N channels, and there are no interactions between channels,
// it can override NewKernel to create a mono processing object per channel. Otherwise,
// don't override NewKernel, and instead, override ProcessBufferLists.
/*! @method NewKernel */
virtual AUKernelBase * NewKernel() { return NULL; }
/*! @method ProcessBufferLists */
virtual OSStatus ProcessBufferLists(
AudioUnitRenderActionFlags & ioActionFlags,
const AudioBufferList & inBuffer,
AudioBufferList & outBuffer,
UInt32 inFramesToProcess );
// convenience format accessors (use output 0's format)
/*! @method GetSampleRate */
Float64 GetSampleRate();
/*! @method GetNumberOfChannels */
UInt32 GetNumberOfChannels();
// convenience wrappers for accessing parameters in the global scope
/*! @method SetParameter */
using AUBase::SetParameter;
void SetParameter( AudioUnitParameterID paramID,
AudioUnitParameterValue value)
{
Globals()->SetParameter(paramID, value);
}
/*! @method GetParameter */
using AUBase::GetParameter;
AudioUnitParameterValue GetParameter( AudioUnitParameterID paramID )
{
return Globals()->GetParameter(paramID );
}
/*! @method CanScheduleParameters */
virtual bool CanScheduleParameters() const { return true; }
/*! @method IsBypassEffect */
// This is used for the property value - to reflect to the UI if an effect is bypassed
bool IsBypassEffect () { return mBypassEffect; }
protected:
/*! @method MaintainKernels */
void MaintainKernels();
/*! @method ShouldBypassEffect */
// This is used in the render call to see if an effect is bypassed
// It can return a different status than IsBypassEffect (though it MUST take that into account)
virtual bool ShouldBypassEffect () { return IsBypassEffect(); }
public:
/*! @method SetBypassEffect */
virtual void SetBypassEffect (bool inFlag) { mBypassEffect = inFlag; }
/*! @method SetParamHasSampleRateDependency */
void SetParamHasSampleRateDependency (bool inFlag)
{
mParamSRDep = inFlag;
}
/*! @method GetParamHasSampleRateDependency */
bool GetParamHasSampleRateDependency () const { return mParamSRDep; }
struct ScheduledProcessParams // pointer passed in as void* userData param for ProcessScheduledSlice()
{
AudioUnitRenderActionFlags *actionFlags;
AudioBufferList *inputBufferList;
AudioBufferList *outputBufferList;
};
virtual OSStatus ProcessScheduledSlice( void *inUserData,
UInt32 inStartFrameInBuffer,
UInt32 inSliceFramesToProcess,
UInt32 inTotalBufferFrames );
bool ProcessesInPlace() const {return mProcessesInPlace;};
void SetProcessesInPlace(bool inProcessesInPlace) {mProcessesInPlace = inProcessesInPlace;};
typedef std::vector<AUKernelBase *> KernelList;
protected:
/*! @var mKernelList */
KernelList mKernelList;
AUKernelBase* GetKernel(UInt32 index) { return mKernelList[index]; }
/*! @method IsInputSilent */
bool IsInputSilent (AudioUnitRenderActionFlags inActionFlags, UInt32 inFramesToProcess)
{
bool inputSilent = (inActionFlags & kAudioUnitRenderAction_OutputIsSilence) != 0;
// take latency and tail time into account when propagating the silent bit
UInt32 silentTimeoutFrames = UInt32(GetSampleRate() * (GetLatency() + GetTailTime()));
mSilentTimeout.Process (inFramesToProcess, silentTimeoutFrames, inputSilent);
return inputSilent;
}
#if TARGET_OS_IPHONE
void SetOnlyOneKernel(bool inUseOnlyOneKernel) { mOnlyOneKernel = inUseOnlyOneKernel; } // set in ctor of subclass that wants it.
#endif
template <typename T>
void ProcessBufferListsT(
AudioUnitRenderActionFlags & ioActionFlags,
const AudioBufferList & inBuffer,
AudioBufferList & outBuffer,
UInt32 inFramesToProcess );
CAStreamBasicDescription::CommonPCMFormat GetCommonPCMFormat() const { return mCommonPCMFormat; }
private:
/*! @var mBypassEffect */
bool mBypassEffect;
/*! @var mParamSRDep */
bool mParamSRDep;
/*! @var mProcessesInplace */
bool mProcessesInPlace;
/*! @var mSilentTimeout */
AUSilentTimeout mSilentTimeout;
/*! @var mMainOutput */
AUOutputElement * mMainOutput;
/*! @var mMainInput */
AUInputElement * mMainInput;
#if TARGET_OS_IPHONE
/*! @var mOnlyOneKernel */
bool mOnlyOneKernel;
#endif
/*! @var mCommonPCMFormat */
CAStreamBasicDescription::CommonPCMFormat mCommonPCMFormat;
UInt32 mBytesPerFrame;
};
// Base class for a "kernel", an object that performs DSP on one channel of an interleaved stream.
/*! @class AUKernelBase */
class AUKernelBase {
public:
/*! @ctor AUKernelBase */
AUKernelBase(AUEffectBase *inAudioUnit ) :
mAudioUnit(inAudioUnit) { }
/*! @dtor ~AUKernelBase */
virtual ~AUKernelBase() { }
/*! @method Reset */
virtual void Reset() { }
/*! @method Process */
virtual void Process( const Float32 * inSourceP,
Float32 * inDestP,
UInt32 inFramesToProcess,
UInt32 inNumChannels,
bool & ioSilence) { throw CAException(kAudio_UnimplementedError ); }
/*! @method Process */
virtual void Process( const SInt32 * inSourceP,
SInt32 * inDestP,
UInt32 inFramesToProcess,
UInt32 inNumChannels,
bool & ioSilence) { throw CAException(kAudio_UnimplementedError ); }
/*! @method Process */
virtual void Process( const SInt16 * inSourceP,
SInt16 * inDestP,
UInt32 inFramesToProcess,
UInt32 inNumChannels,
bool & ioSilence) { throw CAException(kAudio_UnimplementedError ); }
/*! @method GetSampleRate */
Float64 GetSampleRate()
{
return mAudioUnit->GetSampleRate();
}
/*! @method GetParameter */
AudioUnitParameterValue GetParameter (AudioUnitParameterID paramID)
{
return mAudioUnit->GetParameter(paramID);
}
void SetChannelNum (UInt32 inChan) { mChannelNum = inChan; }
UInt32 GetChannelNum () { return mChannelNum; }
protected:
/*! @var mAudioUnit */
AUEffectBase * mAudioUnit;
UInt32 mChannelNum;
};
template <typename T>
void AUEffectBase::ProcessBufferListsT(
AudioUnitRenderActionFlags & ioActionFlags,
const AudioBufferList & inBuffer,
AudioBufferList & outBuffer,
UInt32 inFramesToProcess )
{
bool ioSilence;
bool silentInput = IsInputSilent (ioActionFlags, inFramesToProcess);
ioActionFlags |= kAudioUnitRenderAction_OutputIsSilence;
// call the kernels to handle either interleaved or deinterleaved
if (inBuffer.mNumberBuffers == 1) {
if (inBuffer.mBuffers[0].mNumberChannels == 0)
throw CAException(kAudio_ParamError);
for (UInt32 channel = 0; channel < mKernelList.size(); ++channel) {
AUKernelBase *kernel = mKernelList[channel];
if (kernel == NULL) continue;
ioSilence = silentInput;
// process each interleaved channel individually
kernel->Process(
(const T *)inBuffer.mBuffers[0].mData + channel,
(T *)outBuffer.mBuffers[0].mData + channel,
inFramesToProcess,
inBuffer.mBuffers[0].mNumberChannels,
ioSilence);
if (!ioSilence)
ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
}
} else {
for (UInt32 channel = 0; channel < mKernelList.size(); ++channel) {
AUKernelBase *kernel = mKernelList[channel];
if (kernel == NULL) continue;
ioSilence = silentInput;
const AudioBuffer *srcBuffer = &inBuffer.mBuffers[channel];
AudioBuffer *destBuffer = &outBuffer.mBuffers[channel];
kernel->Process(
(const T *)srcBuffer->mData,
(T *)destBuffer->mData,
inFramesToProcess,
1,
ioSilence);
if (!ioSilence)
ioActionFlags &= ~kAudioUnitRenderAction_OutputIsSilence;
}
}
}
#endif // __AUEffectBase_h__

+ 0
- 164
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUMIDIEffectBase.cpp View File

@@ -1,164 +0,0 @@
/*
File: AUMIDIEffectBase.cpp
Abstract: AUMIDIEffectBase.h
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#include "AUMIDIEffectBase.h"
// compatibility with older OS SDK releases
typedef OSStatus
(*TEMP_MusicDeviceMIDIEventProc)( void * inComponentStorage,
UInt32 inStatus,
UInt32 inData1,
UInt32 inData2,
UInt32 inOffsetSampleFrame);
static OSStatus AUMIDIEffectBaseMIDIEvent(void * inComponentStorage,
UInt32 inStatus,
UInt32 inData1,
UInt32 inData2,
UInt32 inOffsetSampleFrame);
AUMIDIEffectBase::AUMIDIEffectBase( AudioComponentInstance inInstance,
bool inProcessesInPlace )
: AUEffectBase(inInstance, inProcessesInPlace),
AUMIDIBase(this)
{
}
OSStatus AUMIDIEffectBase::GetPropertyInfo(AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
UInt32 & outDataSize,
Boolean & outWritable)
{
OSStatus result;
result = AUEffectBase::GetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
if (result == kAudioUnitErr_InvalidProperty)
result = AUMIDIBase::DelegateGetPropertyInfo (inID, inScope, inElement, outDataSize, outWritable);
return result;
}
OSStatus AUMIDIEffectBase::GetProperty( AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
void * outData)
{
OSStatus result;
#if !CA_USE_AUDIO_PLUGIN_ONLY
if (inID == kAudioUnitProperty_FastDispatch) {
if (inElement == kMusicDeviceMIDIEventSelect) {
*(TEMP_MusicDeviceMIDIEventProc *)outData = AUMIDIEffectBaseMIDIEvent;
return noErr;
}
return kAudioUnitErr_InvalidElement;
}
#endif
result = AUEffectBase::GetProperty (inID, inScope, inElement, outData);
if (result == kAudioUnitErr_InvalidProperty)
result = AUMIDIBase::DelegateGetProperty (inID, inScope, inElement, outData);
return result;
}
OSStatus AUMIDIEffectBase::SetProperty( AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
const void * inData,
UInt32 inDataSize)
{
OSStatus result = AUEffectBase::SetProperty (inID, inScope, inElement, inData, inDataSize);
if (result == kAudioUnitErr_InvalidProperty)
result = AUMIDIBase::DelegateSetProperty (inID, inScope, inElement, inData, inDataSize);
return result;
}
#if !TARGET_OS_IPHONE
OSStatus AUMIDIEffectBase::ComponentEntryDispatch(ComponentParameters * params,
AUMIDIEffectBase * This)
{
if (This == NULL) return paramErr;
OSStatus result;
switch (params->what) {
case kMusicDeviceMIDIEventSelect:
case kMusicDeviceSysExSelect:
result = AUMIDIBase::ComponentEntryDispatch (params, This);
break;
default:
result = AUEffectBase::ComponentEntryDispatch(params, This);
break;
}
return result;
}
#endif
// fast dispatch
static OSStatus AUMIDIEffectBaseMIDIEvent(void * inComponentStorage,
UInt32 inStatus,
UInt32 inData1,
UInt32 inData2,
UInt32 inOffsetSampleFrame)
{
OSStatus result = noErr;
try {
AUMIDIEffectBase *This = static_cast<AUMIDIEffectBase *>(inComponentStorage);
if (This == NULL) return paramErr;
result = This->AUMIDIBase::MIDIEvent(inStatus, inData1, inData2, inOffsetSampleFrame);
}
COMPONENT_CATCH
return result;
}

+ 0
- 104
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUMIDIEffectBase.h View File

@@ -1,104 +0,0 @@
/*
File: AUMIDIEffectBase.h
Abstract: Part of CoreAudio Utility Classes
Version: 1.1
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#ifndef __AUMIDIEffectBase_h__
#define __AUMIDIEffectBase_h__
#include "AUMIDIBase.h"
#include "AUEffectBase.h"
// ________________________________________________________________________
// AUMIDIEffectBase
//
/*! @class AUMIDIEffectBase */
class AUMIDIEffectBase : public AUEffectBase, public AUMIDIBase {
public:
/*! @ctor AUMIDIEffectBase */
AUMIDIEffectBase( AudioComponentInstance inInstance,
bool inProcessesInPlace = false );
/*! @method MIDIEvent */
virtual OSStatus MIDIEvent(UInt32 inStatus,
UInt32 inData1,
UInt32 inData2,
UInt32 inOffsetSampleFrame)
{
return AUMIDIBase::MIDIEvent (inStatus, inData1, inData2, inOffsetSampleFrame);
}
/*! @method SysEx */
virtual OSStatus SysEx(const UInt8 * inData,
UInt32 inLength)
{
return AUMIDIBase::SysEx (inData, inLength);
}
/*! @method GetPropertyInfo */
virtual OSStatus GetPropertyInfo(AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
UInt32 & outDataSize,
Boolean & outWritable);
/*! @method GetProperty */
virtual OSStatus GetProperty( AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
void * outData);
/*! @method SetProperty */
virtual OSStatus SetProperty( AudioUnitPropertyID inID,
AudioUnitScope inScope,
AudioUnitElement inElement,
const void * inData,
UInt32 inDataSize);
#if !TARGET_OS_IPHONE
// component dispatcher
/*! @method ComponentEntryDispatch */
static OSStatus ComponentEntryDispatch( ComponentParameters * params,
AUMIDIEffectBase * This);
#endif
};
#endif // __AUMIDIEffectBase_h__

+ 2
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUOutputElement.cpp View File

@@ -44,6 +44,8 @@
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#include "AUOutputElement.h"
#include "AUBase.h"


+ 2
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/AUScopeElement.cpp View File

@@ -44,6 +44,8 @@
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#include "AUScopeElement.h"
#include "AUBase.h"


+ 2
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/CAAUParameter.cpp View File

@@ -44,6 +44,8 @@
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#include "CAAUParameter.h"
CAAUParameter::CAAUParameter()


+ 2
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/CADebugMacros.h View File

@@ -48,6 +48,8 @@
#define __CADebugMacros_h__
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
//=============================================================================
// Includes


+ 2
- 0
libs/juce/source/modules/juce_audio_plugin_client/AU/CoreAudioUtilityClasses/CarbonEventHandler.cpp View File

@@ -44,6 +44,8 @@
Copyright (C) 2014 Apple Inc. All Rights Reserved.
*/
#include "../../../juce_core/native/juce_mac_ClangBugWorkaround.h"
#include "CarbonEventHandler.h"
static pascal OSStatus TheEventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData)


+ 989
- 374
libs/juce/source/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm
File diff suppressed because it is too large
View File


+ 1
- 4
libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode1.cpp View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_RTAS


+ 1
- 4
libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode2.cpp View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_RTAS


+ 1
- 4
libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode3.cpp View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_RTAS


+ 2
- 0
libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_DigiCode_Header.h View File

@@ -64,6 +64,8 @@
#define PLUGIN_SDK_DIRECTMIDI 1
#define DIGI_PASCAL
#define Point CarbonDummyPointName
#define Component CarbonDummyCompName
#include <MacAlwaysInclude.h>
#endif


+ 1
- 4
libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_MacUtilities.mm View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_RTAS


+ 1
- 4
libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_WinUtilities.cpp View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
// (these functions are in their own file because of problems including windows.h


+ 12
- 12
libs/juce/source/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_RTAS
@@ -89,6 +86,9 @@
#include <FicProcessTokens.h>
#include <ExternalVersionDefines.h>
#undef Point
#undef Component
//==============================================================================
#ifdef _MSC_VER
#pragma pack (push, 8)
@@ -421,7 +421,7 @@ public:
JuceCustomUIView* getView() const
{
return dynamic_cast <JuceCustomUIView*> (fOurPlugInView);
return dynamic_cast<JuceCustomUIView*> (fOurPlugInView);
}
void GetViewRect (Rect* size) override
@@ -478,11 +478,11 @@ public:
if (MIDILogIn() == noErr)
{
#if JucePlugin_WantsMidiInput
if (CEffectType* const type = dynamic_cast <CEffectType*> (this->GetProcessType()))
if (CEffectType* const type = dynamic_cast<CEffectType*> (this->GetProcessType()))
{
char nodeName [64];
type->GetProcessTypeName (63, nodeName);
p2cstrcpy (nodeName, reinterpret_cast <unsigned char*> (nodeName));
p2cstrcpy (nodeName, reinterpret_cast<unsigned char*> (nodeName));
midiBufferNode = new CEffectMIDIOtherBufferedNode (&mMIDIWorld,
8192,
@@ -498,8 +498,8 @@ public:
midiTransport = new CEffectMIDITransport (&mMIDIWorld);
midiEvents.ensureSize (2048);
channels.calloc (jmax (juceFilter->getNumInputChannels(),
juceFilter->getNumOutputChannels()));
channels.calloc (jmax (juceFilter->getTotalNumInputChannels(),
juceFilter->getTotalNumOutputChannels()));
juceFilter->setPlayHead (this);
juceFilter->addListener (this);
@@ -539,14 +539,14 @@ public:
#if JUCE_DEBUG || JUCE_LOG_ASSERTIONS
const int numMidiEventsComingIn = midiEvents.getNumEvents();
(void) numMidiEventsComingIn;
ignoreUnused (numMidiEventsComingIn);
#endif
{
const ScopedLock sl (juceFilter->getCallbackLock());
const int numIn = juceFilter->getNumInputChannels();
const int numOut = juceFilter->getNumOutputChannels();
const int numIn = juceFilter->getTotalNumInputChannels();
const int numOut = juceFilter->getTotalNumOutputChannels();
const int totalChans = jmax (numIn, numOut);
if (juceFilter->isSuspended())


+ 6
- 6
libs/juce/source/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h View File

@@ -163,10 +163,10 @@ public:
{
DialogWindow::LaunchOptions o;
o.content.setOwned (new AudioDeviceSelectorComponent (deviceManager,
processor->getNumInputChannels(),
processor->getNumInputChannels(),
processor->getNumOutputChannels(),
processor->getNumOutputChannels(),
processor->getTotalNumInputChannels(),
processor->getTotalNumInputChannels(),
processor->getTotalNumOutputChannels(),
processor->getTotalNumOutputChannels(),
true, false,
true, false));
o.content->setSize (500, 450);
@@ -196,8 +196,8 @@ public:
if (settings != nullptr)
savedState = settings->getXmlValue ("audioSetup");
deviceManager.initialise (processor->getNumInputChannels(),
processor->getNumOutputChannels(),
deviceManager.initialise (processor->getTotalNumInputChannels(),
processor->getTotalNumOutputChannels(),
savedState,
true);
}


+ 304
- 178
libs/juce/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_VST
@@ -34,27 +31,13 @@
#pragma warning (disable : 4996 4100)
#endif
#ifdef _WIN32
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x500
#undef STRICT
#define STRICT 1
#include <windows.h>
#elif defined (LINUX)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#undef KeyPress
#else
#include <Carbon/Carbon.h>
#endif
#include "../utility/juce_IncludeSystemHeaders.h"
#ifdef PRAGMA_ALIGN_SUPPORTED
#undef PRAGMA_ALIGN_SUPPORTED
#define PRAGMA_ALIGN_SUPPORTED 1
#endif
//==============================================================================
#ifndef _MSC_VER
#define __cdecl
#endif
@@ -117,6 +100,7 @@
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_FakeMouseMoveGenerator.h"
#include "../utility/juce_WindowsHooks.h"
#include "../utility/juce_PluginBusUtilities.h"
#ifdef _MSC_VER
#pragma pack (pop)
@@ -278,11 +262,8 @@ public:
JuceVSTWrapper (audioMasterCallback audioMasterCB, AudioProcessor* const af)
: AudioEffectX (audioMasterCB, af->getNumPrograms(), af->getNumParameters()),
filter (af),
busUtils (*filter, false),
chunkMemoryTime (0),
speakerIn (kSpeakerArrEmpty),
speakerOut (kSpeakerArrEmpty),
numInChans (JucePlugin_MaxNumInputChannels),
numOutChans (JucePlugin_MaxNumOutputChannels),
isProcessing (false),
isBypassed (false),
hasShutdown (false),
@@ -296,7 +277,16 @@ public:
#endif
hostWindow (0)
{
filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0);
busUtils.findAllCompatibleLayouts();
// VST-2 does not support disabling buses: so always enable all of them
if (busUtils.hasDynamicInBuses() || busUtils.hasDynamicOutBuses())
busUtils.enableAllBuses();
const int totalNumInChannels = busUtils.findTotalNumChannels(true);
const int totalNumOutChannels = busUtils.findTotalNumChannels(false);
filter->setRateAndBufferSizeDetails (0, 0);
filter->setPlayHead (this);
filter->addListener (this);
@@ -305,8 +295,8 @@ public:
setUniqueID ((int) (JucePlugin_VSTUniqueID));
setNumInputs (numInChans);
setNumOutputs (numOutChans);
setNumInputs (totalNumInChannels);
setNumOutputs (totalNumOutChannels);
canProcessReplacing (true);
canDoubleReplacing (filter->supportsDoublePrecisionProcessing());
@@ -443,7 +433,7 @@ public:
VstIntPtr vendorSpecific (VstInt32 lArg, VstIntPtr lArg2, void* ptrArg, float floatArg) override
{
(void) lArg; (void) lArg2; (void) ptrArg; (void) floatArg;
ignoreUnused (lArg, lArg2, ptrArg, floatArg);
#if JucePlugin_Build_VST3 && JUCE_VST3_CAN_REPLACE_VST2
if ((lArg == 'stCA' || lArg == 'stCa') && lArg2 == 'FUID' && ptrArg != nullptr)
@@ -456,47 +446,6 @@ public:
return 0;
}
bool getInputProperties (VstInt32 index, VstPinProperties* properties) override
{
if (filter == nullptr || index >= JucePlugin_MaxNumInputChannels)
return false;
setPinProperties (*properties, filter->getInputChannelName ((int) index),
speakerIn, filter->isInputChannelStereoPair ((int) index));
return true;
}
bool getOutputProperties (VstInt32 index, VstPinProperties* properties) override
{
if (filter == nullptr || index >= JucePlugin_MaxNumOutputChannels)
return false;
setPinProperties (*properties, filter->getOutputChannelName ((int) index),
speakerOut, filter->isOutputChannelStereoPair ((int) index));
return true;
}
static void setPinProperties (VstPinProperties& properties, const String& name,
VstSpeakerArrangementType type, const bool isPair)
{
name.copyToUTF8 (properties.label, (size_t) (kVstMaxLabelLen - 1));
name.copyToUTF8 (properties.shortLabel, (size_t) (kVstMaxShortLabelLen - 1));
if (type != kSpeakerArrEmpty)
{
properties.flags = kVstPinUseSpeaker;
properties.arrangementType = type;
}
else
{
properties.flags = kVstPinIsActive;
properties.arrangementType = 0;
if (isPair)
properties.flags |= kVstPinIsStereo;
}
}
bool setBypass (bool b) override
{
isBypassed = b;
@@ -518,31 +467,11 @@ public:
VSTMidiEventList::addEventsToMidiBuffer (events, midiEvents);
return 1;
#else
(void) events;
ignoreUnused (events);
return 0;
#endif
}
void process (float** inputs, float** outputs, VstInt32 numSamples)
{
VstTempBuffers<float>& tmpBuffers = floatTempBuffers;
const int numIn = numInChans;
const int numOut = numOutChans;
tmpBuffers.processTempBuffer.setSize (numIn, numSamples, false, false, true);
for (int i = numIn; --i >= 0;)
tmpBuffers.processTempBuffer.copyFrom (i, 0, outputs[i], numSamples);
processReplacing (inputs, outputs, numSamples);
AudioSampleBuffer dest (outputs, numOut, numSamples);
for (int i = jmin (numIn, numOut); --i >= 0;)
dest.addFrom (i, 0, tmpBuffers.processTempBuffer, i, 0, numSamples);
}
template <typename FloatType>
void internalProcessReplacing (FloatType** inputs, FloatType** outputs,
VstInt32 numSamples, VstTempBuffers<FloatType>& tmpBuffers)
@@ -560,12 +489,6 @@ public:
resume();
filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
#if JUCE_WINDOWS
if (GetThreadPriority (GetCurrentThread()) <= THREAD_PRIORITY_NORMAL
&& GetThreadPriority (GetCurrentThread()) >= THREAD_PRIORITY_LOWEST)
filter->setNonRealtime (true);
#endif
}
#if JUCE_DEBUG && ! JucePlugin_ProducesMidiOutput
@@ -575,10 +498,10 @@ public:
jassert (activePlugins.contains (this));
{
const ScopedLock sl (filter->getCallbackLock());
const int numIn = filter->getTotalNumInputChannels();
const int numOut = filter->getTotalNumOutputChannels();
const int numIn = numInChans;
const int numOut = numOutChans;
const ScopedLock sl (filter->getCallbackLock());
if (filter->isSuspended())
{
@@ -716,6 +639,13 @@ public:
if (filter != nullptr)
{
isProcessing = true;
const int numInChans = filter->busArrangement.getTotalNumInputChannels();
const int numOutChans = filter->busArrangement.getTotalNumOutputChannels();
setNumInputs (numInChans);
setNumOutputs (numOutChans);
floatTempBuffers.channels.calloc ((size_t) (numInChans + numOutChans));
doubleTempBuffers.channels.calloc ((size_t) (numInChans + numOutChans));
@@ -730,7 +660,7 @@ public:
firstProcessCallback = true;
filter->setNonRealtime (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */);
filter->setPlayConfigDetails (numInChans, numOutChans, rate, currentBlockSize);
filter->setRateAndBufferSizeDetails (rate, currentBlockSize);
deleteTempChannels();
@@ -967,94 +897,296 @@ public:
return filter != nullptr && filter->isParameterAutomatable ((int) index);
}
struct ChannelConfigComparator
bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput,
VstSpeakerArrangement* pluginOutput) override
{
static int compareElements (const short* const first, const short* const second) noexcept
if ((busUtils.getBusCount (true) == 0 || busUtils.busIgnoresLayout(true, 0))
&& (busUtils.getBusCount (false) == 0 || busUtils.busIgnoresLayout(false, 0)))
return false;
if (pluginInput != nullptr && filter->busArrangement.inputBuses.size() == 0)
return false;
if (pluginOutput != nullptr && filter->busArrangement.outputBuses.size() == 0)
return false;
PluginBusUtilities::ScopedBusRestorer busRestorer (busUtils);
if (pluginInput != nullptr)
{
if (first[0] < second[0]) return -1;
if (first[0] > second[0]) return 1;
if (first[1] < second[1]) return -1;
if (first[1] > second[1]) return 1;
return 0;
AudioChannelSet newType = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginInput);
if (busUtils.getChannelSet (true, 0) != newType)
if (! filter->setPreferredBusArrangement (true, 0, newType))
return false;
}
};
bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput,
VstSpeakerArrangement* pluginOutput) override
if (pluginOutput != nullptr)
{
AudioChannelSet newType = SpeakerMappings::vstArrangementTypeToChannelSet (*pluginOutput);
if (busUtils.getChannelSet (false, 0) != newType)
if (! filter->setPreferredBusArrangement (false, 0, newType))
return false;
}
busRestorer.release();
const int totalNumInChannels = busUtils.findTotalNumChannels(true);
const int totalNumOutChannels = busUtils.findTotalNumChannels(false);
filter->setRateAndBufferSizeDetails(0, 0);
setNumInputs (totalNumInChannels);
setNumOutputs(totalNumOutChannels);
ioChanged();
return true;
}
bool getSpeakerArrangement (VstSpeakerArrangement** pluginInput, VstSpeakerArrangement** pluginOutput) override
{
short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations };
*pluginInput = 0;
*pluginOutput = 0;
Array<short*> channelConfigsSorted;
ChannelConfigComparator comp;
if ((busUtils.getBusCount (true) == 0 || busUtils.busIgnoresLayout(true, 0))
&& (busUtils.getBusCount (false) == 0 || busUtils.busIgnoresLayout(false, 0)))
return false;
for (int i = 0; i < numElementsInArray (channelConfigs); ++i)
channelConfigsSorted.addSorted (comp, channelConfigs[i]);
if (! AudioEffectX::allocateArrangement (pluginInput, busUtils.getNumChannels (true, 0)))
return false;
for (int i = channelConfigsSorted.size(); --i >= 0;)
if (! AudioEffectX::allocateArrangement (pluginOutput, busUtils.getNumChannels (false, 0)))
{
const short* const config = channelConfigsSorted.getUnchecked(i);
bool inCountMatches = (config[0] == pluginInput->numChannels);
bool outCountMatches = (config[1] == pluginOutput->numChannels);
AudioEffectX::deallocateArrangement (pluginInput);
*pluginInput = 0;
return false;
}
SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (true, 0), **pluginInput);
SpeakerMappings::channelSetToVstArrangement (busUtils.getChannelSet (false, 0), **pluginOutput);
if (inCountMatches && outCountMatches)
return true;
}
bool getInputProperties (VstInt32 index, VstPinProperties* properties) override
{
return filter != nullptr
&& getPinProperties (*properties, true, (int) index);
}
bool getOutputProperties (VstInt32 index, VstPinProperties* properties) override
{
return filter != nullptr
&& getPinProperties (*properties, false, (int) index);
}
bool getPinProperties (VstPinProperties& properties, bool direction, int index) const
{
// index refers to the absolute index when combining all channels of every bus
const int n = busUtils.getBusCount(direction);
int busIdx;
for (busIdx = 0; busIdx < n; ++busIdx)
{
const int numChans = busUtils.getNumChannels(direction, busIdx);
if (index < numChans)
break;
index -= numChans;
}
if (busIdx >= n)
return false;
const AudioProcessor::AudioProcessorBus& busInfo = busUtils.getFilterBus (direction).getReference (busIdx);
busInfo.name.copyToUTF8 (properties.label, (size_t) (kVstMaxLabelLen - 1));
busInfo.name.copyToUTF8 (properties.shortLabel, (size_t) (kVstMaxShortLabelLen - 1));
VstInt32 type = SpeakerMappings::channelSetToVstArrangementType (busInfo.channels);
if (type != kSpeakerArrEmpty)
{
properties.flags = kVstPinUseSpeaker | kVstPinIsActive;
properties.arrangementType = type;
}
else
{
properties.flags = 0;
properties.arrangementType = 0;
}
if (busInfo.channels.size() == 2)
properties.flags |= kVstPinIsStereo;
return true;
}
//==============================================================================
struct SpeakerMappings : private AudioChannelSet // (inheritance only to give easier access to items in the namespace)
{
struct Mapping
{
VstInt32 vst2;
ChannelType channels[13];
bool matches (const Array<ChannelType>& chans) const noexcept
{
speakerIn = (VstSpeakerArrangementType) pluginInput->type;
speakerOut = (VstSpeakerArrangementType) pluginOutput->type;
numInChans = pluginInput->numChannels;
numOutChans = pluginOutput->numChannels;
const int n = sizeof (channels) / sizeof (ChannelType);
filter->setPlayConfigDetails (numInChans, numOutChans,
filter->getSampleRate(),
filter->getBlockSize());
for (int i = 0; i < n; ++i)
{
if (channels[i] == unknown) return (i == chans.size());
if (i == chans.size()) return (channels[i] == unknown);
if (channels[i] != chans.getUnchecked(i))
return false;
}
filter->setSpeakerArrangement (getSpeakerArrangementString (speakerIn),
getSpeakerArrangementString (speakerOut));
return true;
}
};
static AudioChannelSet vstArrangementTypeToChannelSet (const VstSpeakerArrangement& arr)
{
for (const Mapping* m = getMappings(); m->vst2 != kSpeakerArrEmpty; ++m)
{
if (m->vst2 == arr.type)
{
AudioChannelSet s;
for (int i = 0; m->channels[i] != 0; ++i)
s.addChannel (m->channels[i]);
return s;
}
}
return AudioChannelSet::discreteChannels (arr.numChannels);
}
filter->setSpeakerArrangement (String::empty, String::empty);
return false;
}
static VstInt32 channelSetToVstArrangementType (AudioChannelSet channels)
{
Array<AudioChannelSet::ChannelType> chans (channels.getChannelTypes());
for (const Mapping* m = getMappings(); m->vst2 != kSpeakerArrEmpty; ++m)
if (m->matches (chans))
return m->vst2;
static const char* getSpeakerArrangementString (VstSpeakerArrangementType type) noexcept
{
switch (type)
{
case kSpeakerArrMono: return "M";
case kSpeakerArrStereo: return "L R";
case kSpeakerArrStereoSurround: return "Ls Rs";
case kSpeakerArrStereoCenter: return "Lc Rc";
case kSpeakerArrStereoSide: return "Sl Sr";
case kSpeakerArrStereoCLfe: return "C Lfe";
case kSpeakerArr30Cine: return "L R C";
case kSpeakerArr30Music: return "L R S";
case kSpeakerArr31Cine: return "L R C Lfe";
case kSpeakerArr31Music: return "L R Lfe S";
case kSpeakerArr40Cine: return "L R C S";
case kSpeakerArr40Music: return "L R Ls Rs";
case kSpeakerArr41Cine: return "L R C Lfe S";
case kSpeakerArr41Music: return "L R Lfe Ls Rs";
case kSpeakerArr50: return "L R C Ls Rs" ;
case kSpeakerArr51: return "L R C Lfe Ls Rs";
case kSpeakerArr60Cine: return "L R C Ls Rs Cs";
case kSpeakerArr60Music: return "L R Ls Rs Sl Sr ";
case kSpeakerArr61Cine: return "L R C Lfe Ls Rs Cs";
case kSpeakerArr61Music: return "L R Lfe Ls Rs Sl Sr";
case kSpeakerArr70Cine: return "L R C Ls Rs Lc Rc ";
case kSpeakerArr70Music: return "L R C Ls Rs Sl Sr";
case kSpeakerArr71Cine: return "L R C Lfe Ls Rs Lc Rc";
case kSpeakerArr71Music: return "L R C Lfe Ls Rs Sl Sr";
case kSpeakerArr80Cine: return "L R C Ls Rs Lc Rc Cs";
case kSpeakerArr80Music: return "L R C Ls Rs Cs Sl Sr";
case kSpeakerArr81Cine: return "L R C Lfe Ls Rs Lc Rc Cs";
case kSpeakerArr81Music: return "L R C Lfe Ls Rs Cs Sl Sr" ;
case kSpeakerArr102: return "L R C Lfe Ls Rs Tfl Tfc Tfr Trl Trr Lfe2";
default: break;
return kSpeakerArrEmpty;
}
return nullptr;
}
static void channelSetToVstArrangement (const AudioChannelSet& channels, VstSpeakerArrangement& result)
{
result.type = channelSetToVstArrangementType (channels);
result.numChannels = channels.size();
for (int i = 0; i < result.numChannels; ++i)
{
VstSpeakerProperties& speaker = result.speakers[i];
zeromem (&speaker, sizeof (VstSpeakerProperties));
speaker.type = getSpeakerType (channels.getTypeOfChannel (i));
}
}
static const Mapping* getMappings() noexcept
{
static const Mapping mappings[] =
{
{ kSpeakerArrMono, { centre, unknown } },
{ kSpeakerArrStereo, { left, right, unknown } },
{ kSpeakerArrStereoSurround, { surroundLeft, surroundRight, unknown } },
{ kSpeakerArrStereoCenter, { centreLeft, centreRight, unknown } },
{ kSpeakerArrStereoSide, { sideLeft, sideRight, unknown } },
{ kSpeakerArrStereoCLfe, { centre, subbass, unknown } },
{ kSpeakerArr30Cine, { left, right, centre, unknown } },
{ kSpeakerArr30Music, { left, right, surround, unknown } },
{ kSpeakerArr31Cine, { left, right, centre, subbass, unknown } },
{ kSpeakerArr31Music, { left, right, subbass, surround, unknown } },
{ kSpeakerArr40Cine, { left, right, centre, surround, unknown } },
{ kSpeakerArr40Music, { left, right, surroundLeft, surroundRight, unknown } },
{ kSpeakerArr41Cine, { left, right, centre, subbass, surround, unknown } },
{ kSpeakerArr41Music, { left, right, subbass, surroundLeft, surroundRight, unknown } },
{ kSpeakerArr50, { left, right, centre, surroundLeft, surroundRight, unknown } },
{ kSpeakerArr51, { left, right, centre, subbass, surroundLeft, surroundRight, unknown } },
{ kSpeakerArr60Cine, { left, right, centre, surroundLeft, surroundRight, surround, unknown } },
{ kSpeakerArr60Music, { left, right, surroundLeft, surroundRight, sideLeft, sideRight, unknown } },
{ kSpeakerArr61Cine, { left, right, centre, subbass, surroundLeft, surroundRight, surround, unknown } },
{ kSpeakerArr61Music, { left, right, subbass, surroundLeft, surroundRight, sideLeft, sideRight, unknown } },
{ kSpeakerArr70Cine, { left, right, centre, surroundLeft, surroundRight, topFrontLeft, topFrontRight, unknown } },
{ kSpeakerArr70Music, { left, right, centre, surroundLeft, surroundRight, sideLeft, sideRight, unknown } },
{ kSpeakerArr71Cine, { left, right, centre, subbass, surroundLeft, surroundRight, topFrontLeft, topFrontRight, unknown } },
{ kSpeakerArr71Music, { left, right, centre, subbass, surroundLeft, surroundRight, sideLeft, sideRight, unknown } },
{ kSpeakerArr80Cine, { left, right, centre, surroundLeft, surroundRight, topFrontLeft, topFrontRight, surround, unknown } },
{ kSpeakerArr80Music, { left, right, centre, surroundLeft, surroundRight, surround, sideLeft, sideRight, unknown } },
{ kSpeakerArr81Cine, { left, right, centre, subbass, surroundLeft, surroundRight, topFrontLeft, topFrontRight, surround, unknown } },
{ kSpeakerArr81Music, { left, right, centre, subbass, surroundLeft, surroundRight, surround, sideLeft, sideRight, unknown } },
{ kSpeakerArr102, { left, right, centre, subbass, surroundLeft, surroundRight, topFrontLeft, topFrontCentre, topFrontRight, topRearLeft, topRearRight, subbass2, unknown } },
{ kSpeakerArrEmpty, { unknown } }
};
return mappings;
}
static inline VstInt32 getSpeakerType (AudioChannelSet::ChannelType type) noexcept
{
switch (type)
{
case AudioChannelSet::ChannelType::left: return kSpeakerL;
case AudioChannelSet::ChannelType::right: return kSpeakerR;
case AudioChannelSet::ChannelType::centre: return kSpeakerC;
case AudioChannelSet::ChannelType::subbass: return kSpeakerLfe;
case AudioChannelSet::ChannelType::surroundLeft: return kSpeakerLs;
case AudioChannelSet::ChannelType::surroundRight: return kSpeakerRs;
case AudioChannelSet::ChannelType::centreLeft: return kSpeakerLc;
case AudioChannelSet::ChannelType::centreRight: return kSpeakerRc;
case AudioChannelSet::ChannelType::surround: return kSpeakerS;
case AudioChannelSet::ChannelType::sideLeft: return kSpeakerSl;
case AudioChannelSet::ChannelType::sideRight: return kSpeakerSr;
case AudioChannelSet::ChannelType::topMiddle: return kSpeakerTm;
case AudioChannelSet::ChannelType::topFrontLeft: return kSpeakerTfl;
case AudioChannelSet::ChannelType::topFrontCentre: return kSpeakerTfc;
case AudioChannelSet::ChannelType::topFrontRight: return kSpeakerTfr;
case AudioChannelSet::ChannelType::topRearLeft: return kSpeakerTrl;
case AudioChannelSet::ChannelType::topRearCentre: return kSpeakerTrc;
case AudioChannelSet::ChannelType::topRearRight: return kSpeakerTrr;
case AudioChannelSet::ChannelType::subbass2: return kSpeakerLfe2;
default: break;
}
return 0;
}
static inline AudioChannelSet::ChannelType getChannelType (VstInt32 type) noexcept
{
switch (type)
{
case kSpeakerL: return AudioChannelSet::ChannelType::left;
case kSpeakerR: return AudioChannelSet::ChannelType::right;
case kSpeakerC: return AudioChannelSet::ChannelType::centre;
case kSpeakerLfe: return AudioChannelSet::ChannelType::subbass;
case kSpeakerLs: return AudioChannelSet::ChannelType::surroundLeft;
case kSpeakerRs: return AudioChannelSet::ChannelType::surroundRight;
case kSpeakerLc: return AudioChannelSet::ChannelType::centreLeft;
case kSpeakerRc: return AudioChannelSet::ChannelType::centreRight;
case kSpeakerS: return AudioChannelSet::ChannelType::surround;
case kSpeakerSl: return AudioChannelSet::ChannelType::sideLeft;
case kSpeakerSr: return AudioChannelSet::ChannelType::sideRight;
case kSpeakerTm: return AudioChannelSet::ChannelType::topMiddle;
case kSpeakerTfl: return AudioChannelSet::ChannelType::topFrontLeft;
case kSpeakerTfc: return AudioChannelSet::ChannelType::topFrontCentre;
case kSpeakerTfr: return AudioChannelSet::ChannelType::topFrontRight;
case kSpeakerTrl: return AudioChannelSet::ChannelType::topRearLeft;
case kSpeakerTrc: return AudioChannelSet::ChannelType::topRearCentre;
case kSpeakerTrr: return AudioChannelSet::ChannelType::topRearRight;
case kSpeakerLfe2: return AudioChannelSet::ChannelType::subbass2;
default: break;
}
return AudioChannelSet::ChannelType::unknown;
}
};
//==============================================================================
VstInt32 getChunk (void** data, bool onlyStoreCurrentProgramData) override
@@ -1508,14 +1640,13 @@ public:
//==============================================================================
private:
AudioProcessor* filter;
PluginBusUtilities busUtils;
juce::MemoryBlock chunkMemory;
juce::uint32 chunkMemoryTime;
ScopedPointer<EditorCompWrapper> editorComp;
ERect editorSize;
MidiBuffer midiEvents;
VSTMidiEventList outgoingEvents;
VstSpeakerArrangementType speakerIn, speakerOut;
int numInChans, numOutChans;
bool isProcessing, isBypassed, hasShutdown, isInSizeWindow, firstProcessCallback;
bool shouldDeleteEditor, useNSView;
VstTempBuffers<float> floatTempBuffers;
@@ -1554,17 +1685,11 @@ private:
{
MessageManager::getInstance()->setCurrentThreadAsMessageThread();
class MessageThreadCallback : public CallbackMessage
struct MessageThreadCallback : public CallbackMessage
{
public:
MessageThreadCallback (bool& tr) : triggered (tr) {}
void messageCallback() override { triggered = true; }
void messageCallback() override
{
triggered = true;
}
private:
bool& triggered;
};
@@ -1584,7 +1709,7 @@ private:
if (filter != nullptr)
{
int numChannels = filter->getNumInputChannels() + filter->getNumOutputChannels();
int numChannels = filter->getTotalNumInputChannels() + filter->getTotalNumOutputChannels();
tmpBuffers.tempChannels.insertMultiple (0, nullptr, numChannels);
}
}
@@ -1595,6 +1720,7 @@ private:
deleteTempChannels (doubleTempBuffers);
}
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceVSTWrapper)
};


+ 1
- 4
libs/juce/source/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.mm View File

@@ -22,10 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#if JucePlugin_Build_VST || JucePlugin_Build_VST3


+ 246
- 202
libs/juce/source/modules/juce_audio_plugin_client/VST3/juce_VST3_Wrapper.cpp View File

@@ -22,9 +22,7 @@
==============================================================================
*/
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
//==============================================================================
#if JucePlugin_Build_VST3 && (__APPLE_CPP__ || __APPLE_CC__ || _WIN32 || _WIN64)
@@ -33,6 +31,7 @@
#include "../utility/juce_CheckSettingMacros.h"
#include "../utility/juce_IncludeModuleHeaders.h"
#include "../utility/juce_WindowsHooks.h"
#include "../utility/juce_PluginBusUtilities.h"
#include "../../juce_audio_processors/format_types/juce_VST3Common.h"
#ifndef JUCE_VST3_CAN_REPLACE_VST2
@@ -640,7 +639,7 @@ private:
private:
//==============================================================================
class ContentWrapperComponent : public juce::Component
class ContentWrapperComponent : public juce::Component
{
public:
ContentWrapperComponent (JuceVST3Editor& editor, AudioProcessor& plugin)
@@ -692,11 +691,12 @@ private:
{
const int w = pluginEditor->getWidth();
const int h = pluginEditor->getHeight();
const PluginHostType host (getHostType());
#if JUCE_WINDOWS
setSize (w, h);
#else
if (owner.macHostWindow != nullptr && ! getHostType().isWavelab())
if (owner.macHostWindow != nullptr && ! (host.isWavelab() || host.isReaper()))
juce::setNativeHostWindowSizeVST (owner.macHostWindow, this, w, h, owner.isNSView);
#endif
@@ -705,7 +705,11 @@ private:
ViewRect newSize (0, 0, w, h);
owner.plugFrame->resizeView (&owner, &newSize);
if (getHostType().isWavelab())
#if JUCE_MAC
if (host.isWavelab() || host.isReaper())
#else
if (host.isWavelab())
#endif
setBounds (0, 0, w, h);
}
}
@@ -761,13 +765,25 @@ class JuceVST3Component : public Vst::IComponent,
public:
JuceVST3Component (Vst::IHostApplication* h)
: refCount (1),
pluginInstance (createPluginFilterOfType (AudioProcessor::wrapperType_VST3)),
host (h),
audioInputs (Vst::kAudio, Vst::kInput),
audioOutputs (Vst::kAudio, Vst::kOutput),
eventInputs (Vst::kEvent, Vst::kInput),
eventOutputs (Vst::kEvent, Vst::kOutput)
isMidiInputBusEnabled (false),
isMidiOutputBusEnabled (false),
busUtils (*pluginInstance, false)
{
pluginInstance = createPluginFilterOfType (AudioProcessor::wrapperType_VST3);
#if JucePlugin_WantsMidiInput
isMidiInputBusEnabled = true;
#endif
#if JucePlugin_ProducesMidiOutput
isMidiOutputBusEnabled = true;
#endif
busUtils.findAllCompatibleLayouts();
copyEnabledBuses (lastEnabledBusStates.inputBuses, pluginInstance->busArrangement.inputBuses, Vst::kInput);
copyEnabledBuses (lastEnabledBusStates.outputBuses, pluginInstance->busArrangement.outputBuses, Vst::kOutput);
comPluginInstance = new JuceAudioProcessor (pluginInstance);
zerostruct (processContext);
@@ -787,11 +803,6 @@ public:
if (pluginInstance != nullptr)
if (pluginInstance->getPlayHead() == this)
pluginInstance->setPlayHead (nullptr);
audioInputs.removeAll();
audioOutputs.removeAll();
eventInputs.removeAll();
eventOutputs.removeAll();
}
//==============================================================================
@@ -829,26 +840,7 @@ public:
if (host != hostContext)
host.loadFrom (hostContext);
#if JucePlugin_MaxNumInputChannels > 0
addAudioBusTo (audioInputs, TRANS("Audio Input"),
getArrangementForNumChannels (JucePlugin_MaxNumInputChannels));
#endif
#if JucePlugin_MaxNumOutputChannels > 0
addAudioBusTo (audioOutputs, TRANS("Audio Output"),
getArrangementForNumChannels (JucePlugin_MaxNumOutputChannels));
#endif
#if JucePlugin_WantsMidiInput
addEventBusTo (eventInputs, TRANS("MIDI Input"));
#endif
#if JucePlugin_ProducesMidiOutput
addEventBusTo (eventOutputs, TRANS("MIDI Output"));
#endif
processContext.sampleRate = processSetup.sampleRate;
preparePlugin (processSetup.sampleRate, (int) processSetup.maxSamplesPerBlock);
return kResultTrue;
@@ -895,56 +887,13 @@ public:
return kResultTrue;
}
//==============================================================================
tresult PLUGIN_API getControllerClassId (TUID classID) override
{
memcpy (classID, JuceVST3EditController::iid, sizeof (TUID));
return kResultTrue;
}
Steinberg::int32 PLUGIN_API getBusCount (Vst::MediaType type, Vst::BusDirection dir) override
{
if (Vst::BusList* const busList = getBusListFor (type, dir))
return busList->total();
return 0;
}
tresult PLUGIN_API getBusInfo (Vst::MediaType type, Vst::BusDirection dir,
Steinberg::int32 index, Vst::BusInfo& info) override
{
if (Vst::BusList* const busList = getBusListFor (type, dir))
{
if (Vst::Bus* const bus = (Vst::Bus*) busList->at (index))
{
info.mediaType = type;
info.direction = dir;
if (bus->getInfo (info))
return kResultTrue;
}
}
zerostruct (info);
return kResultFalse;
}
tresult PLUGIN_API activateBus (Vst::MediaType type, Vst::BusDirection dir,
Steinberg::int32 index, TBool state) override
{
if (Vst::BusList* const busList = getBusListFor (type, dir))
{
if (Vst::Bus* const bus = (Vst::Bus*) busList->at (index))
{
bus->setActive (state);
return kResultTrue;
}
}
jassertfalse;
return kResultFalse;
}
//==============================================================================
tresult PLUGIN_API setActive (TBool state) override
{
if (! state)
@@ -1039,7 +988,7 @@ public:
uint64 privateDataSize;
std::memcpy (&privateDataSize,
buffer + (size - jucePrivDataIdentifierSize - sizeof (uint64)),
buffer + ((size_t) size - jucePrivDataIdentifierSize - sizeof (uint64)),
sizeof (uint64));
privateDataSize = ByteOrder::swapIfBigEndian (privateDataSize);
@@ -1062,7 +1011,7 @@ public:
{
const int headerLen = static_cast<int> (htonl (*(juce::int32*) (data + 4)));
const struct fxBank* bank = (const struct fxBank*) (data + (8 + headerLen));
const int version = static_cast<int> (htonl (bank->version)); (void) version;
const int version = static_cast<int> (htonl (bank->version)); ignoreUnused (version);
jassert ('VstW' == htonl (*(juce::int32*) data));
jassert (1 == htonl (*(juce::int32*) (data + 8))); // version should be 1 according to Steinberg's docs
@@ -1331,9 +1280,7 @@ public:
tresult PLUGIN_API setUnitProgramData (Steinberg::int32, Steinberg::int32, IBStream*) override { return kNotImplemented; }
Vst::UnitID PLUGIN_API getSelectedUnit() override { return Vst::kRootUnitId; }
tresult PLUGIN_API getUnitByBus (Vst::MediaType, Vst::BusDirection,
Steinberg::int32, Steinberg::int32,
Vst::UnitID& unitId) override
tresult PLUGIN_API getUnitByBus (Vst::MediaType, Vst::BusDirection, Steinberg::int32, Steinberg::int32, Vst::UnitID& unitId) override
{
zerostruct (unitId);
return kNotImplemented;
@@ -1382,70 +1329,178 @@ public:
}
//==============================================================================
static tresult setBusArrangementFor (Vst::BusList& list,
Vst::SpeakerArrangement* arrangement,
Steinberg::int32 numBusses)
Steinberg::int32 PLUGIN_API getBusCount (Vst::MediaType type, Vst::BusDirection dir) override
{
if (arrangement != nullptr && numBusses == 1) //Should only be 1 bus per BusList
if (type == Vst::kAudio)
return (dir == Vst::kInput ? pluginInstance->busArrangement.inputBuses
: pluginInstance->busArrangement.outputBuses).size();
if (type == Vst::kEvent)
{
if (dir == Vst::kInput)
return isMidiInputBusEnabled ? 1 : 0;
if (dir == Vst::kOutput)
return isMidiOutputBusEnabled ? 1 : 0;
}
return 0;
}
static const AudioProcessor::AudioProcessorBus* getAudioBus (AudioProcessor::AudioBusArrangement& busArrangement,
Vst::BusDirection dir, Steinberg::int32 index) noexcept
{
const Array<AudioProcessor::AudioProcessorBus>& buses = dir == Vst::kInput ? busArrangement.inputBuses
: busArrangement.outputBuses;
return isPositiveAndBelow (index, static_cast<Steinberg::int32> (buses.size())) ? &buses.getReference (index) : nullptr;
}
const AudioProcessor::AudioProcessorBus* getAudioBus (Vst::BusDirection dir, Steinberg::int32 index) const noexcept
{
return getAudioBus (pluginInstance->busArrangement, dir, index);
}
tresult PLUGIN_API getBusInfo (Vst::MediaType type, Vst::BusDirection dir,
Steinberg::int32 index, Vst::BusInfo& info) override
{
if (type == Vst::kAudio)
{
if (const AudioProcessor::AudioProcessorBus* bus = getAudioBus (lastEnabledBusStates, dir, index))
{
info.mediaType = Vst::kAudio;
info.direction = dir;
info.channelCount = bus->channels.size();
toString128 (info.name, bus->name);
info.busType = index == 0 ? Vst::kMain : Vst::kAux;
info.flags = busUtils.getSupportedBusLayouts (dir == Vst::kInput, index).isEnabledByDefault ? Vst::BusInfo::kDefaultActive : 0;
return kResultTrue;
}
}
if (type == Vst::kEvent)
{
Steinberg::int32 counter = 0;
info.flags = Vst::BusInfo::kDefaultActive;
FOREACH_CAST (IPtr<Vst::Bus>, Vst::AudioBus, bus, list)
#if JucePlugin_WantsMidiInput
if (dir == Vst::kInput)
{
if (counter < numBusses)
bus->setArrangement (arrangement[counter]);
info.mediaType = Vst::kEvent;
info.direction = dir;
info.channelCount = 0;
toString128 (info.name, TRANS("MIDI Input"));
info.busType = Vst::kMain;
return kResultTrue;
}
#endif
counter++;
#if JucePlugin_ProducesMidiOutput
if (dir == Vst::kOutput)
{
info.mediaType = Vst::kEvent;
info.direction = dir;
info.channelCount = 0;
toString128 (info.name, TRANS("MIDI Output"));
info.busType = Vst::kMain;
return kResultTrue;
}
ENDFOR
#endif
}
zerostruct (info);
return kResultFalse;
}
tresult PLUGIN_API activateBus (Vst::MediaType type, Vst::BusDirection dir, Steinberg::int32 index, TBool state) override
{
if (type == Vst::kEvent)
{
if (index != 0)
return kResultFalse;
if (dir == Vst::kInput)
isMidiInputBusEnabled = (state != 0);
else
isMidiOutputBusEnabled = (state != 0);
return kResultTrue;
}
if (type == Vst::kAudio)
{
if (const AudioProcessor::AudioProcessorBus* bus = getAudioBus (dir, index))
{
if (state == (bus->channels.size() > 0))
return kResultTrue;
AudioChannelSet newChannels;
if (state)
if (const AudioProcessor::AudioProcessorBus* lastBusState = getAudioBus (lastEnabledBusStates, dir, index))
newChannels = lastBusState->channels;
if (pluginInstance->setPreferredBusArrangement (dir == Vst::kInput, index, newChannels))
return kResultTrue;
}
}
return kResultFalse;
}
void copyEnabledBuses (Array<AudioProcessor::AudioProcessorBus>& copies,
const Array<AudioProcessor::AudioProcessorBus>& source,
Vst::BusDirection dir)
{
for (int i = 0; i < source.size(); ++i)
{
AudioProcessor::AudioProcessorBus bus = source.getReference (i);
if (bus.channels.size() == 0 && i < copies.size())
bus = AudioProcessor::AudioProcessorBus (bus.name, copies.getReference (i).channels);
if (bus.channels.size() == 0)
bus = AudioProcessor::AudioProcessorBus (bus.name, busUtils.getDefaultLayoutForBus (dir == Vst::kInput, i));
copies.set (i, bus);
}
}
tresult PLUGIN_API setBusArrangements (Vst::SpeakerArrangement* inputs, Steinberg::int32 numIns,
Vst::SpeakerArrangement* outputs, Steinberg::int32 numOuts) override
{
(void) inputs; (void) outputs;
PluginBusUtilities::ScopedBusRestorer restorer (busUtils);
#if JucePlugin_MaxNumInputChannels > 0
if (setBusArrangementFor (audioInputs, inputs, numIns) != kResultTrue)
return kResultFalse;
#else
if (numIns != 0)
return kResultFalse;
#endif
for (int i = 0; i < numIns; ++i)
if (! pluginInstance->setPreferredBusArrangement (true, i, getChannelSetForSpeakerArrangement (inputs[i])))
return kInvalidArgument;
#if JucePlugin_MaxNumOutputChannels > 0
if (setBusArrangementFor (audioOutputs, outputs, numOuts) != kResultTrue)
return kResultFalse;
#else
if (numOuts != 0)
return kResultFalse;
#endif
for (int i = 0; i < numOuts; ++i)
if (! pluginInstance->setPreferredBusArrangement (false, i, getChannelSetForSpeakerArrangement (outputs[i])))
return kInvalidArgument;
restorer.release();
preparePlugin (getPluginInstance().getSampleRate(),
getPluginInstance().getBlockSize());
copyEnabledBuses (lastEnabledBusStates.inputBuses, pluginInstance->busArrangement.inputBuses, Vst::kInput);
copyEnabledBuses (lastEnabledBusStates.outputBuses, pluginInstance->busArrangement.outputBuses, Vst::kOutput);
return kResultTrue;
}
tresult PLUGIN_API getBusArrangement (Vst::BusDirection dir, Steinberg::int32 index, Vst::SpeakerArrangement& arr) override
{
if (Vst::BusList* const busList = getBusListFor (Vst::kAudio, dir))
if (const AudioProcessor::AudioProcessorBus* bus = getAudioBus (lastEnabledBusStates, dir, index))
{
if (Vst::AudioBus* const audioBus = FCast<Vst::AudioBus> (busList->at (index)))
{
arr = audioBus->getArrangement();
return kResultTrue;
}
arr = getSpeakerArrangement (bus->channels);
return kResultTrue;
}
return kResultFalse;
}
//==============================================================================
tresult PLUGIN_API canProcessSampleSize (Steinberg::int32 symbolicSampleSize) override
{
return (symbolicSampleSize == Vst::kSample32
@@ -1574,8 +1629,8 @@ public:
const int numInputChans = (data.inputs != nullptr && data.inputs[0].channelBuffers32 != nullptr) ? (int) data.inputs[0].numChannels : 0;
const int numOutputChans = (data.outputs != nullptr && data.outputs[0].channelBuffers32 != nullptr) ? (int) data.outputs[0].numChannels : 0;
if ((pluginInstance->getNumInputChannels() + pluginInstance->getNumOutputChannels()) > 0
&& (numInputChans + numOutputChans) == 0)
if ((pluginInstance->getTotalNumInputChannels() + pluginInstance->getTotalNumOutputChannels()) > 0
&& (numInputChans + numOutputChans) == 0)
return kResultFalse;
}
@@ -1623,11 +1678,14 @@ private:
Vst::ProcessContext processContext;
Vst::ProcessSetup processSetup;
Vst::BusList audioInputs, audioOutputs, eventInputs, eventOutputs;
MidiBuffer midiBuffer;
Array<float*> channelListFloat;
Array<double*> channelListDouble;
AudioProcessor::AudioBusArrangement lastEnabledBusStates;
bool isMidiInputBusEnabled, isMidiOutputBusEnabled;
PluginBusUtilities busUtils;
ScopedJuceInitialiser_GUI libraryInitialiser;
int vstBypassParameterId;
@@ -1638,11 +1696,49 @@ private:
template <typename FloatType>
void processAudio (Vst::ProcessData& data, Array<FloatType*>& channelList)
{
const int totalChans = prepareChannelLists (channelList, data);
int totalInputChans = 0;
const int plugInInputChannels = pluginInstance->getTotalNumInputChannels();
const int plugInOutputChannels = pluginInstance->getTotalNumOutputChannels();
if (data.inputs != nullptr)
{
for (int bus = 0; bus < data.numInputs && totalInputChans < plugInInputChannels; ++bus)
{
if (FloatType** const busChannels = getPointerForAudioBus<FloatType> (data.inputs[bus]))
{
const int numChans = jmin ((int) data.inputs[bus].numChannels, plugInInputChannels - totalInputChans);
for (int i = 0; i < numChans; ++i)
channelList.set (totalInputChans++, busChannels[i]);
}
}
}
int totalOutputChans = 0;
if (data.outputs != nullptr)
{
for (int bus = 0; bus < data.numOutputs && totalOutputChans < plugInOutputChannels; ++bus)
{
if (FloatType** const busChannels = getPointerForAudioBus<FloatType> (data.outputs[bus]))
{
const int numChans = jmin ((int) data.outputs[bus].numChannels, plugInOutputChannels - totalOutputChans);
for (int i = 0; i < numChans; ++i)
{
if (totalOutputChans >= totalInputChans)
channelList.set (totalOutputChans, busChannels[i]);
++totalOutputChans;
}
}
}
}
AudioBuffer<FloatType> buffer;
if (totalChans != 0)
if (int totalChans = jmax (totalOutputChans, totalInputChans))
buffer.setDataToReferTo (channelList.getRawDataPointer(), totalChans, (int) data.numSamples);
{
@@ -1659,57 +1755,47 @@ private:
}
else
{
if (isBypassed())
pluginInstance->processBlockBypassed (buffer, midiBuffer);
else
pluginInstance->processBlock (buffer, midiBuffer);
if (totalInputChans == pluginInstance->getTotalNumInputChannels()
&& totalOutputChans == pluginInstance->getTotalNumOutputChannels())
{
if (isBypassed())
pluginInstance->processBlockBypassed (buffer, midiBuffer);
else
pluginInstance->processBlock (buffer, midiBuffer);
}
}
}
if (data.outputs != nullptr)
{
for (int i = 0; i < data.numOutputs; ++i)
FloatVectorOperations::copy (getPointerForAudioBus<FloatType> (data.outputs[0])[i],
buffer.getReadPointer (i), (int) data.numSamples);
}
// clear extra busses..
if (data.outputs != nullptr)
for (int i = 1; i < data.numOutputs; ++i)
for (int f = 0; f < data.outputs[i].numChannels; ++f)
FloatVectorOperations::clear (getPointerForAudioBus<FloatType> (data.outputs[i])[f], (int) data.numSamples);
}
int outChanIndex = 0;
//==============================================================================
void addBusTo (Vst::BusList& busList, Vst::Bus* newBus)
{
busList.append (IPtr<Vst::Bus> (newBus, false));
}
void addAudioBusTo (Vst::BusList& busList, const juce::String& name, Vst::SpeakerArrangement arr)
{
addBusTo (busList, new Vst::AudioBus (toString (name), Vst::kMain, Vst::BusInfo::kDefaultActive, arr));
}
void addEventBusTo (Vst::BusList& busList, const juce::String& name)
{
addBusTo (busList, new Vst::EventBus (toString (name), Vst::kMain, Vst::BusInfo::kDefaultActive, 16));
}
for (int bus = 0; bus < data.numOutputs; ++bus)
{
if (FloatType** const busChannels = getPointerForAudioBus<FloatType> (data.outputs[bus]))
{
const int numChans = (int) data.outputs[bus].numChannels;
Vst::BusList* getBusListFor (Vst::MediaType type, Vst::BusDirection dir)
{
if (type == Vst::kAudio) return dir == Vst::kInput ? &audioInputs : &audioOutputs;
if (type == Vst::kEvent) return dir == Vst::kInput ? &eventInputs : &eventOutputs;
for (int i = 0; i < numChans; ++i)
{
if (outChanIndex < totalInputChans)
FloatVectorOperations::copy (busChannels[i], buffer.getReadPointer (outChanIndex), (int) data.numSamples);
else if (outChanIndex >= totalOutputChans)
FloatVectorOperations::clear (busChannels[i], (int) data.numSamples);
return nullptr;
++outChanIndex;
}
}
}
}
}
//==============================================================================
template <typename FloatType>
void allocateChannelLists (Array<FloatType*>& channelList)
{
channelList.clear();
channelList.insertMultiple (0, nullptr, jmax (JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) + 1);
channelList.clearQuick();
channelList.insertMultiple (0, nullptr, 128);
}
template <typename FloatType>
@@ -1718,60 +1804,18 @@ private:
return AudioBusPointerHelper<FloatType>::impl (data);
}
template <typename FloatType>
static int prepareChannelLists (Array<FloatType*>& channelList, Vst::ProcessData& data) noexcept
{
int totalChans = 0;
FloatType** inChannelBuffers =
data.inputs != nullptr ? getPointerForAudioBus<FloatType> (data.inputs[0]) : nullptr;
FloatType** outChannelBuffers =
data.outputs != nullptr ? getPointerForAudioBus<FloatType> (data.outputs[0]) : nullptr;
const int numInputChans = (data.inputs != nullptr && inChannelBuffers != nullptr) ? (int) data.inputs[0].numChannels : 0;
const int numOutputChans = (data.outputs != nullptr && outChannelBuffers != nullptr) ? (int) data.outputs[0].numChannels : 0;
for (int idx = 0; totalChans < numInputChans; ++idx)
{
channelList.set (totalChans, inChannelBuffers[idx]);
++totalChans;
}
// note that the loop bounds are correct: as VST-3 is always process replacing
// we already know the output channel buffers of the first numInputChans channels
for (int idx = 0; totalChans < numOutputChans; ++idx)
{
channelList.set (totalChans, outChannelBuffers[idx]);
++totalChans;
}
return totalChans;
}
//==============================================================================
enum InternalParameters
{
paramPreset = 'prst'
};
static int getNumChannels (Vst::BusList& busList)
{
Vst::BusInfo info;
info.channelCount = 0;
if (Vst::Bus* bus = busList.first())
bus->getInfo (info);
return (int) info.channelCount;
}
void preparePlugin (double sampleRate, int bufferSize)
{
getPluginInstance().setPlayConfigDetails (getNumChannels (audioInputs),
getNumChannels (audioOutputs),
sampleRate, bufferSize);
AudioProcessor& p = getPluginInstance();
getPluginInstance().prepareToPlay (sampleRate, bufferSize);
p.setRateAndBufferSizeDetails (sampleRate, bufferSize);
p.prepareToPlay (sampleRate, bufferSize);
}
//==============================================================================


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

@@ -1,7 +1,7 @@
{
"id": "juce_audio_plugin_client",
"name": "JUCE audio plugin wrapper classes",
"version": "4.0.2",
"version": "4.1.0",
"description": "Classes for building VST, VST3, RTAS, AAX and AU plugins.",
"website": "http://www.juce.com/juce",
"license": "GPL/Commercial",
@@ -31,10 +31,8 @@
{ "file": "AU/CoreAudioUtilityClasses/AUCarbonViewControl.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUCarbonViewDispatch.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUDispatch.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUEffectBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUInputElement.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUMIDIBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUMIDIEffectBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUOutputBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUOutputElement.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },
{ "file": "AU/CoreAudioUtilityClasses/AUScopeElement.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" },


+ 0
- 12
libs/juce/source/modules/juce_audio_plugin_client/utility/juce_CheckSettingMacros.h View File

@@ -51,18 +51,6 @@
#error "You need to define the JucePlugin_WantsMidiInput value!"
#endif
#ifndef JucePlugin_MaxNumInputChannels
#error "You need to define the JucePlugin_MaxNumInputChannels value!"
#endif
#ifndef JucePlugin_MaxNumOutputChannels
#error "You need to define the JucePlugin_MaxNumOutputChannels value!"
#endif
#ifndef JucePlugin_PreferredChannelConfigurations
#error "You need to define the JucePlugin_PreferredChannelConfigurations value!"
#endif
#ifdef JucePlugin_Latency
#error "JucePlugin_Latency is now deprecated - instead, call the AudioProcessor::setLatencySamples() method if your plugin has a non-zero delay"
#endif


+ 11
- 4
libs/juce/source/modules/juce_audio_plugin_client/utility/juce_IncludeSystemHeaders.h View File

@@ -50,16 +50,23 @@
#define JUCE_SUPPORT_CARBON 1
#endif
#define Point CarbonDummyPointName
#if JUCE_SUPPORT_CARBON
#define Point CarbonDummyPointName
#define Component CarbonDummyCompName
#endif
#ifdef __OBJC__
#include <Cocoa/Cocoa.h>
#endif
#if JUCE_SUPPORT_CARBON
#include <Carbon/Carbon.h>
#undef Point
#undef Component
#else
#include <Cocoa/Cocoa.h>
#endif
#undef Point
#include <objc/runtime.h>
#include <objc/objc.h>
#include <objc/message.h>


+ 372
- 0
libs/juce/source/modules/juce_audio_plugin_client/utility/juce_PluginBusUtilities.h View File

@@ -0,0 +1,372 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
struct PluginBusUtilities
{
//==============================================================================
typedef Array<AudioProcessor::AudioProcessorBus> AudioBusArray;
//==============================================================================
PluginBusUtilities (AudioProcessor& plugin, bool markDiscreteLayoutsAsSupported)
: processor (plugin),
dynamicInBuses (false),
dynamicOutBuses (false),
addDiscreteLayouts (markDiscreteLayoutsAsSupported)
{
}
//==============================================================================
// the first layout is the default layout
struct SupportedBusLayouts
{
enum
{
pseudoChannelBitNum = 90 // use this bit index to check if plug-in really doesn't care about layouts
};
//==============================================================================
SupportedBusLayouts() : defaultLayoutIndex (0), busIgnoresLayout (true), canBeDisabled (false) {}
AudioChannelSet& getDefault() noexcept { return supportedLayouts.getReference (defaultLayoutIndex); }
const AudioChannelSet& getDefault() const noexcept { return supportedLayouts.getReference (defaultLayoutIndex); }
void updateDefaultLayout (const AudioChannelSet& defaultLayout) noexcept { defaultLayoutIndex = jmax (supportedLayouts.indexOf (defaultLayout), 0); }
bool busSupportsNumChannels (int numChannels) const noexcept { return getDefaultLayoutForChannelNum (numChannels) != nullptr; }
//==============================================================================
const AudioChannelSet* getDefaultLayoutForChannelNum (int channelNum) const noexcept
{
const AudioChannelSet& dflt = getDefault();
if (dflt.size() == channelNum)
return &dflt;
for (int i = 0; i < supportedLayouts.size(); ++i)
{
const AudioChannelSet& layout = supportedLayouts.getReference (i);
if (layout.size() == channelNum)
return &layout;
}
return nullptr;
}
int defaultLayoutIndex;
bool busIgnoresLayout, canBeDisabled, isEnabledByDefault;
SortedSet<AudioChannelSet> supportedLayouts;
};
//==============================================================================
AudioBusArray& getFilterBus (bool inputBus) noexcept { return inputBus ? processor.busArrangement.inputBuses : processor.busArrangement.outputBuses; }
const AudioBusArray& getFilterBus (bool inputBus) const noexcept { return inputBus ? processor.busArrangement.inputBuses : processor.busArrangement.outputBuses; }
int getBusCount (bool inputBus) const noexcept { return getFilterBus (inputBus).size(); }
AudioChannelSet getChannelSet (bool inputBus, int bus) noexcept { return getFilterBus (inputBus).getReference (bus).channels; }
int getNumChannels (bool inp, int bus) const noexcept { return isPositiveAndBelow (bus, getBusCount (inp)) ? getFilterBus (inp).getReference (bus).channels.size() : 0; }
bool isBusEnabled (bool inputBus, int bus) const noexcept { return (getNumChannels (inputBus, bus) > 0); }
bool hasInputs (int bus) const noexcept { return isBusEnabled (true, bus); }
bool hasOutputs (int bus) const noexcept { return isBusEnabled (false, bus); }
int getNumEnabledBuses (bool inputBus) const noexcept { int i; for (i = 0; i < getBusCount (inputBus); ++i) if (! isBusEnabled (inputBus, i)) break; return i; }
int findTotalNumChannels (bool isInput) const noexcept
{
int total = 0;
const AudioBusArray& ioBuses = getFilterBus (isInput);
for (int i = 0; i < ioBuses.size(); ++i)
total += ioBuses.getReference (i).channels.size();
return total;
}
//==============================================================================
void restoreBusArrangement (const AudioProcessor::AudioBusArrangement& original) const
{
const int numInputBuses = getBusCount (true);
const int numOutputBuses = getBusCount (false);
jassert (original.inputBuses. size() == numInputBuses);
jassert (original.outputBuses.size() == numOutputBuses);
for (int busNr = 0; busNr < numInputBuses; ++busNr)
processor.setPreferredBusArrangement (true, busNr, original.inputBuses.getReference (busNr).channels);
for (int busNr = 0; busNr < numOutputBuses; ++busNr)
processor.setPreferredBusArrangement (false, busNr, original.outputBuses.getReference (busNr).channels);
}
//==============================================================================
Array<SupportedBusLayouts>& getSupportedLayouts (bool isInput) noexcept { return isInput ? inputLayouts : outputLayouts; }
const Array<SupportedBusLayouts>& getSupportedLayouts (bool isInput) const noexcept { return isInput ? inputLayouts : outputLayouts; }
SupportedBusLayouts& getSupportedBusLayouts (bool isInput, int busNr) noexcept { return getSupportedLayouts (isInput).getReference (busNr); }
const SupportedBusLayouts& getSupportedBusLayouts (bool isInput, int busNr) const noexcept { return getSupportedLayouts (isInput).getReference (busNr); }
bool busIgnoresLayout (bool inp, int bus) const noexcept
{
return isPositiveAndBelow (bus, getSupportedLayouts (inp).size()) ? getSupportedBusLayouts (inp, bus).busIgnoresLayout : true;
}
const AudioChannelSet& getDefaultLayoutForBus (bool isInput, int busIdx) const noexcept { return getSupportedBusLayouts (isInput, busIdx).getDefault(); }
bool hasDynamicInBuses() const noexcept { return dynamicInBuses; }
bool hasDynamicOutBuses() const noexcept { return dynamicOutBuses; }
void clear (int inputCount, int outputCount)
{
inputLayouts.clear();
inputLayouts.resize (inputCount);
outputLayouts.clear();
outputLayouts.resize (outputCount);
}
//==============================================================================
AudioChannelSet getDefaultLayoutForChannelNumAndBus (bool isInput, int busIdx, int channelNum) const noexcept
{
if (const AudioChannelSet* set = getSupportedBusLayouts (isInput, busIdx).getDefaultLayoutForChannelNum (channelNum))
return *set;
return AudioChannelSet::canonicalChannelSet (channelNum);
}
void findAllCompatibleLayouts()
{
{
ScopedBusRestorer restorer (*this);
clear (getBusCount (true), getBusCount (false));
for (int i = 0; i < getBusCount (true); ++i) findAllCompatibleLayoutsForBus (true, i);
for (int i = 0; i < getBusCount (false); ++i) findAllCompatibleLayoutsForBus (false, i);
}
// find the defaults
for (int i = 0; i < getBusCount (true); ++i)
updateDefaultLayout (true, i);
for (int i = 0; i < getBusCount (false); ++i)
updateDefaultLayout (false, i);
// can any of the buses be disabled/enabled
dynamicInBuses = doesPlugInHaveDynamicBuses (true);
dynamicOutBuses = doesPlugInHaveDynamicBuses (false);
}
//==============================================================================
void enableAllBuses()
{
for (int busIdx = 1; busIdx < getBusCount (true); ++busIdx)
if (getChannelSet (true, busIdx) == AudioChannelSet::disabled())
processor.setPreferredBusArrangement (true, busIdx, getDefaultLayoutForBus (true, busIdx));
for (int busIdx = 1; busIdx < getBusCount (false); ++busIdx)
if (getChannelSet (false, busIdx) == AudioChannelSet::disabled())
processor.setPreferredBusArrangement (false, busIdx, getDefaultLayoutForBus (false, busIdx));
}
//==============================================================================
// Helper class which restores the original arrangement when it leaves scope
class ScopedBusRestorer
{
public:
ScopedBusRestorer (PluginBusUtilities& bUtils)
: busUtils (bUtils),
originalArr (bUtils.processor.busArrangement),
shouldRestore (true)
{}
~ScopedBusRestorer()
{
if (shouldRestore)
busUtils.restoreBusArrangement (originalArr);
}
void release() noexcept { shouldRestore = false; }
private:
PluginBusUtilities& busUtils;
const AudioProcessor::AudioBusArrangement originalArr;
bool shouldRestore;
JUCE_DECLARE_NON_COPYABLE (ScopedBusRestorer)
};
//==============================================================================
AudioProcessor& processor;
private:
friend class ScopedBusRestorer;
//==============================================================================
Array<SupportedBusLayouts> inputLayouts, outputLayouts;
bool dynamicInBuses, dynamicOutBuses, addDiscreteLayouts;
//==============================================================================
bool busIgnoresLayoutForChannelNum (bool isInput, int busNr, int channelNum)
{
AudioChannelSet set;
// If the plug-in does not complain about setting it's layout to an undefined layout
// then we assume that the plug-in ignores the layout alltogether
for (int i = 0; i < channelNum; ++i)
set.addChannel (static_cast<AudioChannelSet::ChannelType> (SupportedBusLayouts::pseudoChannelBitNum + i));
return processor.setPreferredBusArrangement (isInput, busNr, set);
}
void findAllCompatibleLayoutsForBus (bool isInput, int busNr)
{
const int maxNumChannels = 9;
SupportedBusLayouts& layouts = getSupportedBusLayouts (isInput, busNr);
layouts.supportedLayouts.clear();
// check if the plug-in bus can be disabled
layouts.canBeDisabled = processor.setPreferredBusArrangement (isInput, busNr, AudioChannelSet());
layouts.busIgnoresLayout = true;
for (int i = 1; i <= maxNumChannels; ++i)
{
const bool ignoresLayoutForChannel = busIgnoresLayoutForChannelNum (isInput, busNr, i);
Array<AudioChannelSet> sets = layoutListCompatibleWithChannelCount (addDiscreteLayouts, i);
for (int j = 0; j < sets.size(); ++j)
{
const AudioChannelSet& layout = sets.getReference (j);
if (processor.setPreferredBusArrangement (isInput, busNr, layout))
{
if (! ignoresLayoutForChannel)
layouts.busIgnoresLayout = false;
layouts.supportedLayouts.add (layout);
}
}
}
// You cannot add a bus in your processor wich does not support any layouts! It must at least support one.
jassert (layouts.supportedLayouts.size() > 0);
}
bool doesPlugInHaveDynamicBuses (bool isInput) const
{
for (int i = 0; i < getBusCount (isInput); ++i)
if (getSupportedBusLayouts (isInput, i).canBeDisabled)
return true;
return false;
}
void updateDefaultLayout (bool isInput, int busIdx)
{
SupportedBusLayouts& layouts = getSupportedBusLayouts (isInput, busIdx);
AudioChannelSet set = getChannelSet (isInput, busIdx);
layouts.isEnabledByDefault = (set.size() > 0);
if (layouts.isEnabledByDefault)
layouts.supportedLayouts.add (set);
// If you hit this assertion then you are disabling the main bus by default
// which is unsupported
jassert (layouts.isEnabledByDefault || busIdx >= 0);
if (set == AudioChannelSet())
{
const bool mainBusHasInputs = hasInputs (0);
const bool mainBusHasOutputs = hasOutputs (0);
if (busIdx != 0 && (mainBusHasInputs || mainBusHasOutputs))
{
// the AudioProcessor does not give us any default layout
// for an aux bus. Use the same number of channels as the
// default layout on the main bus as a sensible default for
// the aux bus
const bool useInput = mainBusHasInputs && mainBusHasOutputs ? isInput : mainBusHasInputs;
const AudioChannelSet& dfltLayout = getSupportedBusLayouts (useInput, 0).getDefault();
if ((layouts.defaultLayoutIndex = layouts.supportedLayouts.indexOf (dfltLayout)) >= 0)
return;
// no exact match: try at least to match the number of channels
for (int i = 0; i < layouts.supportedLayouts.size(); ++i)
{
if (layouts.supportedLayouts.getReference (i).size() == dfltLayout.size())
{
layouts.defaultLayoutIndex = i;
return;
}
}
}
if (layouts.busIgnoresLayout)
set = AudioChannelSet::discreteChannels (set.size());
}
layouts.updateDefaultLayout (set);
}
static Array<AudioChannelSet> layoutListCompatibleWithChannelCount (bool addDiscrete, const int channelCount) noexcept
{
jassert (channelCount > 0);
Array<AudioChannelSet> sets;
if (addDiscrete)
sets.add (AudioChannelSet::discreteChannels (channelCount));
switch (channelCount)
{
case 1:
sets.add (AudioChannelSet::mono());
break;
case 2:
sets.add (AudioChannelSet::stereo());
break;
case 4:
sets.add (AudioChannelSet::quadraphonic());
sets.add (AudioChannelSet::ambisonic());
break;
case 5:
sets.add (AudioChannelSet::pentagonal());
sets.add (AudioChannelSet::create5point0());
break;
case 6:
sets.add (AudioChannelSet::hexagonal());
sets.add (AudioChannelSet::create6point0());
break;
case 7:
sets.add (AudioChannelSet::create6point1());
sets.add (AudioChannelSet::create7point0());
sets.add (AudioChannelSet::createFront7point0());
break;
case 8:
sets.add (AudioChannelSet::octagonal());
sets.add (AudioChannelSet::create7point1());
sets.add (AudioChannelSet::createFront7point1());
break;
}
return sets;
}
JUCE_DECLARE_NON_COPYABLE (PluginBusUtilities)
};

+ 2
- 5
libs/juce/source/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp View File

@@ -26,10 +26,7 @@
#include <windows.h>
#endif
// Your project must contain an AppConfig.h file with your project-specific settings in it,
// and your header search path must make it accessible to the module's files.
#include "AppConfig.h"
#include "../../juce_core/system/juce_TargetPlatform.h"
#include "../utility/juce_CheckSettingMacros.h"
#include "juce_IncludeModuleHeaders.h"
@@ -54,7 +51,7 @@ extern "C" BOOL WINAPI DllMain (HINSTANCE instance, DWORD reason, LPVOID reserve
}
#endif
(void) reserved;
ignoreUnused (reserved);
return TRUE;
}


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

Loading…
Cancel
Save