Browse Source

Update to latest juce; Fix crash when reopening lv2 uis

Closes #13
tags/2018-04-16
falkTX 8 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