Browse Source

Update JUCE

Signed-off-by: falkTX <falktx@falktx.com>
juce7
falkTX 3 years ago
parent
commit
e8dc1685ad
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
100 changed files with 3649 additions and 1561 deletions
  1. +6
    -0
      .gitignore
  2. +2
    -3
      libs/juce-current/source/README.md
  3. +126
    -32
      libs/juce-current/source/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h
  4. +83
    -67
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp
  5. +35
    -6
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h
  6. +61
    -22
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp
  7. +145
    -4
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h
  8. +44
    -20
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp
  9. +16
    -3
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h
  10. +130
    -33
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
  11. +866
    -459
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
  12. +135
    -123
      libs/juce-current/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h
  13. +14
    -1
      libs/juce-current/source/modules/juce_audio_basics/juce_audio_basics.cpp
  14. +4
    -3
      libs/juce-current/source/modules/juce_audio_basics/juce_audio_basics.h
  15. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/juce_audio_basics.mm
  16. +29
    -17
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp
  17. +16
    -17
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h
  18. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiDataConcatenator.h
  19. +55
    -59
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp
  20. +7
    -6
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiFile.h
  21. +1
    -5
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp
  22. +1
    -2
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h
  23. +8
    -5
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
  24. +7
    -7
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.h
  25. +489
    -31
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp
  26. +16
    -7
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h
  27. +1
    -6
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp
  28. +3
    -4
      libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.h
  29. +47
    -0
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMP.h
  30. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h
  31. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h
  32. +14
    -2
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h
  33. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h
  34. +6
    -2
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h
  35. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h
  36. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp
  37. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h
  38. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h
  39. +7
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h
  40. +7
    -7
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp
  41. +6
    -2
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h
  42. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPUtils.cpp
  43. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h
  44. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPView.cpp
  45. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPView.h
  46. +7
    -9
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp
  47. +7
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPacket.h
  48. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPackets.h
  49. +61
    -19
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp
  50. +23
    -12
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h
  51. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp
  52. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h
  53. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp
  54. +2
    -2
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPENote.h
  55. +10
    -7
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp
  56. +3
    -4
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h
  57. +18
    -19
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp
  58. +7
    -6
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h
  59. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp
  60. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h
  61. +75
    -14
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp
  62. +7
    -2
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.h
  63. +29
    -9
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp
  64. +7
    -1
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEValue.h
  65. +18
    -8
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp
  66. +102
    -86
      libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h
  67. +144
    -132
      libs/juce-current/source/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h
  68. +80
    -0
      libs/juce-current/source/modules/juce_audio_basics/native/juce_mac_CoreAudioTimeConversions.h
  69. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_AudioSource.h
  70. +87
    -73
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp
  71. +13
    -8
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h
  72. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp
  73. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h
  74. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp
  75. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h
  76. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp
  77. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h
  78. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp
  79. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.h
  80. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h
  81. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp
  82. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h
  83. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp
  84. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h
  85. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp
  86. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h
  87. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
  88. +1
    -9
      libs/juce-current/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h
  89. +128
    -79
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_ADSR.h
  90. +257
    -0
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp
  91. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Decibels.h
  92. +7
    -10
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_GenericInterpolator.h
  93. +23
    -19
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp
  94. +45
    -8
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_IIRFilter.h
  95. +6
    -9
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Interpolators.cpp
  96. +6
    -9
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Interpolators.h
  97. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp
  98. +5
    -1
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Reverb.h
  99. +1
    -1
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp
  100. +9
    -10
      libs/juce-current/source/modules/juce_audio_basics/utilities/juce_SmoothedValue.h

+ 6
- 0
.gitignore View File

@@ -11,6 +11,11 @@
*.orig
*.rej

*.cmake
CMakeCache.txt
CMakeFiles
Makefile

*.lv2
*.make
*.make-e
@@ -74,3 +79,4 @@ libs/juce-*/source/modules/*/native/javaopt/
libs/juce-*/source/modules/*/native/oboe/
libs/juce-*/source/modules/*/native/*_android_*
libs/juce-*/source/modules/*/native/*_ios_*
libs/juce-*/source/tools

+ 2
- 3
libs/juce-current/source/README.md View File

@@ -12,8 +12,7 @@ JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE, ARE DISCLAIMED.

The core JUCE modules (juce_audio_basics, juce_audio_devices, juce_blocks_basics, juce_core
and juce_events) are permissively licensed under the terms of the
[ISC license](http://www.isc.org/downloads/software-support-policy/isc-license/).
The juce_audio_basics, juce_audio_devices, juce_core and juce_events modules
are permissively licensed under the terms of the [ISC license](http://www.isc.org/downloads/software-support-policy/isc-license).
Other modules are covered by the
[GNU General Public License v.3](https://www.gnu.org/licenses/gpl-3.0.en.html).

+ 126
- 32
libs/juce-current/source/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -60,29 +60,121 @@ public:
fpsUnknown = 99
};
/** More descriptive frame rate type. */
class JUCE_API FrameRate
{
public:
/** Creates a frame rate with a base rate of 0. */
FrameRate() = default;
/** Creates a FrameRate instance from a FrameRateType. */
FrameRate (FrameRateType type) : FrameRate (fromType (type)) {}
/** Gets the FrameRateType that matches the state of this FrameRate.
Returns fpsUnknown if this FrameRate cannot be represented by any of the
other enum fields.
*/
FrameRateType getType() const
{
switch (base)
{
case 24: return pulldown ? fps23976 : fps24;
case 25: return fps25;
case 30: return pulldown ? (drop ? fps2997drop : fps2997)
: (drop ? fps30drop : fps30);
case 60: return drop ? fps60drop : fps60;
}
return fpsUnknown;
}
/** Returns the plain rate, without taking pulldown into account. */
int getBaseRate() const { return base; }
/** Returns true if drop-frame timecode is in use. */
bool isDrop() const { return drop; }
/** Returns true if the effective framerate is actually equal to the base rate divided by 1.001 */
bool isPullDown() const { return pulldown; }
/** Returns the actual rate described by this object, taking pulldown into account. */
double getEffectiveRate() const { return pulldown ? (double) base / 1.001 : (double) base; }
/** Returns a copy of this object with the specified base rate. */
JUCE_NODISCARD FrameRate withBaseRate (int x) const { return with (&FrameRate::base, x); }
/** Returns a copy of this object with drop frames enabled or disabled, as specified. */
JUCE_NODISCARD FrameRate withDrop (bool x = true) const { return with (&FrameRate::drop, x); }
/** Returns a copy of this object with pulldown enabled or disabled, as specified. */
JUCE_NODISCARD FrameRate withPullDown (bool x = true) const { return with (&FrameRate::pulldown, x); }
/** Returns true if this instance is equal to other. */
bool operator== (const FrameRate& other) const
{
const auto tie = [] (const FrameRate& x) { return std::tie (x.base, x.drop, x.pulldown); };
return tie (*this) == tie (other);
}
/** Returns true if this instance is not equal to other. */
bool operator!= (const FrameRate& other) const { return ! (*this == other); }
private:
static FrameRate fromType (FrameRateType type)
{
switch (type)
{
case fps23976: return FrameRate().withBaseRate (24).withPullDown();
case fps24: return FrameRate().withBaseRate (24);
case fps25: return FrameRate().withBaseRate (25);
case fps2997: return FrameRate().withBaseRate (30).withPullDown();
case fps30: return FrameRate().withBaseRate (30);
case fps2997drop: return FrameRate().withBaseRate (30).withDrop().withPullDown();
case fps30drop: return FrameRate().withBaseRate (30).withDrop();
case fps60: return FrameRate().withBaseRate (60);
case fps60drop: return FrameRate().withBaseRate (60).withDrop();
case fpsUnknown: break;
}
return {};
}
template <typename Member, typename Value>
FrameRate with (Member&& member, Value&& value) const
{
auto copy = *this;
copy.*member = std::forward<Value> (value);
return copy;
}
int base = 0;
bool drop = false, pulldown = false;
};
//==============================================================================
/** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method.
*/
struct JUCE_API CurrentPositionInfo
{
/** The tempo in BPM */
double bpm;
double bpm = 120.0;
/** Time signature numerator, e.g. the 3 of a 3/4 time sig */
int timeSigNumerator;
int timeSigNumerator = 4;
/** Time signature denominator, e.g. the 4 of a 3/4 time sig */
int timeSigDenominator;
int timeSigDenominator = 4;
/** The current play position, in samples from the start of the timeline. */
int64 timeInSamples;
int64 timeInSamples = 0;
/** The current play position, in seconds from the start of the timeline. */
double timeInSeconds;
double timeInSeconds = 0;
/** For timecode, the position of the start of the timeline, in seconds from 00:00:00:00. */
double editOriginTime;
double editOriginTime = 0;
/** The current play position, in units of quarter-notes. */
double ppqPosition;
double ppqPosition = 0;
/** The position of the start of the last bar, in units of quarter-notes.
@@ -92,51 +184,56 @@ public:
Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If
it's not available, the value will be 0.
*/
double ppqPositionOfLastBarStart;
double ppqPositionOfLastBarStart = 0;
/** The video frame rate, if applicable. */
FrameRateType frameRate;
FrameRate frameRate = FrameRateType::fps23976;
/** True if the transport is currently playing. */
bool isPlaying;
bool isPlaying = false;
/** True if the transport is currently recording.
(When isRecording is true, then isPlaying will also be true).
*/
bool isRecording;
bool isRecording = false;
/** The current cycle start position in units of quarter-notes.
Note that not all hosts or plugin formats may provide this value.
@see isLooping
*/
double ppqLoopStart;
double ppqLoopStart = 0;
/** The current cycle end position in units of quarter-notes.
Note that not all hosts or plugin formats may provide this value.
@see isLooping
*/
double ppqLoopEnd;
double ppqLoopEnd = 0;
/** True if the transport is currently looping. */
bool isLooping;
bool isLooping = false;
//==============================================================================
bool operator== (const CurrentPositionInfo& other) const noexcept
{
return timeInSamples == other.timeInSamples
&& ppqPosition == other.ppqPosition
&& editOriginTime == other.editOriginTime
&& ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart
&& frameRate == other.frameRate
&& isPlaying == other.isPlaying
&& isRecording == other.isRecording
&& bpm == other.bpm
&& timeSigNumerator == other.timeSigNumerator
&& timeSigDenominator == other.timeSigDenominator
&& ppqLoopStart == other.ppqLoopStart
&& ppqLoopEnd == other.ppqLoopEnd
&& isLooping == other.isLooping;
const auto tie = [] (const CurrentPositionInfo& i)
{
return std::tie (i.timeInSamples,
i.ppqPosition,
i.editOriginTime,
i.ppqPositionOfLastBarStart,
i.frameRate,
i.isPlaying,
i.isRecording,
i.bpm,
i.timeSigNumerator,
i.timeSigDenominator,
i.ppqLoopStart,
i.ppqLoopEnd,
i.isLooping);
};
return tie (*this) == tie (other);
}
bool operator!= (const CurrentPositionInfo& other) const noexcept
@@ -146,10 +243,7 @@ public:
void resetToDefault()
{
zerostruct (*this);
timeSigNumerator = 4;
timeSigDenominator = 4;
bpm = 120;
*this = CurrentPositionInfo{};
}
};


+ 83
- 67
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -29,7 +29,7 @@ AudioChannelSet::AudioChannelSet (uint32 c) : channels (static_cast<int64> (c))
{
}
AudioChannelSet::AudioChannelSet (const Array<ChannelType>& c)
AudioChannelSet::AudioChannelSet (const std::initializer_list<ChannelType>& c)
{
for (auto channel : c)
addChannel (channel);
@@ -339,6 +339,8 @@ String AudioChannelSet::getDescription() const
if (*this == create5point0()) return "5.0 Surround";
if (*this == create5point1()) return "5.1 Surround";
if (*this == create5point1point2()) return "5.1.2 Surround";
if (*this == create5point1point4()) return "5.1.4 Surround";
if (*this == create6point0()) return "6.0 Surround";
if (*this == create6point1()) return "6.1 Surround";
if (*this == create6point0Music()) return "6.0 (Music) Surround";
@@ -348,7 +350,11 @@ String AudioChannelSet::getDescription() const
if (*this == create7point0SDDS()) return "7.0 Surround SDDS";
if (*this == create7point1SDDS()) return "7.1 Surround SDDS";
if (*this == create7point0point2()) return "7.0.2 Surround";
if (*this == create7point0point4()) return "7.0.4 Surround";
if (*this == create7point1point2()) return "7.1.2 Surround";
if (*this == create7point1point4()) return "7.1.4 Surround";
if (*this == create7point1point6()) return "7.1.6 Surround";
if (*this == create9point1point6()) return "9.1.6 Surround";
if (*this == quadraphonic()) return "Quadraphonic";
if (*this == pentagonal()) return "Pentagonal";
@@ -442,29 +448,33 @@ void AudioChannelSet::removeChannel (ChannelType newChannel)
}
AudioChannelSet AudioChannelSet::disabled() { return {}; }
AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (1u << centre); }
AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); }
AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); }
AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surround)); }
AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); }
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround)); }
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround)); }
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); }
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); }
AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); }
AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); }
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); }
AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); }
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); }
AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); }
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround)); }
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); }
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << centreSurround) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); }
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround) | (1u << wideLeft) | (1u << wideRight)); }
AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); }
AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); }
AudioChannelSet AudioChannelSet::create7point0point4() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topFrontLeft) | (1u << topFrontRight) | (1u << topRearLeft) | (1u << topRearRight)); }
AudioChannelSet AudioChannelSet::create7point1point4() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topFrontLeft) | (1u << topFrontRight) | (1u << topRearLeft) | (1u << topRearRight)); }
AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet ({ centre }); }
AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ({ left, right }); }
AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ({ left, right, centre }); }
AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ({ left, right, surround }); }
AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ({ left, right, centre, surround }); }
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround }); }
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround }); }
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, centreSurround }); }
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, centreSurround }); }
AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ({ left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide }); }
AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ({ left, right, LFE, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide }); }
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear }); }
AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre }); }
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear }); }
AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre }); }
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ({ left, right, leftSurround, rightSurround }); }
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ({ left, right, centre, leftSurroundRear, rightSurroundRear }); }
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ({ left, right, centre, centreSurround, leftSurroundRear, rightSurroundRear }); }
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ({ left, right, centre, leftSurround, rightSurround, centreSurround, wideLeft, wideRight }); }
AudioChannelSet AudioChannelSet::create5point1point2() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight }); }
AudioChannelSet AudioChannelSet::create5point1point4() { return AudioChannelSet ({ left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); }
AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight }); }
AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight }); }
AudioChannelSet AudioChannelSet::create7point0point4() { return AudioChannelSet ({ left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); }
AudioChannelSet AudioChannelSet::create7point1point4() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight }); }
AudioChannelSet AudioChannelSet::create7point1point6() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); }
AudioChannelSet AudioChannelSet::create9point1point6() { return AudioChannelSet ({ left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight }); }
AudioChannelSet AudioChannelSet::ambisonic (int order)
{
@@ -534,49 +544,55 @@ Array<AudioChannelSet> AudioChannelSet::channelSetsWithNumberOfChannels (int num
{
retval.add (AudioChannelSet::discreteChannels (numChannels));
if (numChannels == 1)
retval.addArray ([numChannels]() -> Array<AudioChannelSet>
{
retval.add (AudioChannelSet::mono());
}
else if (numChannels == 2)
{
retval.add (AudioChannelSet::stereo());
}
else if (numChannels == 3)
{
retval.add (AudioChannelSet::createLCR());
retval.add (AudioChannelSet::createLRS());
}
else if (numChannels == 4)
{
retval.add (AudioChannelSet::quadraphonic());
retval.add (AudioChannelSet::createLCRS());
}
else if (numChannels == 5)
{
retval.add (AudioChannelSet::create5point0());
retval.add (AudioChannelSet::pentagonal());
}
else if (numChannels == 6)
{
retval.add (AudioChannelSet::create5point1());
retval.add (AudioChannelSet::create6point0());
retval.add (AudioChannelSet::create6point0Music());
retval.add (AudioChannelSet::hexagonal());
}
else if (numChannels == 7)
{
retval.add (AudioChannelSet::create7point0());
retval.add (AudioChannelSet::create7point0SDDS());
retval.add (AudioChannelSet::create6point1());
retval.add (AudioChannelSet::create6point1Music());
}
else if (numChannels == 8)
{
retval.add (AudioChannelSet::create7point1());
retval.add (AudioChannelSet::create7point1SDDS());
retval.add (AudioChannelSet::octagonal());
}
switch (numChannels)
{
case 1:
return { AudioChannelSet::mono() };
case 2:
return { AudioChannelSet::stereo() };
case 3:
return { AudioChannelSet::createLCR(),
AudioChannelSet::createLRS() };
case 4:
return { AudioChannelSet::quadraphonic(),
AudioChannelSet::createLCRS() };
case 5:
return { AudioChannelSet::create5point0(),
AudioChannelSet::pentagonal() };
case 6:
return { AudioChannelSet::create5point1(),
AudioChannelSet::create6point0(),
AudioChannelSet::create6point0Music(),
AudioChannelSet::hexagonal() };
case 7:
return { AudioChannelSet::create7point0(),
AudioChannelSet::create7point0SDDS(),
AudioChannelSet::create6point1(),
AudioChannelSet::create6point1Music() };
case 8:
return { AudioChannelSet::create7point1(),
AudioChannelSet::create7point1SDDS(),
AudioChannelSet::octagonal(),
AudioChannelSet::create5point1point2() };
case 9:
return { AudioChannelSet::create7point0point2() };
case 10:
return { AudioChannelSet::create5point1point4(),
AudioChannelSet::create7point1point2() };
case 11:
return { AudioChannelSet::create7point0point4() };
case 12:
return { AudioChannelSet::create7point1point4() };
case 14:
return { AudioChannelSet::create7point1point6() };
case 16:
return { AudioChannelSet::create9point1point6() };
}
return {};
}());
auto order = getAmbisonicOrderForNumChannels (numChannels);
if (order >= 0)


+ 35
- 6
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -196,6 +196,18 @@ public:
*/
static AudioChannelSet JUCE_CALLTYPE create7point1SDDS();
/** Creates a set for a 5.1.2 surround setup (left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight).
Is equivalent to: kAudioChannelLayoutTag_Atmos_5_1_2 (CoreAudio).
*/
static AudioChannelSet JUCE_CALLTYPE create5point1point2();
/** Creates a set for a 5.1.4 surround setup (left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight).
Is equivalent to: kAudioChannelLayoutTag_Atmos_5_1_4 (CoreAudio).
*/
static AudioChannelSet JUCE_CALLTYPE create5point1point4();
/** Creates a set for Dolby Atmos 7.0.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight).
Is equivalent to: n/a (VST), AAX_eStemFormat_7_0_2 (AAX), n/a (CoreAudio)
@@ -204,7 +216,7 @@ public:
/** Creates a set for Dolby Atmos 7.1.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topSideLeft, topSideRight).
Is equivalent to: k71_2 (VST), AAX_eStemFormat_7_1_2 (AAX), n/a (CoreAudio)
Is equivalent to: k71_2 (VST), AAX_eStemFormat_7_1_2 (AAX), kAudioChannelLayoutTag_Atmos_7_1_2 (CoreAudio)
*/
static AudioChannelSet JUCE_CALLTYPE create7point1point2();
@@ -216,10 +228,27 @@ public:
/** Creates a set for Dolby Atmos 7.1.4 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topFrontLeft, topFrontRight, topRearLeft, topRearRight).
Is equivalent to: k71_4 (VST), n/a (AAX), n/a (CoreAudio)
Is equivalent to: k71_4 (VST), n/a (AAX), kAudioChannelLayoutTag_Atmos_7_1_4 (CoreAudio)
*/
static AudioChannelSet JUCE_CALLTYPE create7point1point4();
/** Creates a set for Dolby Atmos 7.1.6 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight).
Is equivalent to: k71_6 (VST), n/a (AAX), n/a (CoreAudio)
*/
static AudioChannelSet JUCE_CALLTYPE create7point1point6();
/** Creates a set for a 9.1.6 surround setup (left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight).
Note that the VST3 layout arranges the front speakers "L Lc C Rc R", but the JUCE layout
uses the arrangement "wideLeft left centre right wideRight". To maintain the relative
positions of the speakers, the channels will be remapped accordingly. This means that the
VST3 host's "L" channel will be received on a JUCE plugin's "wideLeft" channel, the
"Lc" channel will be received on a JUCE plugin's "left" channel, and so on.
Is equivalent to: k91_6 (VST3), kAudioChannelLayoutTag_Atmos_9_1_6 (CoreAudio).
*/
static AudioChannelSet JUCE_CALLTYPE create9point1point6();
//==============================================================================
/** Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround)
@@ -318,8 +347,8 @@ public:
//==============================================================================
// Used by Dolby Atmos 7.0.2 and 7.1.2
topSideLeft = 28, /**< Lts (AAX), Tsl (VST) channel for Dolby Atmos. */
topSideRight = 29, /**< Rts (AAX), Tsr (VST) channel for Dolby Atmos. */
topSideLeft = 28, /**< Lts (AAX), Tsl (VST), Ltm (AU) channel for Dolby Atmos. */
topSideRight = 29, /**< Rts (AAX), Tsr (VST), Rtm (AU) channel for Dolby Atmos. */
//==============================================================================
// Ambisonic ACN formats - all channels are SN3D normalised
@@ -487,7 +516,7 @@ private:
//==============================================================================
explicit AudioChannelSet (uint32);
explicit AudioChannelSet (const Array<ChannelType>&);
explicit AudioChannelSet (const std::initializer_list<ChannelType>&);
//==============================================================================
static int JUCE_CALLTYPE getAmbisonicOrderForNumChannels (int);


+ 61
- 22
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -23,6 +23,9 @@
namespace juce
{
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample)
{
auto maxVal = (double) 0x7fff;
@@ -431,35 +434,22 @@ void AudioDataConverters::convertFormatToFloat (DataFormat sourceFormat, const v
//==============================================================================
void AudioDataConverters::interleaveSamples (const float** source, float* dest, int numSamples, int numChannels)
{
for (int chan = 0; chan < numChannels; ++chan)
{
auto i = chan;
auto src = source [chan];
using Format = AudioData::Format<AudioData::Float32, AudioData::NativeEndian>;
for (int j = 0; j < numSamples; ++j)
{
dest [i] = src [j];
i += numChannels;
}
}
AudioData::interleaveSamples (AudioData::NonInterleavedSource<Format> { source, numChannels },
AudioData::InterleavedDest<Format> { dest, numChannels },
numSamples);
}
void AudioDataConverters::deinterleaveSamples (const float* source, float** dest, int numSamples, int numChannels)
{
for (int chan = 0; chan < numChannels; ++chan)
{
auto i = chan;
auto dst = dest [chan];
using Format = AudioData::Format<AudioData::Float32, AudioData::NativeEndian>;
for (int j = 0; j < numSamples; ++j)
{
dst [j] = source [i];
i += numChannels;
}
}
AudioData::deinterleaveSamples (AudioData::InterleavedSource<Format> { source, numChannels },
AudioData::NonInterleavedDest<Format> { dest, numChannels },
numSamples);
}
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS
@@ -480,6 +470,7 @@ public:
test (unitTest, true, r);
}
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
static void test (UnitTest& unitTest, bool inPlace, Random& r)
{
const int numSamples = 2048;
@@ -537,6 +528,7 @@ public:
unitTest.expect (biggestDiff <= errorMargin);
}
}
JUCE_END_IGNORE_WARNINGS_MSVC
};
template <class F1, class E1, class FormatType>
@@ -586,6 +578,50 @@ public:
Test1 <AudioData::Int32>::test (*this, r);
beginTest ("Round-trip conversion: Float32");
Test1 <AudioData::Float32>::test (*this, r);
using Format = AudioData::Format<AudioData::Float32, AudioData::NativeEndian>;
beginTest ("Interleaving");
{
constexpr auto numChannels = 4;
constexpr auto numSamples = 512;
AudioBuffer<float> sourceBuffer { numChannels, numSamples },
destBuffer { 1, numChannels * numSamples };
for (int ch = 0; ch < numChannels; ++ch)
for (int i = 0; i < numSamples; ++i)
sourceBuffer.setSample (ch, i, r.nextFloat());
AudioData::interleaveSamples (AudioData::NonInterleavedSource<Format> { sourceBuffer.getArrayOfReadPointers(), numChannels },
AudioData::InterleavedDest<Format> { destBuffer.getWritePointer (0), numChannels },
numSamples);
for (int ch = 0; ch < numChannels; ++ch)
for (int i = 0; i < numSamples; ++i)
expect (destBuffer.getSample (0, ch + (i * numChannels)) == sourceBuffer.getSample (ch, i));
}
beginTest ("Deinterleaving");
{
constexpr auto numChannels = 4;
constexpr auto numSamples = 512;
AudioBuffer<float> sourceBuffer { 1, numChannels * numSamples },
destBuffer { numChannels, numSamples };
for (int ch = 0; ch < numChannels; ++ch)
for (int i = 0; i < numSamples; ++i)
sourceBuffer.setSample (0, ch + (i * numChannels), r.nextFloat());
AudioData::deinterleaveSamples (AudioData::InterleavedSource<Format> { sourceBuffer.getReadPointer (0), numChannels },
AudioData::NonInterleavedDest<Format> { destBuffer.getArrayOfWritePointers(), numChannels },
numSamples);
for (int ch = 0; ch < numChannels; ++ch)
for (int i = 0; i < numSamples; ++i)
expect (sourceBuffer.getSample (0, ch + (i * numChannels)) == destBuffer.getSample (ch, i));
}
}
};
@@ -593,4 +629,7 @@ static AudioConversionTests audioConversionUnitTests;
#endif
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
} // namespace juce

+ 145
- 4
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -639,11 +639,152 @@ public:
const int sourceChannels, destChannels;
};
};
//==============================================================================
/** A struct that contains a SampleFormat and Endianness to be used with the source and
destination types when calling the interleaveSamples() and deinterleaveSamples() helpers.
@see interleaveSamples, deinterleaveSamples
*/
template <typename DataFormatIn, typename EndiannessIn>
struct Format
{
using DataFormat = DataFormatIn;
using Endianness = EndiannessIn;
};
private:
template <bool IsInterleaved, bool IsConst, typename...>
struct ChannelDataSubtypes;
template <bool IsInterleaved, bool IsConst, typename DataFormat, typename Endianness>
struct ChannelDataSubtypes<IsInterleaved, IsConst, DataFormat, Endianness>
{
using ElementType = std::remove_pointer_t<decltype (DataFormat::data)>;
using ChannelType = std::conditional_t<IsConst, const ElementType*, ElementType*>;
using DataType = std::conditional_t<IsInterleaved, ChannelType, ChannelType*>;
using PointerType = Pointer<DataFormat,
Endianness,
std::conditional_t<IsInterleaved, Interleaved, NonInterleaved>,
std::conditional_t<IsConst, Const, NonConst>>;
};
template <bool IsInterleaved, bool IsConst, typename DataFormat, typename Endianness>
struct ChannelDataSubtypes<IsInterleaved, IsConst, Format<DataFormat, Endianness>>
{
using Subtypes = ChannelDataSubtypes<IsInterleaved, IsConst, DataFormat, Endianness>;
using DataType = typename Subtypes::DataType;
using PointerType = typename Subtypes::PointerType;
};
template <bool IsInterleaved, bool IsConst, typename... Format>
struct ChannelData
{
using Subtypes = ChannelDataSubtypes<IsInterleaved, IsConst, Format...>;
using DataType = typename Subtypes::DataType;
using PointerType = typename Subtypes::PointerType;
DataType data;
int channels;
};
public:
//==============================================================================
/** A sequence of interleaved samples used as the source for the deinterleaveSamples() method. */
template <typename... Format> using InterleavedSource = ChannelData<true, true, Format...>;
/** A sequence of interleaved samples used as the destination for the interleaveSamples() method. */
template <typename... Format> using InterleavedDest = ChannelData<true, false, Format...>;
/** A sequence of non-interleaved samples used as the source for the interleaveSamples() method. */
template <typename... Format> using NonInterleavedSource = ChannelData<false, true, Format...>;
/** A sequence of non-interleaved samples used as the destination for the deinterleaveSamples() method. */
template <typename... Format> using NonInterleavedDest = ChannelData<false, false, Format...>;
/** A helper function for converting a sequence of samples from a non-interleaved source
to an interleaved destination.
When calling this method you need to specify the source and destination data format and endianness
from the AudioData SampleFormat and Endianness types and provide the data and number of channels
for each. For example, to convert a floating-point stream of big endian samples to an interleaved,
native endian stream of 16-bit integer samples you would do the following:
@code
using SourceFormat = AudioData::Format<AudioData::Float32, AudioData::BigEndian>;
using DestFormat = AudioData::Format<AudioData::Int16, AudioData::NativeEndian>;
AudioData::interleaveSamples (AudioData::NonInterleavedSource<SourceFormat> { sourceData, numSourceChannels },
AudioData::InterleavedDest<DestFormat> { destData, numDestChannels },
numSamples);
@endcode
*/
template <typename... SourceFormat, typename... DestFormat>
static void interleaveSamples (NonInterleavedSource<SourceFormat...> source,
InterleavedDest<DestFormat...> dest,
int numSamples)
{
using SourceType = typename decltype (source)::PointerType;
using DestType = typename decltype (dest) ::PointerType;
for (int i = 0; i < dest.channels; ++i)
{
const DestType destType (addBytesToPointer (dest.data, i * DestType::getBytesPerSample()), dest.channels);
if (i < source.channels)
{
if (*source.data != nullptr)
{
destType.convertSamples (SourceType { *source.data }, numSamples);
++source.data;
}
}
else
{
destType.clearSamples (numSamples);
}
}
}
/** A helper function for converting a sequence of samples from an interleaved source
to a non-interleaved destination.
When calling this method you need to specify the source and destination data format and endianness
from the AudioData SampleFormat and Endianness types and provide the data and number of channels
for each. For example, to convert a floating-point stream of big endian samples to an non-interleaved,
native endian stream of 16-bit integer samples you would do the following:
@code
using SourceFormat = AudioData::Format<AudioData::Float32, AudioData::BigEndian>;
using DestFormat = AudioData::Format<AudioData::Int16, AudioData::NativeEndian>;
AudioData::deinterleaveSamples (AudioData::InterleavedSource<SourceFormat> { sourceData, numSourceChannels },
AudioData::NonInterleavedDest<DestFormat> { destData, numDestChannels },
numSamples);
@endcode
*/
template <typename... SourceFormat, typename... DestFormat>
static void deinterleaveSamples (InterleavedSource<SourceFormat...> source,
NonInterleavedDest<DestFormat...> dest,
int numSamples)
{
using SourceType = typename decltype (source)::PointerType;
using DestType = typename decltype (dest) ::PointerType;
for (int i = 0; i < dest.channels; ++i)
{
if (auto* targetChan = dest.data[i])
{
const DestType destType (targetChan);
if (i < source.channels)
destType.convertSamples (SourceType (addBytesToPointer (source.data, i * SourceType::getBytesPerSample()), source.channels), numSamples);
else
destType.clearSamples (numSamples);
}
}
}
};
//==============================================================================
#ifndef DOXYGEN
/**
A set of routines to convert buffers of 32-bit floating point data to and from
various integer formats.
@@ -653,7 +794,7 @@ public:
@tags{Audio}
*/
class JUCE_API AudioDataConverters
class [[deprecated]] JUCE_API AudioDataConverters
{
public:
//==============================================================================
@@ -710,7 +851,7 @@ public:
private:
AudioDataConverters();
JUCE_DECLARE_NON_COPYABLE (AudioDataConverters)
};
#endif
} // namespace juce

+ 44
- 20
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -23,8 +23,8 @@
namespace juce
{
AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() {}
AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() {}
AudioProcessLoadMeasurer::AudioProcessLoadMeasurer() = default;
AudioProcessLoadMeasurer::~AudioProcessLoadMeasurer() = default;
void AudioProcessLoadMeasurer::reset()
{
@@ -33,43 +33,67 @@ void AudioProcessLoadMeasurer::reset()
void AudioProcessLoadMeasurer::reset (double sampleRate, int blockSize)
{
cpuUsageMs = 0;
const SpinLock::ScopedLockType lock (mutex);
cpuUsageProportion = 0;
xruns = 0;
if (sampleRate > 0.0 && blockSize > 0)
{
msPerBlock = 1000.0 * blockSize / sampleRate;
timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0;
}
else
{
msPerBlock = 0;
timeToCpuScale = 0;
}
samplesPerBlock = blockSize;
msPerSample = (sampleRate > 0.0 && blockSize > 0) ? 1000.0 / sampleRate : 0;
}
void AudioProcessLoadMeasurer::registerBlockRenderTime (double milliseconds)
{
const double filterAmount = 0.2;
cpuUsageMs += filterAmount * (milliseconds - cpuUsageMs);
const SpinLock::ScopedTryLockType lock (mutex);
if (lock.isLocked())
registerRenderTimeLocked (milliseconds, samplesPerBlock);
}
void AudioProcessLoadMeasurer::registerRenderTime (double milliseconds, int numSamples)
{
const SpinLock::ScopedTryLockType lock (mutex);
if (lock.isLocked())
registerRenderTimeLocked (milliseconds, numSamples);
}
if (milliseconds > msPerBlock)
void AudioProcessLoadMeasurer::registerRenderTimeLocked (double milliseconds, int numSamples)
{
if (msPerSample == 0)
return;
const auto maxMilliseconds = numSamples * msPerSample;
const auto usedProportion = milliseconds / maxMilliseconds;
const auto filterAmount = 0.2;
const auto proportion = cpuUsageProportion.load();
cpuUsageProportion = proportion + filterAmount * (usedProportion - proportion);
if (milliseconds > maxMilliseconds)
++xruns;
}
double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); }
double AudioProcessLoadMeasurer::getLoadAsProportion() const { return jlimit (0.0, 1.0, cpuUsageProportion.load()); }
double AudioProcessLoadMeasurer::getLoadAsPercentage() const { return 100.0 * getLoadAsProportion(); }
int AudioProcessLoadMeasurer::getXRunCount() const { return xruns; }
AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p)
: owner (p), startTime (Time::getMillisecondCounterHiRes())
: ScopedTimer (p, p.samplesPerBlock)
{
}
AudioProcessLoadMeasurer::ScopedTimer::ScopedTimer (AudioProcessLoadMeasurer& p, int numSamplesInBlock)
: owner (p), startTime (Time::getMillisecondCounterHiRes()), samplesInBlock (numSamplesInBlock)
{
// numSamplesInBlock should never be zero. Did you remember to call AudioProcessLoadMeasurer::reset(),
// passing the expected samples per block?
jassert (numSamplesInBlock);
}
AudioProcessLoadMeasurer::ScopedTimer::~ScopedTimer()
{
owner.registerBlockRenderTime (Time::getMillisecondCounterHiRes() - startTime);
owner.registerRenderTime (Time::getMillisecondCounterHiRes() - startTime, samplesInBlock);
}
} // namespace juce

+ 16
- 3
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioProcessLoadMeasurer.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -72,11 +72,13 @@ public:
struct JUCE_API ScopedTimer
{
ScopedTimer (AudioProcessLoadMeasurer&);
ScopedTimer (AudioProcessLoadMeasurer&, int numSamplesInBlock);
~ScopedTimer();
private:
AudioProcessLoadMeasurer& owner;
double startTime;
int samplesInBlock;
JUCE_DECLARE_NON_COPYABLE (ScopedTimer)
};
@@ -87,9 +89,20 @@ public:
*/
void registerBlockRenderTime (double millisecondsTaken);
/** Can be called manually to add the time of a callback to the stats.
Normally you probably would never call this - it's simpler and more robust to
use a ScopedTimer to measure the time using an RAII pattern.
*/
void registerRenderTime (double millisecondsTaken, int numSamples);
private:
double cpuUsageMs = 0, timeToCpuScale = 0, msPerBlock = 0;
int xruns = 0;
void registerRenderTimeLocked (double, int);
SpinLock mutex;
int samplesPerBlock = 0;
double msPerSample = 0;
std::atomic<double> cpuUsageProportion { 0 };
std::atomic<int> xruns { 0 };
};


+ 130
- 33
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,11 +20,6 @@
==============================================================================
*/
#ifndef __clang__
// GCC4 compatibility
namespace std { using ::max_align_t; }
#endif
namespace juce
{
@@ -197,6 +192,7 @@ public:
}
/** Copies another buffer onto this one.
This buffer's size will be changed to that of the other buffer.
*/
AudioBuffer& operator= (const AudioBuffer& other)
@@ -222,17 +218,18 @@ public:
}
/** Destructor.
This will free any memory allocated by the buffer.
*/
~AudioBuffer() = default;
/** Move constructor */
/** Move constructor. */
AudioBuffer (AudioBuffer&& other) noexcept
: numChannels (other.numChannels),
size (other.size),
allocatedBytes (other.allocatedBytes),
allocatedData (std::move (other.allocatedData)),
isClear (other.isClear.load())
isClear (other.isClear)
{
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
{
@@ -251,14 +248,14 @@ public:
other.allocatedBytes = 0;
}
/** Move assignment */
/** Move assignment. */
AudioBuffer& operator= (AudioBuffer&& other) noexcept
{
numChannels = other.numChannels;
size = other.size;
allocatedBytes = other.allocatedBytes;
allocatedData = std::move (other.allocatedData);
isClear = other.isClear.load();
isClear = other.isClear;
if (numChannels < (int) numElementsInArray (preallocatedChannelSpace))
{
@@ -280,18 +277,22 @@ public:
//==============================================================================
/** Returns the number of channels of audio data that this buffer contains.
@see getNumSamples, getReadPointer, getWritePointer
*/
int getNumChannels() const noexcept { return numChannels; }
/** Returns the number of samples allocated in each of the buffer's channels.
@see getNumChannels, getReadPointer, getWritePointer
*/
int getNumSamples() const noexcept { return size; }
/** Returns a pointer to an array of read-only samples in one of the buffer's channels.
For speed, this doesn't check whether the channel number is out of range,
so be careful when using it!
If you need to write to the data, do NOT call this method and const_cast the
result! Instead, you must call getWritePointer so that the buffer knows you're
planning on modifying the data.
@@ -303,8 +304,10 @@ public:
}
/** Returns a pointer to an array of read-only samples in one of the buffer's channels.
For speed, this doesn't check whether the channel number or index are out of range,
so be careful when using it!
If you need to write to the data, do NOT call this method and const_cast the
result! Instead, you must call getWritePointer so that the buffer knows you're
planning on modifying the data.
@@ -317,10 +320,20 @@ public:
}
/** Returns a writeable pointer to one of the buffer's channels.
For speed, this doesn't check whether the channel number is out of range,
so be careful when using it!
Note that if you're not planning on writing to the data, you should always
use getReadPointer instead.
This will mark the buffer as not cleared and the hasBeenCleared method will return
false after this call. If you retain this write pointer and write some data to
the buffer after calling its clear method, subsequent clear calls will do nothing.
To avoid this either call this method each time you need to write data, or use the
setNotClear method to force the internal cleared flag to false.
@see setNotClear
*/
Type* getWritePointer (int channelNumber) noexcept
{
@@ -330,10 +343,20 @@ public:
}
/** Returns a writeable pointer to one of the buffer's channels.
For speed, this doesn't check whether the channel number or index are out of range,
so be careful when using it!
Note that if you're not planning on writing to the data, you should
use getReadPointer instead.
This will mark the buffer as not cleared and the hasBeenCleared method will return
false after this call. If you retain this write pointer and write some data to
the buffer after calling its clear method, subsequent clear calls will do nothing.
To avoid this either call this method each time you need to write data, or use the
setNotClear method to force the internal cleared flag to false.
@see setNotClear
*/
Type* getWritePointer (int channelNumber, int sampleIndex) noexcept
{
@@ -354,6 +377,14 @@ public:
Don't modify any of the pointers that are returned, and bear in mind that
these will become invalid if the buffer is resized.
This will mark the buffer as not cleared and the hasBeenCleared method will return
false after this call. If you retain this write pointer and write some data to
the buffer after calling its clear method, subsequent clear calls will do nothing.
To avoid this either call this method each time you need to write data, or use the
setNotClear method to force the internal cleared flag to false.
@see setNotClear
*/
Type** getArrayOfWritePointers() noexcept { isClear = false; return channels; }
@@ -362,23 +393,23 @@ public:
This can expand or contract the buffer's length, and add or remove channels.
If keepExistingContent is true, it will try to preserve as much of the
old data as it can in the new buffer.
If clearExtraSpace is true, then any extra channels or space that is
allocated will be also be cleared. If false, then this space is left
uninitialised.
If avoidReallocating is true, then changing the buffer's size won't reduce the
amount of memory that is currently allocated (but it will still increase it if
the new size is bigger than the amount it currently has). If this is false, then
a new allocation will be done so that the buffer uses takes up the minimum amount
of memory that it needs.
Note that if keepExistingContent and avoidReallocating are both true, then it will
only avoid reallocating if neither the channel count or length in samples increase.
If the required memory can't be allocated, this will throw a std::bad_alloc exception.
@param newNumChannels the new number of channels.
@param newNumSamples the new number of samples.
@param keepExistingContent if this is true, it will try to preserve as much of the
old data as it can in the new buffer.
@param clearExtraSpace if this is true, then any extra channels or space that is
allocated will be also be cleared. If false, then this space is left
uninitialised.
@param avoidReallocating if this is true, then changing the buffer's size won't reduce the
amount of memory that is currently allocated (but it will still
increase it if the new size is bigger than the amount it currently has).
If this is false, then a new allocation will be done so that the buffer
uses takes up the minimum amount of memory that it needs.
*/
void setSize (int newNumChannels,
int newNumSamples,
@@ -469,6 +500,8 @@ public:
will re-allocate memory internally and copy the existing data to this new area,
so it will then stop directly addressing this memory.
The hasBeenCleared method will return false after this call.
@param dataToReferTo a pre-allocated array containing pointers to the data
for each channel that should be used by this buffer. The
buffer will only refer to this memory, it won't try to delete
@@ -509,6 +542,8 @@ public:
will re-allocate memory internally and copy the existing data to this new area,
so it will then stop directly addressing this memory.
The hasBeenCleared method will return false after this call.
@param dataToReferTo a pre-allocated array containing pointers to the data
for each channel that should be used by this buffer. The
buffer will only refer to this memory, it won't try to delete
@@ -526,8 +561,12 @@ public:
}
/** Resizes this buffer to match the given one, and copies all of its content across.
The source buffer can contain a different floating point type, so this can be used to
convert between 32 and 64 bit float buffer types.
The hasBeenCleared method will return false after this call if the other buffer
contains data.
*/
template <typename OtherType>
void makeCopyOf (const AudioBuffer<OtherType>& other, bool avoidReallocating = false)
@@ -554,7 +593,13 @@ public:
}
//==============================================================================
/** Clears all the samples in all channels. */
/** Clears all the samples in all channels and marks the buffer as cleared.
This method will do nothing if the buffer has been marked as cleared (i.e. the
hasBeenCleared method returns true.)
@see hasBeenCleared, setNotClear
*/
void clear() noexcept
{
if (! isClear)
@@ -568,8 +613,15 @@ public:
/** Clears a specified region of all the channels.
This will mark the buffer as cleared if the entire buffer contents are cleared.
For speed, this doesn't check whether the channel and sample number
are in-range, so be careful!
This method will do nothing if the buffer has been marked as cleared (i.e. the
hasBeenCleared method returns true.)
@see hasBeenCleared, setNotClear
*/
void clear (int startSample, int numSamples) noexcept
{
@@ -577,11 +629,10 @@ public:
if (! isClear)
{
if (startSample == 0 && numSamples == size)
isClear = true;
for (int i = 0; i < numChannels; ++i)
FloatVectorOperations::clear (channels[i] + startSample, numSamples);
isClear = (startSample == 0 && numSamples == size);
}
}
@@ -589,6 +640,11 @@ public:
For speed, this doesn't check whether the channel and sample number
are in-range, so be careful!
This method will do nothing if the buffer has been marked as cleared (i.e. the
hasBeenCleared method returns true.)
@see hasBeenCleared, setNotClear
*/
void clear (int channel, int startSample, int numSamples) noexcept
{
@@ -600,15 +656,26 @@ public:
}
/** Returns true if the buffer has been entirely cleared.
Note that this does not actually measure the contents of the buffer - it simply
returns a flag that is set when the buffer is cleared, and which is reset whenever
functions like getWritePointer() are invoked. That means the method does not take
any time, but it may return false negatives when in fact the buffer is still empty.
functions like getWritePointer are invoked. That means the method is quick, but it
may return false negatives when in fact the buffer is still empty.
*/
bool hasBeenCleared() const noexcept { return isClear; }
/** Forces the internal cleared flag of the buffer to false.
This may be useful in the case where you are holding on to a write pointer and call
the clear method before writing some data. You can then use this method to mark the
buffer as containing data so that subsequent clear calls will succeed. However a
better solution is to call getWritePointer each time you need to write data.
*/
void setNotClear() noexcept { isClear = false; }
//==============================================================================
/** Returns a sample from the buffer.
The channel and index are not checked - they are expected to be in-range. If not,
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
territory.
@@ -621,9 +688,12 @@ public:
}
/** Sets a sample in the buffer.
The channel and index are not checked - they are expected to be in-range. If not,
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
territory.
The hasBeenCleared method will return false after this call.
*/
void setSample (int destChannel, int destSample, Type newValue) noexcept
{
@@ -634,9 +704,12 @@ public:
}
/** Adds a value to a sample in the buffer.
The channel and index are not checked - they are expected to be in-range. If not,
an assertion will be thrown, but in a release build, you're into 'undefined behaviour'
territory.
The hasBeenCleared method will return false after this call.
*/
void addSample (int destChannel, int destSample, Type valueToAdd) noexcept
{
@@ -737,6 +810,9 @@ public:
/** Adds samples from another buffer to this one.
The hasBeenCleared method will return false after this call if samples have
been added.
@param destChannel the channel within this buffer to add the samples to
@param destStartSample the start sample within this buffer's channel
@param source the source buffer to add from
@@ -756,7 +832,10 @@ public:
int numSamples,
Type gainToApplyToSource = Type (1)) noexcept
{
jassert (&source != this || sourceChannel != destChannel);
jassert (&source != this
|| sourceChannel != destChannel
|| sourceStartSample + numSamples <= destStartSample
|| destStartSample + numSamples <= sourceStartSample);
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size);
jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
@@ -788,6 +867,9 @@ public:
/** Adds samples from an array of floats to one of the channels.
The hasBeenCleared method will return false after this call if samples have
been added.
@param destChannel the channel within this buffer to add the samples to
@param destStartSample the start sample within this buffer's channel
@param source the source data to use
@@ -833,6 +915,9 @@ public:
/** Adds samples from an array of floats, applying a gain ramp to them.
The hasBeenCleared method will return false after this call if samples have
been added.
@param destChannel the channel within this buffer to add the samples to
@param destStartSample the start sample within this buffer's channel
@param source the source data to use
@@ -892,7 +977,10 @@ public:
int sourceStartSample,
int numSamples) noexcept
{
jassert (&source != this || sourceChannel != destChannel);
jassert (&source != this
|| sourceChannel != destChannel
|| sourceStartSample + numSamples <= destStartSample
|| destStartSample + numSamples <= sourceStartSample);
jassert (isPositiveAndBelow (destChannel, numChannels));
jassert (destStartSample >= 0 && destStartSample + numSamples <= size);
jassert (isPositiveAndBelow (sourceChannel, source.numChannels));
@@ -917,6 +1005,9 @@ public:
/** Copies samples from an array of floats into one of the channels.
The hasBeenCleared method will return false after this call if samples have
been copied.
@param destChannel the channel within this buffer to copy the samples to
@param destStartSample the start sample within this buffer's channel
@param source the source buffer to read from
@@ -942,6 +1033,9 @@ public:
/** Copies samples from an array of floats into one of the channels, applying a gain to it.
The hasBeenCleared method will return false after this call if samples have
been copied.
@param destChannel the channel within this buffer to copy the samples to
@param destStartSample the start sample within this buffer's channel
@param source the source buffer to read from
@@ -987,6 +1081,9 @@ public:
/** Copies samples from an array of floats into one of the channels, applying a gain ramp.
The hasBeenCleared method will return false after this call if samples have
been copied.
@param destChannel the channel within this buffer to copy the samples to
@param destStartSample the start sample within this buffer's channel
@param source the source buffer to read from
@@ -1123,11 +1220,11 @@ private:
Type** channels;
HeapBlock<char, true> allocatedData;
Type* preallocatedChannelSpace[32];
std::atomic<bool> isClear { false };
bool isClear = false;
void allocateData()
{
#if ! JUCE_PROJUCER_LIVE_BUILD && (! JUCE_GCC || (__GNUC__ * 100 + __GNUC_MINOR__) >= 409)
#if (! JUCE_GCC || (__GNUC__ * 100 + __GNUC_MINOR__) >= 409)
static_assert (alignof (Type) <= detail::maxAlignment,
"AudioBuffer cannot hold types with alignment requirements larger than that guaranteed by malloc");
#endif


+ 866
- 459
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
File diff suppressed because it is too large
View File


+ 135
- 123
libs/juce-current/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -34,185 +34,197 @@ class ScopedNoDenormals;
//==============================================================================
/**
A collection of simple vector operations on arrays of floats, accelerated with
SIMD instructions where possible.
A collection of simple vector operations on arrays of floating point numbers,
accelerated with SIMD instructions where possible, usually accessed from
the FloatVectorOperations class.
@tags{Audio}
*/
class JUCE_API FloatVectorOperations
{
public:
//==============================================================================
/** Clears a vector of floats. */
static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept;
/** Clears a vector of doubles. */
static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept;
/** Copies a repeated value into a vector of floats. */
static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept;
@code
float data[64];
/** Copies a repeated value into a vector of doubles. */
static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept;
// The following two function calls are equivalent:
FloatVectorOperationsBase<float, int>::clear (data, 64);
FloatVectorOperations::clear (data, 64);
@endcode
/** Copies a vector of floats. */
static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept;
@see FloatVectorOperations
/** Copies a vector of doubles. */
static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept;
@tags{Audio}
*/
template <typename FloatType, typename CountType>
struct FloatVectorOperationsBase
{
/** Clears a vector of floating point numbers. */
static void JUCE_CALLTYPE clear (FloatType* dest, CountType numValues) noexcept;
/** Copies a vector of floats, multiplying each value by a given multiplier */
static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
/** Copies a repeated value into a vector of floating point numbers. */
static void JUCE_CALLTYPE fill (FloatType* dest, FloatType valueToFill, CountType numValues) noexcept;
/** Copies a vector of doubles, multiplying each value by a given multiplier */
static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept;
/** Copies a vector of floating point numbers. */
static void JUCE_CALLTYPE copy (FloatType* dest, const FloatType* src, CountType numValues) noexcept;
/** Adds a fixed value to the destination values. */
static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept;
/** Copies a vector of floating point numbers, multiplying each value by a given multiplier */
static void JUCE_CALLTYPE copyWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept;
/** Adds a fixed value to the destination values. */
static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept;
/** Adds a fixed value to each source value and stores it in the destination array. */
static void JUCE_CALLTYPE add (float* dest, const float* src, float amount, int numValues) noexcept;
static void JUCE_CALLTYPE add (FloatType* dest, FloatType amountToAdd, CountType numValues) noexcept;
/** Adds a fixed value to each source value and stores it in the destination array. */
static void JUCE_CALLTYPE add (double* dest, const double* src, double amount, int numValues) noexcept;
static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, FloatType amount, CountType numValues) noexcept;
/** Adds the source values to the destination values. */
static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept;
/** Adds the source values to the destination values. */
static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept;
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept;
static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src, CountType numValues) noexcept;
/** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */
static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept;
/** Subtracts the source values from the destination values. */
static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept;
static void JUCE_CALLTYPE add (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept;
/** Subtracts the source values from the destination values. */
static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept;
static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src, CountType numValues) noexcept;
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */
static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept;
static void JUCE_CALLTYPE subtract (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept;
/** Multiplies each source value by the given multiplier, then adds it to the destination value. */
static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
/** Multiplies each source value by the given multiplier, then adds it to the destination value. */
static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept;
static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept;
/** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */
static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */
static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept;
/** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */
static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept;
static void JUCE_CALLTYPE addWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept;
/** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */
static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept;
static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType numValues) noexcept;
/** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */
static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */
static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept;
static void JUCE_CALLTYPE subtractWithMultiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept;
/** Multiplies the destination values by the source values. */
static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept;
/** Multiplies the destination values by the source values. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept;
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept;
static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, CountType numValues) noexcept;
/** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept;
/** Multiplies each of the destination values by a fixed multiplier. */
static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept;
static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType numValues) noexcept;
/** Multiplies each of the destination values by a fixed multiplier. */
static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept;
static void JUCE_CALLTYPE multiply (FloatType* dest, FloatType multiplier, CountType numValues) noexcept;
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept;
/** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */
static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept;
/** Copies a source vector to a destination, negating each value. */
static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept;
static void JUCE_CALLTYPE multiply (FloatType* dest, const FloatType* src, FloatType multiplier, CountType num) noexcept;
/** Copies a source vector to a destination, negating each value. */
static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept;
static void JUCE_CALLTYPE negate (FloatType* dest, const FloatType* src, CountType numValues) noexcept;
/** Copies a source vector to a destination, taking the absolute of each value. */
static void JUCE_CALLTYPE abs (float* dest, const float* src, int numValues) noexcept;
static void JUCE_CALLTYPE abs (FloatType* dest, const FloatType* src, CountType numValues) noexcept;
/** Copies a source vector to a destination, taking the absolute of each value. */
static void JUCE_CALLTYPE abs (double* dest, const double* src, int numValues) noexcept;
/** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */
static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept;
/** Converts a stream of integers to floats, multiplying each one by the given multiplier. */
static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept;
/** Each element of dest will be the minimum of the corresponding source1 and source2 values. */
static void JUCE_CALLTYPE min (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept;
/** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */
static void JUCE_CALLTYPE min (float* dest, const float* src, float comp, int num) noexcept;
/** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */
static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src, FloatType comp, CountType num) noexcept;
/** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */
static void JUCE_CALLTYPE min (double* dest, const double* src, double comp, int num) noexcept;
/** Each element of dest will be the maximum of the corresponding source1 and source2 values. */
static void JUCE_CALLTYPE max (FloatType* dest, const FloatType* src1, const FloatType* src2, CountType num) noexcept;
/** Each element of dest will be the minimum of the corresponding source1 and source2 values. */
static void JUCE_CALLTYPE min (float* dest, const float* src1, const float* src2, int num) noexcept;
/** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */
static void JUCE_CALLTYPE clip (FloatType* dest, const FloatType* src, FloatType low, FloatType high, CountType num) noexcept;
/** Each element of dest will be the minimum of the corresponding source1 and source2 values. */
static void JUCE_CALLTYPE min (double* dest, const double* src1, const double* src2, int num) noexcept;
/** Finds the minimum and maximum values in the given array. */
static Range<FloatType> JUCE_CALLTYPE findMinAndMax (const FloatType* src, CountType numValues) noexcept;
/** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */
static void JUCE_CALLTYPE max (float* dest, const float* src, float comp, int num) noexcept;
/** Finds the minimum value in the given array. */
static FloatType JUCE_CALLTYPE findMinimum (const FloatType* src, CountType numValues) noexcept;
/** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */
static void JUCE_CALLTYPE max (double* dest, const double* src, double comp, int num) noexcept;
/** Finds the maximum value in the given array. */
static FloatType JUCE_CALLTYPE findMaximum (const FloatType* src, CountType numValues) noexcept;
};
/** Each element of dest will be the maximum of the corresponding source1 and source2 values. */
static void JUCE_CALLTYPE max (float* dest, const float* src1, const float* src2, int num) noexcept;
#if ! DOXYGEN
namespace detail
{
/** Each element of dest will be the maximum of the corresponding source1 and source2 values. */
static void JUCE_CALLTYPE max (double* dest, const double* src1, const double* src2, int num) noexcept;
template <typename...>
struct NameForwarder;
/** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */
static void JUCE_CALLTYPE clip (float* dest, const float* src, float low, float high, int num) noexcept;
template <typename Head>
struct NameForwarder<Head> : Head {};
/** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */
static void JUCE_CALLTYPE clip (double* dest, const double* src, double low, double high, int num) noexcept;
template <typename Head, typename... Tail>
struct NameForwarder<Head, Tail...> : Head, NameForwarder<Tail...>
{
using Head::clear;
using NameForwarder<Tail...>::clear;
/** Finds the minimum and maximum values in the given array. */
static Range<float> JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept;
using Head::fill;
using NameForwarder<Tail...>::fill;
/** Finds the minimum and maximum values in the given array. */
static Range<double> JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept;
using Head::copy;
using NameForwarder<Tail...>::copy;
/** Finds the minimum value in the given array. */
static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept;
using Head::copyWithMultiply;
using NameForwarder<Tail...>::copyWithMultiply;
/** Finds the minimum value in the given array. */
static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept;
using Head::add;
using NameForwarder<Tail...>::add;
/** Finds the maximum value in the given array. */
static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept;
using Head::subtract;
using NameForwarder<Tail...>::subtract;
/** Finds the maximum value in the given array. */
static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept;
using Head::addWithMultiply;
using NameForwarder<Tail...>::addWithMultiply;
using Head::subtractWithMultiply;
using NameForwarder<Tail...>::subtractWithMultiply;
using Head::multiply;
using NameForwarder<Tail...>::multiply;
using Head::negate;
using NameForwarder<Tail...>::negate;
using Head::abs;
using NameForwarder<Tail...>::abs;
using Head::min;
using NameForwarder<Tail...>::min;
using Head::max;
using NameForwarder<Tail...>::max;
using Head::clip;
using NameForwarder<Tail...>::clip;
using Head::findMinAndMax;
using NameForwarder<Tail...>::findMinAndMax;
using Head::findMinimum;
using NameForwarder<Tail...>::findMinimum;
using Head::findMaximum;
using NameForwarder<Tail...>::findMaximum;
};
} // namespace detail
#endif
//==============================================================================
/**
A collection of simple vector operations on arrays of floating point numbers,
accelerated with SIMD instructions where possible and providing all methods
from FloatVectorOperationsBase.
@see FloatVectorOperationsBase
@tags{Audio}
*/
class JUCE_API FloatVectorOperations : public detail::NameForwarder<FloatVectorOperationsBase<float, int>,
FloatVectorOperationsBase<float, size_t>,
FloatVectorOperationsBase<double, int>,
FloatVectorOperationsBase<double, size_t>>
{
public:
static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept;
static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, size_t num) noexcept;
/** This method enables or disables the SSE/NEON flush-to-zero mode. */
static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept;


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -31,6 +31,8 @@
#include "juce_audio_basics.h"
#include <juce_core/containers/juce_Optional.h>
#if JUCE_MINGW && ! defined (alloca)
#define alloca __builtin_alloca
#endif
@@ -86,3 +88,14 @@
#include "sources/juce_ReverbAudioSource.cpp"
#include "sources/juce_ToneGeneratorAudioSource.cpp"
#include "synthesisers/juce_Synthesiser.cpp"
#include "midi/ump/juce_UMP.h"
#include "midi/ump/juce_UMPUtils.cpp"
#include "midi/ump/juce_UMPView.cpp"
#include "midi/ump/juce_UMPSysEx7.cpp"
#include "midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp"
#if JUCE_UNIT_TESTS
#include "utilities/juce_ADSR_test.cpp"
#include "midi/ump/juce_UMP_test.cpp"
#endif

+ 4
- 3
libs/juce-current/source/modules/juce_audio_basics/juce_audio_basics.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -32,11 +32,12 @@
ID: juce_audio_basics
vendor: juce
version: 6.0.7
version: 6.1.6
name: JUCE audio and MIDI data classes
description: Classes for audio buffer manipulation, midi message handling, synthesis, etc.
website: http://www.juce.com/juce
license: ISC
minimumCppStandard: 14
dependencies: juce_core
OSXFrameworks: Accelerate
@@ -119,4 +120,4 @@
#include "sources/juce_ReverbAudioSource.h"
#include "sources/juce_ToneGeneratorAudioSource.h"
#include "synthesisers/juce_Synthesiser.h"
#include "audio_play_head/juce_AudioPlayHead.h"
#include "audio_play_head/juce_AudioPlayHead.h"

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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 29
- 17
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -119,29 +119,37 @@ void MidiBuffer::clear (int startSample, int numSamples)
data.removeRange ((int) (start - data.begin()), (int) (end - start));
}
void MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber)
bool MidiBuffer::addEvent (const MidiMessage& m, int sampleNumber)
{
addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber);
return addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber);
}
void MidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber)
bool MidiBuffer::addEvent (const void* newData, int maxBytes, int sampleNumber)
{
auto numBytes = MidiBufferHelpers::findActualEventLength (static_cast<const uint8*> (newData), maxBytes);
if (numBytes > 0)
if (numBytes <= 0)
return true;
if (std::numeric_limits<uint16>::max() < numBytes)
{
auto newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16);
auto offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin());
// This method only supports messages smaller than (1 << 16) bytes
return false;
}
data.insertMultiple (offset, 0, (int) newItemSize);
auto newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16);
auto offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin());
auto* d = data.begin() + offset;
writeUnaligned<int32> (d, sampleNumber);
d += sizeof (int32);
writeUnaligned<uint16> (d, static_cast<uint16> (numBytes));
d += sizeof (uint16);
memcpy (d, newData, (size_t) numBytes);
}
data.insertMultiple (offset, 0, (int) newItemSize);
auto* d = data.begin() + offset;
writeUnaligned<int32> (d, sampleNumber);
d += sizeof (int32);
writeUnaligned<uint16> (d, static_cast<uint16> (numBytes));
d += sizeof (uint16);
memcpy (d, newData, (size_t) numBytes);
return true;
}
void MidiBuffer::addEvents (const MidiBuffer& otherBuffer,
@@ -201,13 +209,14 @@ MidiBufferIterator MidiBuffer::findNextSamplePosition (int samplePosition) const
}
//==============================================================================
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wdeprecated-declarations")
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4996)
MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept
: buffer (b), iterator (b.data.begin())
{
}
MidiBuffer::Iterator::~Iterator() noexcept {}
void MidiBuffer::Iterator::setNextSamplePosition (int samplePosition) noexcept
{
iterator = buffer.findNextSamplePosition (samplePosition);
@@ -236,6 +245,9 @@ bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePositio
return true;
}
JUCE_END_IGNORE_WARNINGS_MSVC
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
//==============================================================================
//==============================================================================
#if JUCE_UNIT_TESTS


+ 16
- 17
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -184,9 +184,11 @@ public:
If an event is added whose sample position is the same as one or more events
already in the buffer, the new event will be placed after the existing ones.
To retrieve events, use a MidiBufferIterator object
To retrieve events, use a MidiBufferIterator object.
Returns true on success, or false on failure.
*/
void addEvent (const MidiMessage& midiMessage, int sampleNumber);
bool addEvent (const MidiMessage& midiMessage, int sampleNumber);
/** Adds an event to the buffer from raw midi data.
@@ -202,9 +204,11 @@ public:
it'll actually only store 3 bytes. If the midi data is invalid, it might not
add an event at all.
To retrieve events, use a MidiBufferIterator object
To retrieve events, use a MidiBufferIterator object.
Returns true on success, or false on failure.
*/
void addEvent (const void* rawMidiData,
bool addEvent (const void* rawMidiData,
int maxBytesOfMidiData,
int sampleNumber);
@@ -269,7 +273,9 @@ public:
MidiBufferIterator findNextSamplePosition (int samplePosition) const noexcept;
//==============================================================================
/**
#ifndef DOXYGEN
/** This class is now deprecated in favour of MidiBufferIterator.
Used to iterate through the events in a MidiBuffer.
Note that altering the buffer while an iterator is using it will produce
@@ -277,20 +283,12 @@ public:
@see MidiBuffer
*/
class JUCE_API Iterator
class [[deprecated]] JUCE_API Iterator
{
public:
//==============================================================================
/** Creates an Iterator for this MidiBuffer.
This class has been deprecated in favour of MidiBufferIterator.
*/
JUCE_DEPRECATED (Iterator (const MidiBuffer&) noexcept);
/** Creates a copy of an iterator. */
Iterator (const Iterator&) = default;
/** Destructor. */
~Iterator() noexcept;
/** Creates an Iterator for this MidiBuffer. */
Iterator (const MidiBuffer& b) noexcept;
//==============================================================================
/** Repositions the iterator so that the next event retrieved will be the first
@@ -332,6 +330,7 @@ public:
const MidiBuffer& buffer;
MidiBufferIterator iterator;
};
#endif
/** The raw data holding this buffer.
Obviously access to this data is provided at your own risk. Its internal format could


libs/juce-current/source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h → libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiDataConcatenator.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.

+ 55
- 59
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -46,18 +46,6 @@ namespace MidiFileHelpers
}
}
template <typename Value>
struct Optional
{
Optional() = default;
Optional (const Value& v)
: value (v), valid (true) {}
Value value = Value();
bool valid = false;
};
template <typename Integral>
struct ReadTrait;
@@ -100,23 +88,23 @@ namespace MidiFileHelpers
auto ch = tryRead<uint32> (data, remaining);
if (! ch.valid)
if (! ch.hasValue())
return {};
if (ch.value != ByteOrder::bigEndianInt ("MThd"))
if (*ch != ByteOrder::bigEndianInt ("MThd"))
{
auto ok = false;
if (ch.value == ByteOrder::bigEndianInt ("RIFF"))
if (*ch == ByteOrder::bigEndianInt ("RIFF"))
{
for (int i = 0; i < 8; ++i)
{
ch = tryRead<uint32> (data, remaining);
if (! ch.valid)
if (! ch.hasValue())
return {};
if (ch.value == ByteOrder::bigEndianInt ("MThd"))
if (*ch == ByteOrder::bigEndianInt ("MThd"))
{
ok = true;
break;
@@ -130,29 +118,29 @@ namespace MidiFileHelpers
const auto bytesRemaining = tryRead<uint32> (data, remaining);
if (! bytesRemaining.valid || bytesRemaining.value > remaining)
if (! bytesRemaining.hasValue() || *bytesRemaining > remaining)
return {};
const auto optFileType = tryRead<uint16> (data, remaining);
if (! optFileType.valid || 2 < optFileType.value)
if (! optFileType.hasValue() || 2 < *optFileType)
return {};
const auto optNumTracks = tryRead<uint16> (data, remaining);
if (! optNumTracks.valid || (optFileType.value == 0 && optNumTracks.value != 1))
if (! optNumTracks.hasValue() || (*optFileType == 0 && *optNumTracks != 1))
return {};
const auto optTimeFormat = tryRead<uint16> (data, remaining);
if (! optTimeFormat.valid)
if (! optTimeFormat.hasValue())
return {};
HeaderDetails result;
result.fileType = (short) optFileType.value;
result.timeFormat = (short) optTimeFormat.value;
result.numberOfTracks = (short) optNumTracks.value;
result.fileType = (short) *optFileType;
result.timeFormat = (short) *optTimeFormat;
result.numberOfTracks = (short) *optNumTracks;
result.bytesRead = maxSize - remaining;
return { result };
@@ -264,7 +252,6 @@ namespace MidiFileHelpers
//==============================================================================
MidiFile::MidiFile() : timeFormat ((short) (unsigned short) 0xe728) {}
MidiFile::~MidiFile() {}
MidiFile::MidiFile (const MidiFile& other) : timeFormat (other.timeFormat)
{
@@ -356,7 +343,9 @@ double MidiFile::getLastTimestamp() const
}
//==============================================================================
bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs)
bool MidiFile::readFrom (InputStream& sourceStream,
bool createMatchingNoteOffs,
int* fileType)
{
clear();
MemoryBlock data;
@@ -372,10 +361,10 @@ bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs)
const auto optHeader = MidiFileHelpers::parseMidiHeader (d, size);
if (! optHeader.valid)
if (! optHeader.hasValue())
return false;
const auto header = optHeader.value;
const auto header = *optHeader;
timeFormat = header.timeFormat;
d += header.bytesRead;
@@ -385,27 +374,32 @@ bool MidiFile::readFrom (InputStream& sourceStream, bool createMatchingNoteOffs)
{
const auto optChunkType = MidiFileHelpers::tryRead<uint32> (d, size);
if (! optChunkType.valid)
if (! optChunkType.hasValue())
return false;
const auto optChunkSize = MidiFileHelpers::tryRead<uint32> (d, size);
if (! optChunkSize.valid)
if (! optChunkSize.hasValue())
return false;
const auto chunkSize = optChunkSize.value;
const auto chunkSize = *optChunkSize;
if (size < chunkSize)
return false;
if (optChunkType.value == ByteOrder::bigEndianInt ("MTrk"))
if (*optChunkType == ByteOrder::bigEndianInt ("MTrk"))
readNextTrack (d, (int) chunkSize, createMatchingNoteOffs);
size -= chunkSize;
d += chunkSize;
}
return size == 0;
const auto successful = (size == 0);
if (successful && fileType != nullptr)
*fileType = header.fileType;
return successful;
}
void MidiFile::readNextTrack (const uint8* data, int size, bool createMatchingNoteOffs)
@@ -604,7 +598,7 @@ struct MidiFileTest : public UnitTest
{
// No data
const auto header = parseHeader ([] (OutputStream&) {});
expect (! header.valid);
expect (! header.hasValue());
}
{
@@ -614,7 +608,7 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 0xff });
});
expect (! header.valid);
expect (! header.hasValue());
}
{
@@ -624,7 +618,7 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'h', 'd' });
});
expect (! header.valid);
expect (! header.hasValue());
}
{
@@ -634,7 +628,7 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 0, 0, 16, 0, 1 });
});
expect (! header.valid);
expect (! header.hasValue());
}
{
@@ -644,7 +638,7 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 5, 0, 16, 0, 1 });
});
expect (! header.valid);
expect (! header.hasValue());
}
{
@@ -654,12 +648,12 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 16, 0, 1 });
});
expect (header.valid);
expect (header.hasValue());
expectEquals (header.value.fileType, (short) 1);
expectEquals (header.value.numberOfTracks, (short) 16);
expectEquals (header.value.timeFormat, (short) 1);
expectEquals ((int) header.value.bytesRead, 14);
expectEquals (header->fileType, (short) 1);
expectEquals (header->numberOfTracks, (short) 16);
expectEquals (header->timeFormat, (short) 1);
expectEquals ((int) header->bytesRead, 14);
}
}
@@ -668,7 +662,7 @@ struct MidiFileTest : public UnitTest
{
// Empty input
const auto file = parseFile ([] (OutputStream&) {});
expect (! file.valid);
expect (! file.hasValue());
}
{
@@ -678,7 +672,7 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'h', 'd' });
});
expect (! file.valid);
expect (! file.hasValue());
}
{
@@ -688,8 +682,8 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 1, 0, 0, 0, 1 });
});
expect (file.valid);
expectEquals (file.value.getNumTracks(), 0);
expect (file.hasValue());
expectEquals (file->getNumTracks(), 0);
}
{
@@ -700,7 +694,7 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'r', '?' });
});
expect (! file.valid);
expect (! file.hasValue());
}
{
@@ -711,9 +705,9 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'r', 'k', 0, 0, 0, 1, 0xff });
});
expect (file.valid);
expectEquals (file.value.getNumTracks(), 1);
expectEquals (file.value.getTrack (0)->getNumEvents(), 0);
expect (file.hasValue());
expectEquals (file->getNumTracks(), 1);
expectEquals (file->getTrack (0)->getNumEvents(), 0);
}
{
@@ -724,7 +718,7 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 'M', 'T', 'r', 'k', 0x0f, 0, 0, 0, 0xff });
});
expect (! file.valid);
expect (! file.hasValue());
}
{
@@ -738,10 +732,10 @@ struct MidiFileTest : public UnitTest
writeBytes (os, { 0x80, 0x00, 0x00 });
});
expect (file.valid);
expectEquals (file.value.getNumTracks(), 1);
expect (file.hasValue());
expectEquals (file->getNumTracks(), 1);
auto& track = *file.value.getTrack (0);
auto& track = *file->getTrack (0);
expectEquals (track.getNumEvents(), 1);
expect (track.getEventPointer (0)->message.isNoteOff());
expectEquals (track.getEventPointer (0)->message.getTimeStamp(), (double) 0x0f);
@@ -760,7 +754,7 @@ struct MidiFileTest : public UnitTest
}
template <typename Fn>
static MidiFileHelpers::Optional<MidiFileHelpers::HeaderDetails> parseHeader (Fn&& fn)
static Optional<MidiFileHelpers::HeaderDetails> parseHeader (Fn&& fn)
{
MemoryOutputStream os;
fn (os);
@@ -770,7 +764,7 @@ struct MidiFileTest : public UnitTest
}
template <typename Fn>
static MidiFileHelpers::Optional<MidiFile> parseFile (Fn&& fn)
static Optional<MidiFile> parseFile (Fn&& fn)
{
MemoryOutputStream os;
fn (os);
@@ -778,7 +772,9 @@ struct MidiFileTest : public UnitTest
MemoryInputStream is (os.getData(), os.getDataSize(), false);
MidiFile mf;
if (mf.readFrom (is))
int fileType = 0;
if (mf.readFrom (is, true, &fileType))
return mf;
return {};


+ 7
- 6
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiFile.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -45,9 +45,6 @@ public:
/** Creates an empty MidiFile object. */
MidiFile();
/** Destructor. */
~MidiFile();
/** Creates a copy of another MidiFile. */
MidiFile (const MidiFile&);
@@ -136,7 +133,7 @@ public:
*/
void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const;
/** Makes a list of all the time-signature meta-events from all tracks in the midi file.
/** Makes a list of all the key-signature meta-events from all tracks in the midi file.
@param keySigEvents a list to which all the events will be added
*/
void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const;
@@ -160,10 +157,14 @@ public:
@param createMatchingNoteOffs if true, any missing note-offs for previous note-ons will
be automatically added at the end of the file by calling
MidiMessageSequence::updateMatchedPairs on each track.
@param midiFileType if not nullptr, the integer at this address will be set
to 0, 1, or 2 depending on the type of the midi file
@returns true if the stream was read successfully
*/
bool readFrom (InputStream& sourceStream, bool createMatchingNoteOffs = true);
bool readFrom (InputStream& sourceStream,
bool createMatchingNoteOffs = true,
int* midiFileType = nullptr);
/** Writes the midi tracks as a standard midi file.
The midiFileType value is written as the file's format type, which can be 0, 1


+ 1
- 5
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -28,10 +28,6 @@ MidiKeyboardState::MidiKeyboardState()
zerostruct (noteStates);
}
MidiKeyboardState::~MidiKeyboardState()
{
}
//==============================================================================
void MidiKeyboardState::reset()
{


+ 1
- 2
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -43,7 +43,6 @@ class JUCE_API MidiKeyboardState
public:
//==============================================================================
MidiKeyboardState();
~MidiKeyboardState();
//==============================================================================
/** Resets the state of the object.


+ 8
- 5
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -287,11 +287,14 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other)
{
if (other.isHeapAllocated())
{
if (isHeapAllocated())
packedData.allocatedData = static_cast<uint8*> (std::realloc (packedData.allocatedData, (size_t) other.size));
else
packedData.allocatedData = static_cast<uint8*> (std::malloc ((size_t) other.size));
auto* newStorage = static_cast<uint8*> (isHeapAllocated()
? std::realloc (packedData.allocatedData, (size_t) other.size)
: std::malloc ((size_t) other.size));
if (newStorage == nullptr)
throw std::bad_alloc{}; // The midi message has not been adjusted at this point
packedData.allocatedData = newStorage;
memcpy (packedData.allocatedData, other.packedData.allocatedData, (size_t) other.size);
}
else


+ 7
- 7
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessage.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -102,7 +102,8 @@ public:
double timeStamp = 0,
bool sysexHasEmbeddedLength = true);
/** Creates an active-sense message.
/** Creates an empty sysex message.
Since the MidiMessage has to contain a valid message, this default constructor
just initialises it with an empty sysex message.
*/
@@ -856,17 +857,16 @@ public:
//==============================================================================
#ifndef DOXYGEN
/** Reads a midi variable-length integer.
This signature has been deprecated in favour of the safer
readVariableLengthValue.
The `data` argument indicates the data to read the number from,
and `numBytesUsed` is used as an out-parameter to indicate the number
of bytes that were read.
*/
JUCE_DEPRECATED (static int readVariableLengthVal (const uint8* data,
int& numBytesUsed) noexcept);
[[deprecated ("This signature has been deprecated in favour of the safer readVariableLengthValue.")]]
static int readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept;
#endif
/** Holds information about a variable-length value which was parsed
from a stream of bytes.


+ 489
- 31
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -25,7 +25,6 @@ namespace juce
MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {}
MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (std::move (mm)) {}
MidiMessageSequence::MidiEventHolder::~MidiEventHolder() {}
//==============================================================================
MidiMessageSequence::MidiMessageSequence()
@@ -63,10 +62,6 @@ MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other
return *this;
}
MidiMessageSequence::~MidiMessageSequence()
{
}
void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept
{
list.swapWith (other.list);
@@ -309,43 +304,174 @@ void MidiMessageSequence::deleteSysExMessages()
}
//==============================================================================
void MidiMessageSequence::createControllerUpdatesForTime (int channelNumber, double time, Array<MidiMessage>& dest)
class OptionalPitchWheel
{
bool doneProg = false;
bool donePitchWheel = false;
bool doneControllers[128] = {};
Optional<int> value;
for (int i = list.size(); --i >= 0;)
public:
void emit (int channel, Array<MidiMessage>& out) const
{
if (value.hasValue())
out.add (MidiMessage::pitchWheel (channel, *value));
}
void set (int v)
{
auto& mm = list.getUnchecked(i)->message;
value = v;
}
};
if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time)
class OptionalControllerValues
{
Optional<char> values[128];
public:
void emit (int channel, Array<MidiMessage>& out) const
{
for (auto it = std::begin (values); it != std::end (values); ++it)
if (it->hasValue())
out.add (MidiMessage::controllerEvent (channel, (int) std::distance (std::begin (values), it), **it));
}
void set (int controller, int value)
{
values[controller] = (char) value;
}
};
class OptionalProgramChange
{
Optional<char> value, bankLSB, bankMSB;
public:
void emit (int channel, double time, Array<MidiMessage>& out) const
{
if (! value.hasValue())
return;
if (bankLSB.hasValue() && bankMSB.hasValue())
{
if (mm.isProgramChange() && ! doneProg)
{
doneProg = true;
dest.add (MidiMessage (mm, 0.0));
}
else if (mm.isPitchWheel() && ! donePitchWheel)
out.add (MidiMessage::controllerEvent (channel, 0x00, *bankMSB).withTimeStamp (time));
out.add (MidiMessage::controllerEvent (channel, 0x20, *bankLSB).withTimeStamp (time));
}
out.add (MidiMessage::programChange (channel, *value).withTimeStamp (time));
}
// Returns true if this is a bank number change, and false otherwise.
bool trySetBank (int controller, int v)
{
switch (controller)
{
case 0x00: bankMSB = (char) v; return true;
case 0x20: bankLSB = (char) v; return true;
}
return false;
}
void setProgram (int v) { value = (char) v; }
};
class ParameterNumberState
{
enum class Kind { rpn, nrpn };
Optional<char> newestRpnLsb, newestRpnMsb, newestNrpnLsb, newestNrpnMsb, lastSentLsb, lastSentMsb;
Kind lastSentKind = Kind::rpn, newestKind = Kind::rpn;
public:
// If the effective parameter number has changed since the last time this function was called,
// this will emit the current parameter in full (MSB and LSB).
// This should be called before each data message (entry, increment, decrement: 0x06, 0x26, 0x60, 0x61)
// to ensure that the data message operates on the correct parameter number.
void sendIfNecessary (int channel, double time, Array<MidiMessage>& out)
{
const auto newestMsb = newestKind == Kind::rpn ? newestRpnMsb : newestNrpnMsb;
const auto newestLsb = newestKind == Kind::rpn ? newestRpnLsb : newestNrpnLsb;
auto lastSent = std::tie (lastSentKind, lastSentMsb, lastSentLsb);
const auto newest = std::tie (newestKind, newestMsb, newestLsb);
if (lastSent == newest || ! newestMsb.hasValue() || ! newestLsb.hasValue())
return;
out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x65 : 0x63, *newestMsb).withTimeStamp (time));
out.add (MidiMessage::controllerEvent (channel, newestKind == Kind::rpn ? 0x64 : 0x62, *newestLsb).withTimeStamp (time));
lastSent = newest;
}
// Returns true if this is a parameter number change, and false otherwise.
bool trySetProgramNumber (int controller, int value)
{
switch (controller)
{
case 0x65: newestRpnMsb = (char) value; newestKind = Kind::rpn; return true;
case 0x64: newestRpnLsb = (char) value; newestKind = Kind::rpn; return true;
case 0x63: newestNrpnMsb = (char) value; newestKind = Kind::nrpn; return true;
case 0x62: newestNrpnLsb = (char) value; newestKind = Kind::nrpn; return true;
}
return false;
}
};
void MidiMessageSequence::createControllerUpdatesForTime (int channel, double time, Array<MidiMessage>& dest)
{
OptionalProgramChange programChange;
OptionalControllerValues controllers;
OptionalPitchWheel pitchWheel;
ParameterNumberState parameterNumberState;
for (const auto& item : list)
{
const auto& mm = item->message;
if (! (mm.isForChannel (channel) && mm.getTimeStamp() <= time))
continue;
if (mm.isController())
{
const auto num = mm.getControllerNumber();
if (parameterNumberState.trySetProgramNumber (num, mm.getControllerValue()))
continue;
if (programChange.trySetBank (num, mm.getControllerValue()))
continue;
constexpr int passthroughs[] { 0x06, 0x26, 0x60, 0x61 };
if (std::find (std::begin (passthroughs), std::end (passthroughs), num) != std::end (passthroughs))
{
donePitchWheel = true;
dest.add (MidiMessage (mm, 0.0));
parameterNumberState.sendIfNecessary (channel, mm.getTimeStamp(), dest);
dest.add (mm);
}
else if (mm.isController())
else
{
auto controllerNumber = mm.getControllerNumber();
jassert (isPositiveAndBelow (controllerNumber, 128));
if (! doneControllers[controllerNumber])
{
doneControllers[controllerNumber] = true;
dest.add (MidiMessage (mm, 0.0));
}
controllers.set (num, mm.getControllerValue());
}
}
else if (mm.isProgramChange())
{
programChange.setProgram (mm.getProgramChangeNumber());
}
else if (mm.isPitchWheel())
{
pitchWheel.set (mm.getPitchWheelValue());
}
}
}
pitchWheel.emit (channel, dest);
controllers.emit (channel, dest);
// Also emits bank change messages if necessary.
programChange.emit (channel, time, dest);
// Set the parameter number to its final state.
parameterNumberState.sendIfNecessary (channel, time, dest);
}
//==============================================================================
//==============================================================================
@@ -402,6 +528,338 @@ struct MidiMessageSequenceTest : public UnitTest
expectEquals (s.getNumEvents(), 7);
expectEquals (s.getIndexOfMatchingKeyUp (0), -1); // Truncated note, should be no note off
expectEquals (s.getTimeOfMatchingKeyUp (1), 5.0);
struct ControlValue { int control, value; };
struct DataEntry
{
int controllerBase, channel, parameter, value;
double time;
std::array<ControlValue, 4> getControlValues() const
{
return { { { controllerBase + 1, (parameter >> 7) & 0x7f },
{ controllerBase + 0, (parameter >> 0) & 0x7f },
{ 0x06, (value >> 7) & 0x7f },
{ 0x26, (value >> 0) & 0x7f } } };
}
void addToSequence (MidiMessageSequence& s) const
{
for (const auto& pair : getControlValues())
s.addEvent (MidiMessage::controllerEvent (channel, pair.control, pair.value), time);
}
bool matches (const MidiMessage* begin, const MidiMessage* end) const
{
const auto isEqual = [this] (const ControlValue& cv, const MidiMessage& msg)
{
return msg.getTimeStamp() == time
&& msg.isController()
&& msg.getChannel() == channel
&& msg.getControllerNumber() == cv.control
&& msg.getControllerValue() == cv.value;
};
const auto pairs = getControlValues();
return std::equal (pairs.begin(), pairs.end(), begin, end, isEqual);
}
};
const auto addNrpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
{
DataEntry { 0x62, channel, parameter, value, time }.addToSequence (seq);
};
const auto addRpn = [&] (MidiMessageSequence& seq, int channel, int parameter, int value, double time = 0.0)
{
DataEntry { 0x64, channel, parameter, value, time }.addToSequence (seq);
};
const auto checkNrpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
{
expect (DataEntry { 0x62, channel, parameter, value, time }.matches (begin, end));
};
const auto checkRpn = [&] (const MidiMessage* begin, const MidiMessage* end, int channel, int parameter, int value, double time = 0.0)
{
expect (DataEntry { 0x64, channel, parameter, value, time }.matches (begin, end));
};
beginTest ("createControllerUpdatesForTime should emit (N)RPN components in the correct order");
{
const auto channel = 1;
const auto number = 200;
const auto value = 300;
MidiMessageSequence sequence;
addNrpn (sequence, channel, number, value);
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, 1.0, m);
checkNrpn (m.begin(), m.end(), channel, number, value);
}
beginTest ("createControllerUpdatesForTime ignores (N)RPNs after the final requested time");
{
const auto channel = 2;
const auto number = 123;
const auto value = 456;
MidiMessageSequence sequence;
addRpn (sequence, channel, number, value, 0.5);
addRpn (sequence, channel, 111, 222, 1.5);
addRpn (sequence, channel, 333, 444, 2.5);
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, 1.0, m);
checkRpn (m.begin(), std::next (m.begin(), 4), channel, number, value, 0.5);
}
beginTest ("createControllerUpdatesForTime should emit separate (N)RPN messages when appropriate");
{
const auto channel = 2;
const auto numberA = 1111;
const auto valueA = 9999;
const auto numberB = 8888;
const auto valueB = 2222;
const auto numberC = 7777;
const auto valueC = 3333;
const auto numberD = 6666;
const auto valueD = 4444;
const auto time = 0.5;
MidiMessageSequence sequence;
addRpn (sequence, channel, numberA, valueA, time);
addRpn (sequence, channel, numberB, valueB, time);
addNrpn (sequence, channel, numberC, valueC, time);
addNrpn (sequence, channel, numberD, valueD, time);
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, time * 2, m);
checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), channel, numberA, valueA, time);
checkRpn (std::next (m.begin(), 4), std::next (m.begin(), 8), channel, numberB, valueB, time);
checkNrpn (std::next (m.begin(), 8), std::next (m.begin(), 12), channel, numberC, valueC, time);
checkNrpn (std::next (m.begin(), 12), std::next (m.begin(), 16), channel, numberD, valueD, time);
}
beginTest ("createControllerUpdatesForTime correctly emits (N)RPN messages on multiple channels");
{
struct Info { int channel, number, value; };
const Info infos[] { { 2, 1111, 9999 },
{ 8, 8888, 2222 },
{ 5, 7777, 3333 },
{ 1, 6666, 4444 } };
const auto time = 0.5;
MidiMessageSequence sequence;
for (const auto& info : infos)
addRpn (sequence, info.channel, info.number, info.value, time);
for (const auto& info : infos)
{
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (info.channel, time * 2, m);
checkRpn (std::next (m.begin(), 0), std::next (m.begin(), 4), info.channel, info.number, info.value, time);
}
}
const auto messagesAreEqual = [] (const MidiMessage& a, const MidiMessage& b)
{
return std::equal (a.getRawData(), a.getRawData() + a.getRawDataSize(),
b.getRawData(), b.getRawData() + b.getRawDataSize());
};
beginTest ("createControllerUpdatesForTime sends bank select messages when the next program is in a new bank");
{
MidiMessageSequence sequence;
const auto time = 0.0;
const auto channel = 1;
sequence.addEvent (MidiMessage::programChange (channel, 5), time);
sequence.addEvent (MidiMessage::controllerEvent (channel, 0x00, 128), time);
sequence.addEvent (MidiMessage::controllerEvent (channel, 0x20, 64), time);
sequence.addEvent (MidiMessage::programChange (channel, 63), time);
const Array<MidiMessage> finalEvents { MidiMessage::controllerEvent (channel, 0x00, 50),
MidiMessage::controllerEvent (channel, 0x20, 40),
MidiMessage::programChange (channel, 30) };
for (const auto& e : finalEvents)
sequence.addEvent (e);
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, 1.0, m);
expect (std::equal (m.begin(), m.end(), finalEvents.begin(), finalEvents.end(), messagesAreEqual));
}
beginTest ("createControllerUpdatesForTime preserves all Data Increment and Data Decrement messages");
{
MidiMessageSequence sequence;
const auto time = 0.0;
const auto channel = 1;
const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x60, 0),
MidiMessage::controllerEvent (channel, 0x06, 100),
MidiMessage::controllerEvent (channel, 0x26, 50),
MidiMessage::controllerEvent (channel, 0x60, 10),
MidiMessage::controllerEvent (channel, 0x61, 10),
MidiMessage::controllerEvent (channel, 0x06, 20),
MidiMessage::controllerEvent (channel, 0x26, 30),
MidiMessage::controllerEvent (channel, 0x61, 10),
MidiMessage::controllerEvent (channel, 0x61, 20) };
for (const auto& m : messages)
sequence.addEvent (m, time);
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, 1.0, m);
expect (std::equal (m.begin(), m.end(), messages.begin(), messages.end(), messagesAreEqual));
}
beginTest ("createControllerUpdatesForTime does not emit redundant parameter number changes");
{
MidiMessageSequence sequence;
const auto time = 0.0;
const auto channel = 1;
const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
MidiMessage::controllerEvent (channel, 0x64, 100),
MidiMessage::controllerEvent (channel, 0x63, 50),
MidiMessage::controllerEvent (channel, 0x62, 10),
MidiMessage::controllerEvent (channel, 0x06, 10) };
for (const auto& m : messages)
sequence.addEvent (m, time);
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, 1.0, m);
const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
MidiMessage::controllerEvent (channel, 0x62, 10),
MidiMessage::controllerEvent (channel, 0x06, 10) };
expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
}
beginTest ("createControllerUpdatesForTime sets parameter number correctly at end of sequence");
{
MidiMessageSequence sequence;
const auto time = 0.0;
const auto channel = 1;
const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 0),
MidiMessage::controllerEvent (channel, 0x64, 100),
MidiMessage::controllerEvent (channel, 0x63, 50),
MidiMessage::controllerEvent (channel, 0x62, 10),
MidiMessage::controllerEvent (channel, 0x06, 10),
MidiMessage::controllerEvent (channel, 0x64, 5) };
for (const auto& m : messages)
sequence.addEvent (m, time);
const auto finalTime = 1.0;
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, finalTime, m);
const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 50),
MidiMessage::controllerEvent (channel, 0x62, 10),
MidiMessage::controllerEvent (channel, 0x06, 10),
// Note: we should send both the MSB and LSB!
MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
MidiMessage::controllerEvent (channel, 0x64, 5).withTimeStamp (finalTime) };
expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
}
beginTest ("createControllerUpdatesForTime does not emit duplicate parameter number change messages");
{
MidiMessageSequence sequence;
const auto time = 0.0;
const auto channel = 1;
const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x65, 1),
MidiMessage::controllerEvent (channel, 0x64, 2),
MidiMessage::controllerEvent (channel, 0x63, 3),
MidiMessage::controllerEvent (channel, 0x62, 4),
MidiMessage::controllerEvent (channel, 0x06, 10),
MidiMessage::controllerEvent (channel, 0x63, 30),
MidiMessage::controllerEvent (channel, 0x62, 40),
MidiMessage::controllerEvent (channel, 0x63, 3),
MidiMessage::controllerEvent (channel, 0x62, 4),
MidiMessage::controllerEvent (channel, 0x60, 5),
MidiMessage::controllerEvent (channel, 0x65, 10) };
for (const auto& m : messages)
sequence.addEvent (m, time);
const auto finalTime = 1.0;
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, finalTime, m);
const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x63, 3),
MidiMessage::controllerEvent (channel, 0x62, 4),
MidiMessage::controllerEvent (channel, 0x06, 10),
// Parameter number is set to (30, 40) then back to (3, 4),
// so there is no need to resend it
MidiMessage::controllerEvent (channel, 0x60, 5),
// Set parameter number to final value
MidiMessage::controllerEvent (channel, 0x65, 10).withTimeStamp (finalTime),
MidiMessage::controllerEvent (channel, 0x64, 2) .withTimeStamp (finalTime) };
expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
}
beginTest ("createControllerUpdatesForTime emits bank change messages immediately before program change");
{
MidiMessageSequence sequence;
const auto time = 0.0;
const auto channel = 1;
const Array<MidiMessage> messages { MidiMessage::controllerEvent (channel, 0x00, 1),
MidiMessage::controllerEvent (channel, 0x20, 2),
MidiMessage::controllerEvent (channel, 0x65, 0),
MidiMessage::controllerEvent (channel, 0x64, 0),
MidiMessage::programChange (channel, 5) };
for (const auto& m : messages)
sequence.addEvent (m, time);
const auto finalTime = 1.0;
Array<MidiMessage> m;
sequence.createControllerUpdatesForTime (channel, finalTime, m);
const Array<MidiMessage> expected { MidiMessage::controllerEvent (channel, 0x00, 1),
MidiMessage::controllerEvent (channel, 0x20, 2),
MidiMessage::programChange (channel, 5),
MidiMessage::controllerEvent (channel, 0x65, 0).withTimeStamp (finalTime),
MidiMessage::controllerEvent (channel, 0x64, 0).withTimeStamp (finalTime) };
expect (std::equal (m.begin(), m.end(), expected.begin(), expected.end(), messagesAreEqual));
}
}
};


+ 16
- 7
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -53,9 +53,6 @@ public:
/** Move assignment operator */
MidiMessageSequence& operator= (MidiMessageSequence&&) noexcept;
/** Destructor. */
~MidiMessageSequence();
//==============================================================================
/** Structure used to hold midi events in the sequence.
@@ -68,9 +65,6 @@ public:
{
public:
//==============================================================================
/** Destructor. */
~MidiEventHolder();
/** The message itself, whose timestamp is used to specify the event's time. */
MidiMessage message;
@@ -277,6 +271,21 @@ public:
As well as controllers, it will also recreate the midi program number
and pitch bend position.
This function has special handling for the "bank select" and "data entry"
controllers (0x00, 0x20, 0x06, 0x26, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65).
If the sequence contains multiple bank select and program change messages,
only the bank select messages immediately preceding the final program change
message will be kept.
All "data increment" and "data decrement" messages will be retained. Some hardware will
ignore the requested increment/decrement values, so retaining all messages is the only
way to ensure compatibility with all hardware.
"Parameter number" changes will be slightly condensed. Only the parameter number
events immediately preceding each data entry event will be kept. The parameter number
will also be set to its final value at the end of the sequence, if necessary.
@param channelNumber the midi channel to look for, in the range 1 to 16. Controllers
for other channels will be ignored.
@param time the time at which you want to find out the state - there are


+ 1
- 6
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -55,11 +55,6 @@ void MidiRPNDetector::reset() noexcept
}
//==============================================================================
MidiRPNDetector::ChannelState::ChannelState() noexcept
: parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false)
{
}
bool MidiRPNDetector::ChannelState::handleController (int channel,
int controllerNumber,
int value,


+ 3
- 4
libs/juce-current/source/modules/juce_audio_basics/midi/juce_MidiRPN.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -95,14 +95,13 @@ 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;
uint8 parameterMSB = 0xff, parameterLSB = 0xff, valueMSB = 0xff, valueLSB = 0xff;
bool isNRPN = false;
};
//==============================================================================


+ 47
- 0
libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMP.h View File

@@ -0,0 +1,47 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "../juce_MidiDataConcatenator.h"
#include "juce_UMPProtocols.h"
#include "juce_UMPUtils.h"
#include "juce_UMPacket.h"
#include "juce_UMPSysEx7.h"
#include "juce_UMPView.h"
#include "juce_UMPIterator.h"
#include "juce_UMPackets.h"
#include "juce_UMPFactory.h"
#include "juce_UMPConversion.h"
#include "juce_UMPMidi1ToBytestreamTranslator.h"
#include "juce_UMPMidi1ToMidi2DefaultTranslator.h"
#include "juce_UMPConverters.h"
#include "juce_UMPDispatcher.h"
#include "juce_UMPReceiver.h"
#ifndef DOXYGEN
namespace juce
{
namespace ump = universal_midi_packets;
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPConversion.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPConversion.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -324,3 +326,5 @@ struct Conversion
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPConverters.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPConverters.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -163,3 +165,5 @@ namespace universal_midi_packets
};
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPDispatcher.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPDispatcher.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -108,7 +110,13 @@ public:
{
using CallbackPtr = decltype (std::addressof (callback));
struct Callback
#if JUCE_MINGW
#define JUCE_MINGW_HIDDEN_VISIBILITY __attribute__ ((visibility ("hidden")))
#else
#define JUCE_MINGW_HIDDEN_VISIBILITY
#endif
struct JUCE_MINGW_HIDDEN_VISIBILITY Callback
{
Callback (BytestreamToUMPDispatcher& d, CallbackPtr c)
: dispatch (d), callbackPtr (c) {}
@@ -127,6 +135,8 @@ public:
CallbackPtr callbackPtr = nullptr;
};
#undef JUCE_MINGW_HIDDEN_VISIBILITY
Callback inputCallback { *this, &callback };
concatenator.pushMidiData (begin, int (end - begin), timestamp, (void*) nullptr, inputCallback);
}
@@ -188,3 +198,5 @@ private:
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPFactory.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPFactory.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -532,3 +534,5 @@ struct Factory
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPIterator.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPIterator.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -54,7 +56,7 @@ public:
using value_type = View;
using reference = const View&;
using pointer = const View*;
using iterator_category = std::input_iterator_tag;
using iterator_category = std::forward_iterator_tag;
/** Moves this iterator to the next packet in the range. */
Iterator& operator++() noexcept
@@ -124,3 +126,5 @@ private:
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToBytestreamTranslator.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToBytestreamTranslator.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -211,3 +213,5 @@ private:
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPMidi1ToMidi2DefaultTranslator.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -185,3 +187,5 @@ private:
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPProtocols.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPProtocols.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -42,3 +44,5 @@ enum class MidiProtocol
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPReceiver.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPReceiver.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -27,6 +29,8 @@ namespace universal_midi_packets
/**
A base class for classes which receive Universal MIDI Packets from an input.
@tags{Audio}
*/
struct Receiver
{
@@ -38,3 +42,5 @@ struct Receiver
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPSysEx7.cpp → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -39,12 +39,12 @@ SysEx7::PacketBytes SysEx7::getDataBytes (const PacketX2& packet)
return
{
{ packet.getU8<2>(),
packet.getU8<3>(),
packet.getU8<4>(),
packet.getU8<5>(),
packet.getU8<6>(),
packet.getU8<7>() },
{ { packet.getU8<2>(),
packet.getU8<3>(),
packet.getU8<4>(),
packet.getU8<5>(),
packet.getU8<6>(),
packet.getU8<7>() } },
jmin (numBytes, maxBytes)
};
}

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPSysEx7.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPSysEx7.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,13 +20,15 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
{
/**
This struct acts as a single-file namespace for Univeral MIDI Packet
This struct acts as a single-file namespace for Universal MIDI Packet
functionality related to 7-bit SysEx.
@tags{Audio}
@@ -71,3 +73,5 @@ struct SysEx7
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPUtils.cpp → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPUtils.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPUtils.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPUtils.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -111,3 +113,5 @@ struct Utils
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPView.cpp → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPView.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPView.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPView.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -86,3 +88,5 @@ private:
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPTests.cpp → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMP_test.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -25,8 +25,6 @@ namespace juce
namespace universal_midi_packets
{
#if JUCE_UNIT_TESTS
constexpr uint8_t operator""_u8 (unsigned long long int i) { return static_cast<uint8_t> (i); }
constexpr uint16_t operator""_u16 (unsigned long long int i) { return static_cast<uint16_t> (i); }
constexpr uint32_t operator""_u32 (unsigned long long int i) { return static_cast<uint32_t> (i); }
@@ -697,7 +695,7 @@ public:
for (const auto typecode : typecodesX1)
{
Packets p;
p.add (PacketX1 { (uint32_t) (typecode << 0x1c | (random.nextInt64() & 0xffffff)) });
p.add (PacketX1 { (uint32_t) ((int64_t) typecode << 0x1c | (random.nextInt64() & 0xffffff)) });
checkMidi2ToMidi1Conversion (p, p);
}
@@ -966,8 +964,10 @@ private:
template <typename Fn>
void forEachNonSysExTestMessage (Random& random, Fn&& fn)
{
for (uint8_t firstByte = 0x80; firstByte != 0x00; ++firstByte)
for (uint16_t counter = 0x80; counter != 0x100; ++counter)
{
const auto firstByte = (uint8_t) counter;
if (firstByte == 0xf0 || firstByte == 0xf7)
continue; // sysEx is tested separately
@@ -990,9 +990,9 @@ private:
}
}
#if JUCE_WINDOWS
#if JUCE_WINDOWS && ! JUCE_MINGW
#define JUCE_CHECKED_ITERATOR(msg, size) \
stdext::checked_array_iterator<typename std::remove_reference<decltype (msg)>::type> ((msg), (size))
stdext::checked_array_iterator<typename std::remove_reference<decltype (msg)>::type> ((msg), (size_t) (size))
#else
#define JUCE_CHECKED_ITERATOR(msg, size) (msg)
#endif
@@ -1014,7 +1014,5 @@ private:
static UniversalMidiPacketTests universalMidiPacketTests;
#endif
}
}

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPacket.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPacket.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -27,6 +29,8 @@ namespace universal_midi_packets
/**
Holds a single Universal MIDI Packet.
@tags{Audio}
*/
template <size_t numWords>
class Packet
@@ -185,3 +189,5 @@ using PacketX4 = Packet<4>;
}
}
#endif

libs/juce-current/source/modules/juce_audio_devices/midi_io/ump/juce_UMPackets.h → libs/juce-current/source/modules/juce_audio_basics/midi/ump/juce_UMPackets.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -20,6 +20,8 @@
==============================================================================
*/
#ifndef DOXYGEN
namespace juce
{
namespace universal_midi_packets
@@ -90,3 +92,5 @@ private:
}
}
#endif

+ 61
- 19
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -43,16 +43,20 @@ MPEInstrument::MPEInstrument() noexcept
mpeInstrumentFill (isMemberChannelSustained, false);
pitchbendDimension.value = &MPENote::pitchbend;
pressureDimension.value = &MPENote::pressure;
timbreDimension.value = &MPENote::timbre;
pressureDimension.value = &MPENote::pressure;
timbreDimension.value = &MPENote::timbre;
resetLastReceivedValues();
legacyMode.isEnabled = false;
legacyMode.pitchbendRange = 2;
legacyMode.channelRange = allChannels;
}
MPEInstrument::MPEInstrument (MPEZoneLayout layout)
: MPEInstrument()
{
setZoneLayout (layout);
}
MPEInstrument::~MPEInstrument() = default;
//==============================================================================
@@ -84,21 +88,30 @@ void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout)
const ScopedLock sl (lock);
legacyMode.isEnabled = false;
zoneLayout = newLayout;
resetLastReceivedValues();
if (zoneLayout != newLayout)
{
zoneLayout = newLayout;
listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
}
}
//==============================================================================
void MPEInstrument::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
{
if (legacyMode.isEnabled)
return;
releaseAllNotes();
const ScopedLock sl (lock);
legacyMode.isEnabled = true;
legacyMode.pitchbendRange = pitchbendRange;
legacyMode.channelRange = channelRange;
zoneLayout.clearAllZones();
listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
}
bool MPEInstrument::isLegacyModeEnabled() const noexcept
@@ -117,7 +130,12 @@ void MPEInstrument::setLegacyModeChannelRange (Range<int> channelRange)
releaseAllNotes();
const ScopedLock sl (lock);
legacyMode.channelRange = channelRange;
if (legacyMode.channelRange != channelRange)
{
legacyMode.channelRange = channelRange;
listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
}
}
int MPEInstrument::getLegacyModePitchbendRange() const noexcept
@@ -131,7 +149,12 @@ void MPEInstrument::setLegacyModePitchbendRange (int pitchbendRange)
releaseAllNotes();
const ScopedLock sl (lock);
legacyMode.pitchbendRange = pitchbendRange;
if (legacyMode.pitchbendRange != pitchbendRange)
{
legacyMode.pitchbendRange = pitchbendRange;
listeners.call ([=] (Listener& l) { l.zoneLayoutChanged(); });
}
}
//==============================================================================
@@ -242,7 +265,7 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me
if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel()))
{
for (auto i = notes.size(); --i >= 0;)
for (int i = notes.size(); --i >= 0;)
{
auto& note = notes.getReference (i);
@@ -260,7 +283,7 @@ void MPEInstrument::processMidiResetAllControllersMessage (const MidiMessage& me
auto zone = (message.getChannel() == 1 ? zoneLayout.getLowerZone()
: zoneLayout.getUpperZone());
for (auto i = notes.size(); --i >= 0;)
for (int i = notes.size(); --i >= 0;)
{
auto& note = notes.getReference (i);
@@ -348,11 +371,11 @@ void MPEInstrument::noteOff (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOffVelocity)
{
const ScopedLock sl (lock);
if (notes.isEmpty() || ! isUsingChannel (midiChannel))
return;
const ScopedLock sl (lock);
if (auto* note = getNotePtr (midiChannel, midiNoteNumber))
{
note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off;
@@ -401,7 +424,7 @@ void MPEInstrument::polyAftertouch (int midiChannel, int midiNoteNumber, MPEValu
{
const ScopedLock sl (lock);
for (auto i = notes.size(); --i >= 0;)
for (int i = notes.size(); --i >= 0;)
{
auto& note = notes.getReference (i);
@@ -435,7 +458,7 @@ void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, M
{
if (dimension.trackingMode == allNotesOnChannel)
{
for (auto i = notes.size(); --i >= 0;)
for (int i = notes.size(); --i >= 0;)
{
auto& note = notes.getReference (i);
@@ -464,7 +487,7 @@ void MPEInstrument::updateDimensionMaster (bool isLowerZone, MPEDimension& dimen
if (! zone.isActive())
return;
for (auto i = notes.size(); --i >= 0;)
for (int i = notes.size(); --i >= 0;)
{
auto& note = notes.getReference (i);
@@ -573,7 +596,7 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool
auto zone = (midiChannel == 1 ? zoneLayout.getLowerZone()
: zoneLayout.getUpperZone());
for (auto i = notes.size(); --i >= 0;)
for (int i = notes.size(); --i >= 0;)
{
auto& note = notes.getReference (i);
@@ -605,11 +628,15 @@ void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool
if (! legacyMode.isEnabled)
{
if (zone.isLowerZone())
for (auto i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i)
{
for (int i = zone.getFirstMemberChannel(); i <= zone.getLastMemberChannel(); ++i)
isMemberChannelSustained[i - 1] = isDown;
}
else
for (auto i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i)
{
for (int i = zone.getFirstMemberChannel(); i >= zone.getLastMemberChannel(); --i)
isMemberChannelSustained[i - 1] = isDown;
}
}
}
}
@@ -664,6 +691,17 @@ MPENote MPEInstrument::getNote (int index) const noexcept
return notes[index];
}
MPENote MPEInstrument::getNoteWithID (uint16 noteID) const noexcept
{
const ScopedLock sl (lock);
for (auto& note : notes)
if (note.noteID == noteID)
return note;
return {};
}
//==============================================================================
MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept
{
@@ -727,6 +765,8 @@ MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) noexcept
//==============================================================================
const MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) const noexcept
{
const ScopedLock sl (lock);
for (auto i = notes.size(); --i >= 0;)
{
auto& note = notes.getReference (i);
@@ -833,6 +873,7 @@ public:
testLayout.setUpperZone (6);
}
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6262)
void runTest() override
{
beginTest ("initial zone layout");
@@ -2145,6 +2186,7 @@ public:
}
}
}
JUCE_END_IGNORE_WARNINGS_MSVC
private:
//==============================================================================


+ 23
- 12
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -38,10 +38,8 @@ namespace juce
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.
The class has a Listener class that can be used to react to note and
state changes and trigger some functionality for your application.
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,
@@ -59,11 +57,14 @@ public:
This will construct an MPE instrument with inactive lower and upper zones.
In order to process incoming MIDI, call setZoneLayout, define the layout
via MIDI RPN messages, or set the instrument to legacy mode.
In order to process incoming MIDI messages call setZoneLayout, use the MPEZoneLayout
constructor, define the layout via MIDI RPN messages, or set the instrument to legacy mode.
*/
MPEInstrument() noexcept;
/** Constructs an MPE instrument with the specified zone layout. */
MPEInstrument (MPEZoneLayout layout);
/** Destructor. */
virtual ~MPEInstrument();
@@ -229,6 +230,9 @@ public:
*/
MPENote getNote (int midiChannel, int midiNoteNumber) const noexcept;
/** Returns the note with a given ID. */
MPENote getNoteWithID (uint16 noteID) 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
@@ -244,8 +248,8 @@ public:
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.
/** Derive from this class to be informed about any changes in the MPE notes played
by this instrument, and any changes to its zone layout.
Note: This listener type receives its callbacks immediately, and not
via the message thread (so you might be for example in the MIDI thread).
@@ -297,6 +301,11 @@ public:
and should therefore stop playing.
*/
virtual void noteReleased (MPENote finishedNote) { ignoreUnused (finishedNote); }
/** Implement this callback to be informed whenever the MPE zone layout
or legacy mode settings of this instrument have been changed.
*/
virtual void zoneLayoutChanged() {}
};
//==============================================================================
@@ -307,7 +316,9 @@ public:
void removeListener (Listener* listenerToRemove);
//==============================================================================
/** Puts the instrument into legacy mode.
/** Puts the instrument into legacy mode. If legacy mode is already enabled this method
does nothing.
As a side effect, this will discard all currently playing notes,
and call noteReleased for all of them.
@@ -360,9 +371,9 @@ private:
struct LegacyMode
{
bool isEnabled;
bool isEnabled = false;
Range<int> channelRange;
int pitchbendRange;
int pitchbendRange = 2;
};
struct MPEDimension


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 2
- 2
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPENote.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -115,7 +115,7 @@ struct JUCE_API MPENote
*/
MPEValue noteOnVelocity { MPEValue::minValue() };
/** Current per-note pitchbend of the note (in units of MIDI pitchwheel
/** 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,


+ 10
- 7
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -25,12 +25,10 @@ namespace juce
MPESynthesiser::MPESynthesiser()
{
MPEZoneLayout zoneLayout;
zoneLayout.setLowerZone (15);
setZoneLayout (zoneLayout);
}
MPESynthesiser::MPESynthesiser (MPEInstrument* mpeInstrument) : MPESynthesiserBase (mpeInstrument)
MPESynthesiser::MPESynthesiser (MPEInstrument& mpeInstrument)
: MPESynthesiserBase (mpeInstrument)
{
}
@@ -129,7 +127,7 @@ void MPESynthesiser::noteReleased (MPENote finishedNote)
{
auto* voice = voices.getUnchecked (i);
if (voice->isCurrentlyPlayingNote(finishedNote))
if (voice->isCurrentlyPlayingNote (finishedNote))
stopVoice (voice, finishedNote, true);
}
}
@@ -305,11 +303,16 @@ 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 (auto* voice : voices)
{
voice->currentlyPlayingNote.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number
voice->currentlyPlayingNote.keyState = MPENote::off;
voice->noteStopped (allowTailOff);
}
}
// finally make sure the MPE Instrument also doesn't have any notes anymore.
instrument->releaseAllNotes();
instrument.releaseAllNotes();
}
//==============================================================================


+ 3
- 4
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -65,11 +65,10 @@ public:
/** 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* instrumentToUse);
MPESynthesiser (MPEInstrument& instrumentToUse);
/** Destructor. */
~MPESynthesiser() override;
@@ -303,7 +302,7 @@ protected:
private:
//==============================================================================
bool shouldStealVoices = false;
std::atomic<bool> shouldStealVoices { false };
uint32 lastNoteOnCounter = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser)


+ 18
- 19
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -24,80 +24,79 @@ namespace juce
{
MPESynthesiserBase::MPESynthesiserBase()
: instrument (new MPEInstrument)
: instrument (defaultInstrument)
{
instrument->addListener (this);
instrument.addListener (this);
}
MPESynthesiserBase::MPESynthesiserBase (MPEInstrument* inst)
MPESynthesiserBase::MPESynthesiserBase (MPEInstrument& inst)
: instrument (inst)
{
jassert (instrument != nullptr);
instrument->addListener (this);
instrument.addListener (this);
}
//==============================================================================
MPEZoneLayout MPESynthesiserBase::getZoneLayout() const noexcept
{
return instrument->getZoneLayout();
return instrument.getZoneLayout();
}
void MPESynthesiserBase::setZoneLayout (MPEZoneLayout newLayout)
{
instrument->setZoneLayout (newLayout);
instrument.setZoneLayout (newLayout);
}
//==============================================================================
void MPESynthesiserBase::enableLegacyMode (int pitchbendRange, Range<int> channelRange)
{
instrument->enableLegacyMode (pitchbendRange, channelRange);
instrument.enableLegacyMode (pitchbendRange, channelRange);
}
bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept
{
return instrument->isLegacyModeEnabled();
return instrument.isLegacyModeEnabled();
}
Range<int> MPESynthesiserBase::getLegacyModeChannelRange() const noexcept
{
return instrument->getLegacyModeChannelRange();
return instrument.getLegacyModeChannelRange();
}
void MPESynthesiserBase::setLegacyModeChannelRange (Range<int> channelRange)
{
instrument->setLegacyModeChannelRange (channelRange);
instrument.setLegacyModeChannelRange (channelRange);
}
int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept
{
return instrument->getLegacyModePitchbendRange();
return instrument.getLegacyModePitchbendRange();
}
void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange)
{
instrument->setLegacyModePitchbendRange (pitchbendRange);
instrument.setLegacyModePitchbendRange (pitchbendRange);
}
//==============================================================================
void MPESynthesiserBase::setPressureTrackingMode (TrackingMode modeToUse)
{
instrument->setPressureTrackingMode (modeToUse);
instrument.setPressureTrackingMode (modeToUse);
}
void MPESynthesiserBase::setPitchbendTrackingMode (TrackingMode modeToUse)
{
instrument->setPitchbendTrackingMode (modeToUse);
instrument.setPitchbendTrackingMode (modeToUse);
}
void MPESynthesiserBase::setTimbreTrackingMode (TrackingMode modeToUse)
{
instrument->setTimbreTrackingMode (modeToUse);
instrument.setTimbreTrackingMode (modeToUse);
}
//==============================================================================
void MPESynthesiserBase::handleMidiEvent (const MidiMessage& m)
{
instrument->processNextMidiEvent (m);
instrument.processNextMidiEvent (m);
}
//==============================================================================
@@ -148,7 +147,7 @@ void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate)
if (sampleRate != newRate)
{
const ScopedLock sl (noteStateLock);
instrument->releaseAllNotes();
instrument.releaseAllNotes();
sampleRate = newRate;
}
}


+ 7
- 6
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -52,13 +52,12 @@ public:
/** 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.
If you use this constructor, the synthesiser will use the provided instrument
object 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);
MPESynthesiserBase (MPEInstrument& instrument);
//==============================================================================
/** Returns the synthesiser's internal MPE zone layout.
@@ -200,10 +199,12 @@ protected:
protected:
//==============================================================================
/** @internal */
std::unique_ptr<MPEInstrument> instrument;
MPEInstrument& instrument;
private:
//==============================================================================
MPEInstrument defaultInstrument { MPEZone (MPEZone::Type::lower, 15) };
CriticalSection noteStateLock;
double sampleRate = 0.0;
int minimumSubBlockSize = 32;


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 75
- 14
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -52,25 +52,25 @@ int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept
if (numChannels <= 1)
return firstChannel;
for (auto ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement)
for (int ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement)
{
if (midiChannels[ch].isFree() && midiChannels[ch].lastNotePlayed == noteNumber)
if (midiChannels[(size_t) ch].isFree() && midiChannels[(size_t) ch].lastNotePlayed == noteNumber)
{
midiChannelLastAssigned = ch;
midiChannels[ch].notes.add (noteNumber);
midiChannels[(size_t) ch].notes.add (noteNumber);
return ch;
}
}
for (auto ch = midiChannelLastAssigned + channelIncrement; ; ch += channelIncrement)
for (int ch = midiChannelLastAssigned + channelIncrement; ; ch += channelIncrement)
{
if (ch == lastChannel + channelIncrement) // loop wrap-around
ch = firstChannel;
if (midiChannels[ch].isFree())
if (midiChannels[(size_t) ch].isFree())
{
midiChannelLastAssigned = ch;
midiChannels[ch].notes.add (noteNumber);
midiChannels[(size_t) ch].notes.add (noteNumber);
return ch;
}
@@ -79,11 +79,21 @@ int MPEChannelAssigner::findMidiChannelForNewNote (int noteNumber) noexcept
}
midiChannelLastAssigned = findMidiChannelPlayingClosestNonequalNote (noteNumber);
midiChannels[midiChannelLastAssigned].notes.add (noteNumber);
midiChannels[(size_t) midiChannelLastAssigned].notes.add (noteNumber);
return midiChannelLastAssigned;
}
int MPEChannelAssigner::findMidiChannelForExistingNote (int noteNumber) noexcept
{
const auto iter = std::find_if (midiChannels.cbegin(), midiChannels.cend(), [&] (auto& ch)
{
return std::find (ch.notes.begin(), ch.notes.end(), noteNumber) != ch.notes.end();
});
return iter != midiChannels.cend() ? (int) std::distance (midiChannels.cbegin(), iter) : -1;
}
void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel)
{
const auto removeNote = [] (MidiChannel& ch, int noteNum)
@@ -99,7 +109,7 @@ void MPEChannelAssigner::noteOff (int noteNumber, int midiChannel)
if (midiChannel >= 0 && midiChannel <= 16)
{
removeNote (midiChannels[midiChannel], noteNumber);
removeNote (midiChannels[(size_t) midiChannel], noteNumber);
return;
}
@@ -126,9 +136,9 @@ int MPEChannelAssigner::findMidiChannelPlayingClosestNonequalNote (int noteNumbe
auto channelWithClosestNote = firstChannel;
int closestNoteDistance = 127;
for (auto ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement)
for (int ch = firstChannel; (isLegacy || zone->isLowerZone() ? ch <= lastChannel : ch >= lastChannel); ch += channelIncrement)
{
for (auto note : midiChannels[ch].notes)
for (auto note : midiChannels[(size_t) ch].notes)
{
auto noteDistance = std::abs (note - noteNumber);
@@ -296,24 +306,35 @@ struct MPEUtilsUnitTests : public UnitTest
// check that channels are assigned in correct order
int noteNum = 60;
for (int ch = 2; ch <= 16; ++ch)
expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch);
{
expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch);
expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch);
++noteNum;
}
// check that note-offs are processed
channelAssigner.noteOff (60);
expectEquals (channelAssigner.findMidiChannelForNewNote (60), 2);
expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 2);
channelAssigner.noteOff (61);
expectEquals (channelAssigner.findMidiChannelForNewNote (61), 3);
expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 3);
// check that assigned channel was last to play note
channelAssigner.noteOff (65);
channelAssigner.noteOff (66);
expectEquals (channelAssigner.findMidiChannelForNewNote (66), 8);
expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7);
expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8);
expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7);
// find closest channel playing nonequal note
expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2);
expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2);
// all notes off
channelAssigner.allNotesOff();
@@ -323,10 +344,16 @@ struct MPEUtilsUnitTests : public UnitTest
expectEquals (channelAssigner.findMidiChannelForNewNote (65), 7);
expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForNewNote (55), 2);
expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 8);
expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 7);
expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 2);
// normal assignment
expectEquals (channelAssigner.findMidiChannelForNewNote (101), 3);
expectEquals (channelAssigner.findMidiChannelForNewNote (20), 4);
expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 3);
expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 4);
}
// upper
@@ -339,24 +366,35 @@ struct MPEUtilsUnitTests : public UnitTest
// check that channels are assigned in correct order
int noteNum = 60;
for (int ch = 15; ch >= 1; --ch)
expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch);
{
expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch);
expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch);
++noteNum;
}
// check that note-offs are processed
channelAssigner.noteOff (60);
expectEquals (channelAssigner.findMidiChannelForNewNote (60), 15);
expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 15);
channelAssigner.noteOff (61);
expectEquals (channelAssigner.findMidiChannelForNewNote (61), 14);
expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 14);
// check that assigned channel was last to play note
channelAssigner.noteOff (65);
channelAssigner.noteOff (66);
expectEquals (channelAssigner.findMidiChannelForNewNote (66), 9);
expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10);
expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9);
expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10);
// find closest channel playing nonequal note
expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1);
expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15);
expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1);
expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15);
// all notes off
channelAssigner.allNotesOff();
@@ -366,10 +404,16 @@ struct MPEUtilsUnitTests : public UnitTest
expectEquals (channelAssigner.findMidiChannelForNewNote (65), 10);
expectEquals (channelAssigner.findMidiChannelForNewNote (80), 1);
expectEquals (channelAssigner.findMidiChannelForNewNote (55), 15);
expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 9);
expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 10);
expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 1);
expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 15);
// normal assignment
expectEquals (channelAssigner.findMidiChannelForNewNote (101), 14);
expectEquals (channelAssigner.findMidiChannelForNewNote (20), 13);
expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 14);
expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 13);
}
// legacy
@@ -379,24 +423,35 @@ struct MPEUtilsUnitTests : public UnitTest
// check that channels are assigned in correct order
int noteNum = 60;
for (int ch = 1; ch <= 16; ++ch)
expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum++), ch);
{
expectEquals (channelAssigner.findMidiChannelForNewNote (noteNum), ch);
expectEquals (channelAssigner.findMidiChannelForExistingNote (noteNum), ch);
++noteNum;
}
// check that note-offs are processed
channelAssigner.noteOff (60);
expectEquals (channelAssigner.findMidiChannelForNewNote (60), 1);
expectEquals (channelAssigner.findMidiChannelForExistingNote (60), 1);
channelAssigner.noteOff (61);
expectEquals (channelAssigner.findMidiChannelForNewNote (61), 2);
expectEquals (channelAssigner.findMidiChannelForExistingNote (61), 2);
// check that assigned channel was last to play note
channelAssigner.noteOff (65);
channelAssigner.noteOff (66);
expectEquals (channelAssigner.findMidiChannelForNewNote (66), 7);
expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6);
expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7);
expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6);
// find closest channel playing nonequal note
expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1);
expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1);
// all notes off
channelAssigner.allNotesOff();
@@ -406,10 +461,16 @@ struct MPEUtilsUnitTests : public UnitTest
expectEquals (channelAssigner.findMidiChannelForNewNote (65), 6);
expectEquals (channelAssigner.findMidiChannelForNewNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForNewNote (55), 1);
expectEquals (channelAssigner.findMidiChannelForExistingNote (66), 7);
expectEquals (channelAssigner.findMidiChannelForExistingNote (65), 6);
expectEquals (channelAssigner.findMidiChannelForExistingNote (80), 16);
expectEquals (channelAssigner.findMidiChannelForExistingNote (55), 1);
// normal assignment
expectEquals (channelAssigner.findMidiChannelForNewNote (101), 2);
expectEquals (channelAssigner.findMidiChannelForNewNote (20), 3);
expectEquals (channelAssigner.findMidiChannelForExistingNote (101), 2);
expectEquals (channelAssigner.findMidiChannelForExistingNote (20), 3);
}
}


+ 7
- 2
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEUtils.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -63,6 +63,11 @@ public:
*/
int findMidiChannelForNewNote (int noteNumber) noexcept;
/** If a note has been added using findMidiChannelForNewNote() this will return the channel
to which it was assigned, otherwise it will return -1.
*/
int findMidiChannelForExistingNote (int initialNoteOnNumber) noexcept;
/** You must call this method for all note-offs that you receive so that this class
can keep track of the currently playing notes internally.
@@ -86,7 +91,7 @@ private:
int lastNotePlayed = -1;
bool isFree() const noexcept { return notes.isEmpty(); }
};
MidiChannel midiChannels[17];
std::array<MidiChannel, 17> midiChannels;
//==============================================================================
int findMidiChannelPlayingClosestNonequalNote (int noteNumber) noexcept;


+ 29
- 9
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -43,6 +43,18 @@ MPEValue MPEValue::from14BitInt (int value) noexcept
return { value };
}
MPEValue MPEValue::fromUnsignedFloat (float value) noexcept
{
jassert (0.0f <= value && value <= 1.0f);
return { roundToInt (value * 16383.0f) };
}
MPEValue MPEValue::fromSignedFloat (float value) noexcept
{
jassert (-1.0f <= value && value <= 1.0f);
return { roundToInt (((value + 1.0f) * 16383.0f) / 2.0f) };
}
//==============================================================================
MPEValue MPEValue::minValue() noexcept { return MPEValue::from7BitInt (0); }
MPEValue MPEValue::centreValue() noexcept { return MPEValue::from7BitInt (64); }
@@ -121,26 +133,34 @@ public:
beginTest ("zero/minimum value");
{
expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f);
expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f);
expectValuesConsistent (MPEValue::from7BitInt (0), 0, 0, -1.0f, 0.0f);
expectValuesConsistent (MPEValue::from14BitInt (0), 0, 0, -1.0f, 0.0f);
expectValuesConsistent (MPEValue::fromUnsignedFloat (0.0f), 0, 0, -1.0f, 0.0f);
expectValuesConsistent (MPEValue::fromSignedFloat (-1.0f), 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);
expectValuesConsistent (MPEValue::from7BitInt (127), 127, 16383, 1.0f, 1.0f);
expectValuesConsistent (MPEValue::from14BitInt (16383), 127, 16383, 1.0f, 1.0f);
expectValuesConsistent (MPEValue::fromUnsignedFloat (1.0f), 127, 16383, 1.0f, 1.0f);
expectValuesConsistent (MPEValue::fromSignedFloat (1.0f), 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);
expectValuesConsistent (MPEValue::from7BitInt (64), 64, 8192, 0.0f, 0.5f);
expectValuesConsistent (MPEValue::from14BitInt (8192), 64, 8192, 0.0f, 0.5f);
expectValuesConsistent (MPEValue::fromUnsignedFloat (0.5f), 64, 8192, 0.0f, 0.5f);
expectValuesConsistent (MPEValue::fromSignedFloat (0.0f), 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);
expectValuesConsistent (MPEValue::from7BitInt (32), 32, 4096, -0.5f, 0.25f);
expectValuesConsistent (MPEValue::from14BitInt (4096), 32, 4096, -0.5f, 0.25f);
expectValuesConsistent (MPEValue::fromUnsignedFloat (0.25f), 32, 4096, -0.5f, 0.25f);
expectValuesConsistent (MPEValue::fromSignedFloat (-0.5f), 32, 4096, -0.5f, 0.25f);
}
}


+ 7
- 1
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEValue.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -53,6 +53,12 @@ public:
*/
static MPEValue from14BitInt (int value) noexcept;
/** Constructs an MPEValue from a float between 0.0f and 1.0f. */
static MPEValue fromUnsignedFloat (float value) noexcept;
/** Constructs an MPEValue from a float between -1.0f and 1.0f. */
static MPEValue fromSignedFloat (float value) noexcept;
/** Constructs an MPEValue corresponding to the centre value. */
static MPEValue centreValue() noexcept;


+ 18
- 8
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -23,7 +23,17 @@
namespace juce
{
MPEZoneLayout::MPEZoneLayout() noexcept {}
MPEZoneLayout::MPEZoneLayout (MPEZone lower, MPEZone upper)
: lowerZone (lower), upperZone (upper)
{
}
MPEZoneLayout::MPEZoneLayout (MPEZone zone)
: lowerZone (zone.isLowerZone() ? zone : MPEZone()),
upperZone (! zone.isLowerZone() ? zone : MPEZone())
{
}
MPEZoneLayout::MPEZoneLayout (const MPEZoneLayout& other)
: lowerZone (other.lowerZone),
@@ -54,9 +64,9 @@ void MPEZoneLayout::setZone (bool isLower, int numMemberChannels, int perNotePit
checkAndLimitZoneParameters (0, 96, masterPitchbendRange);
if (isLower)
lowerZone = { true, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
lowerZone = { MPEZone::Type::lower, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
else
upperZone = { false, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
upperZone = { MPEZone::Type::upper, numMemberChannels, perNotePitchbendRange, masterPitchbendRange };
if (numMemberChannels > 0)
{
@@ -86,8 +96,8 @@ void MPEZoneLayout::setUpperZone (int numMemberChannels, int perNotePitchbendRan
void MPEZoneLayout::clearAllZones()
{
lowerZone = { true, 0 };
upperZone = { false, 0 };
lowerZone = { MPEZone::Type::lower, 0 };
upperZone = { MPEZone::Type::upper, 0 };
sendLayoutChangeMessage();
}
@@ -128,7 +138,7 @@ void MPEZoneLayout::processZoneLayoutRpnMessage (MidiRPNMessage rpn)
}
}
void MPEZoneLayout::updateMasterPitchbend (Zone& zone, int value)
void MPEZoneLayout::updateMasterPitchbend (MPEZone& zone, int value)
{
if (zone.masterPitchbendRange != value)
{
@@ -138,7 +148,7 @@ void MPEZoneLayout::updateMasterPitchbend (Zone& zone, int value)
}
}
void MPEZoneLayout::updatePerNotePitchbendRange (Zone& zone, int value)
void MPEZoneLayout::updatePerNotePitchbendRange (MPEZone& zone, int value)
{
if (zone.perNotePitchbendRange != value)
{


+ 102
- 86
libs/juce-current/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -23,6 +23,82 @@
namespace juce
{
//==============================================================================
/**
This struct represents an MPE zone.
It can either be a lower or an upper zone, where:
- A lower zone encompasses master channel 1 and an arbitrary number of ascending
MIDI channels, increasing from channel 2.
- An upper zone encompasses master channel 16 and an arbitrary number of descending
MIDI channels, decreasing from channel 15.
It also defines a pitchbend range (in semitones) to be applied for per-note pitchbends and
master pitchbends, respectively.
*/
struct MPEZone
{
enum class Type { lower, upper };
MPEZone() = default;
MPEZone (Type type, int memberChannels = 0, int perNotePitchbend = 48, int masterPitchbend = 2)
: zoneType (type),
numMemberChannels (memberChannels),
perNotePitchbendRange (perNotePitchbend),
masterPitchbendRange (masterPitchbend)
{}
bool isLowerZone() const noexcept { return zoneType == Type::lower; }
bool isUpperZone() const noexcept { return zoneType == Type::upper; }
bool isActive() const noexcept { return numMemberChannels > 0; }
int getMasterChannel() const noexcept { return isLowerZone() ? lowerZoneMasterChannel : upperZoneMasterChannel; }
int getFirstMemberChannel() const noexcept { return isLowerZone() ? lowerZoneMasterChannel + 1 : upperZoneMasterChannel - 1; }
int getLastMemberChannel() const noexcept { return isLowerZone() ? (lowerZoneMasterChannel + numMemberChannels)
: (upperZoneMasterChannel - numMemberChannels); }
bool isUsingChannelAsMemberChannel (int channel) const noexcept
{
return isLowerZone() ? (lowerZoneMasterChannel < channel && channel <= getLastMemberChannel())
: (channel < upperZoneMasterChannel && getLastMemberChannel() <= channel);
}
bool isUsing (int channel) const noexcept
{
return isUsingChannelAsMemberChannel (channel) || channel == getMasterChannel();
}
static auto tie (const MPEZone& z)
{
return std::tie (z.zoneType,
z.numMemberChannels,
z.perNotePitchbendRange,
z.masterPitchbendRange);
}
bool operator== (const MPEZone& other) const
{
return tie (*this) == tie (other);
}
bool operator!= (const MPEZone& other) const
{
return tie (*this) != tie (other);
}
//==============================================================================
static constexpr int lowerZoneMasterChannel = 1,
upperZoneMasterChannel = 16;
Type zoneType = Type::lower;
int numMemberChannels = 0;
int perNotePitchbendRange = 48;
int masterPitchbendRange = 2;
};
//==============================================================================
/**
This class represents the current MPE zone layout of a device capable of handling MPE.
@@ -44,89 +120,28 @@ namespace juce
class JUCE_API MPEZoneLayout
{
public:
/** Default constructor.
//==============================================================================
/** Creates a layout with inactive upper and lower zones. */
MPEZoneLayout() = default;
This will create a layout with inactive lower and upper zones, representing
a device with MPE mode disabled.
/** Creates a layout with the given upper and lower zones. */
MPEZoneLayout (MPEZone lower, MPEZone upper);
You can set the lower or upper MPE zones using the setZone() method.
/** Creates a layout with a single upper or lower zone, leaving the other zone uninitialised. */
MPEZoneLayout (MPEZone singleZone);
@see setZone
*/
MPEZoneLayout() noexcept;
/** Copy constuctor.
This will not copy the listeners registered to the MPEZoneLayout.
*/
MPEZoneLayout (const MPEZoneLayout& other);
/** Copy assignment operator.
This will not copy the listeners registered to the MPEZoneLayout.
*/
MPEZoneLayout& operator= (const MPEZoneLayout& other);
//==============================================================================
/**
This struct represents an MPE zone.
It can either be a lower or an upper zone, where:
- A lower zone encompasses master channel 1 and an arbitrary number of ascending
MIDI channels, increasing from channel 2.
- An upper zone encompasses master channel 16 and an arbitrary number of descending
MIDI channels, decreasing from channel 15.
It also defines a pitchbend range (in semitones) to be applied for per-note pitchbends and
master pitchbends, respectively.
*/
struct Zone
{
Zone (const Zone& other) = default;
bool isLowerZone() const noexcept { return lowerZone; }
bool isUpperZone() const noexcept { return ! lowerZone; }
bool isActive() const noexcept { return numMemberChannels > 0; }
int getMasterChannel() const noexcept { return lowerZone ? 1 : 16; }
int getFirstMemberChannel() const noexcept { return lowerZone ? 2 : 15; }
int getLastMemberChannel() const noexcept { return lowerZone ? (1 + numMemberChannels)
: (16 - numMemberChannels); }
bool isUsingChannelAsMemberChannel (int channel) const noexcept
{
return lowerZone ? (channel > 1 && channel <= 1 + numMemberChannels)
: (channel < 16 && channel >= 16 - numMemberChannels);
}
bool isUsing (int channel) const noexcept
{
return isUsingChannelAsMemberChannel (channel) || channel == getMasterChannel();
}
bool operator== (const MPEZoneLayout& other) const { return lowerZone == other.lowerZone && upperZone == other.upperZone; }
bool operator!= (const MPEZoneLayout& other) const { return ! operator== (other); }
bool operator== (const Zone& other) const noexcept { return lowerZone == other.lowerZone
&& numMemberChannels == other.numMemberChannels
&& perNotePitchbendRange == other.perNotePitchbendRange
&& masterPitchbendRange == other.masterPitchbendRange; }
bool operator!= (const Zone& other) const noexcept { return ! operator== (other); }
int numMemberChannels;
int perNotePitchbendRange;
int masterPitchbendRange;
private:
friend class MPEZoneLayout;
Zone (bool lower, int memberChans = 0, int perNotePb = 48, int masterPb = 2) noexcept
: numMemberChannels (memberChans),
perNotePitchbendRange (perNotePb),
masterPitchbendRange (masterPb),
lowerZone (lower)
{
}
//==============================================================================
/** Returns a struct representing the lower MPE zone. */
MPEZone getLowerZone() const noexcept { return lowerZone; }
bool lowerZone;
};
/** Returns a struct representing the upper MPE zone. */
MPEZone getUpperZone() const noexcept { return upperZone; }
/** Sets the lower zone of this layout. */
void setLowerZone (int numMemberChannels = 0,
@@ -138,17 +153,14 @@ public:
int perNotePitchbendRange = 48,
int masterPitchbendRange = 2) noexcept;
/** Returns a struct representing the lower MPE zone. */
const Zone getLowerZone() const noexcept { return lowerZone; }
/** Returns a struct representing the upper MPE zone. */
const Zone getUpperZone() const noexcept { return upperZone; }
/** Clears the lower and upper zones of this layout, making them both inactive
and disabling MPE mode.
*/
void clearAllZones();
/** Returns true if either of the zones are active. */
bool isActive() const { return lowerZone.isActive() || upperZone.isActive(); }
//==============================================================================
/** 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
@@ -200,10 +212,14 @@ public:
/** Removes a listener. */
void removeListener (Listener* const listenerToRemove) noexcept;
#ifndef DOXYGEN
using Zone = MPEZone;
#endif
private:
//==============================================================================
Zone lowerZone { true, 0 };
Zone upperZone { false, 0 };
MPEZone lowerZone { MPEZone::Type::lower, 0 };
MPEZone upperZone { MPEZone::Type::upper, 0 };
MidiRPNDetector rpnDetector;
ListenerList<Listener> listeners;
@@ -215,8 +231,8 @@ private:
void processZoneLayoutRpnMessage (MidiRPNMessage);
void processPitchbendRangeRpnMessage (MidiRPNMessage);
void updateMasterPitchbend (Zone&, int);
void updatePerNotePitchbendRange (Zone&, int);
void updateMasterPitchbend (MPEZone&, int);
void updatePerNotePitchbendRange (MPEZone&, int);
void sendLayoutChangeMessage();
void checkAndLimitZoneParameters (int, int, int&) noexcept;


+ 144
- 132
libs/juce-current/source/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -23,10 +23,140 @@
namespace juce
{
#if ! DOXYGEN && (JUCE_MAC || JUCE_IOS)
#if ! defined (DOXYGEN) && (JUCE_MAC || JUCE_IOS)
struct CoreAudioLayouts
{
//==============================================================================
struct LayoutTagSpeakerList
{
AudioChannelLayoutTag tag;
AudioChannelSet::ChannelType channelTypes[16];
};
//==============================================================================
// This list has been derived from https://pastebin.com/24dQ4BPJ
// Apple channel labels have been replaced by JUCE channel names
// This means that some layouts will be identical in JUCE but not in CoreAudio
// In Apple's official definition the following tags exist with the same speaker layout and order
// even when *not* represented in JUCE channels
// kAudioChannelLayoutTag_Binaural = kAudioChannelLayoutTag_Stereo
// kAudioChannelLayoutTag_MPEG_5_0_B = kAudioChannelLayoutTag_Pentagonal
// kAudioChannelLayoutTag_ITU_2_2 = kAudioChannelLayoutTag_Quadraphonic
// kAudioChannelLayoutTag_AudioUnit_6_0 = kAudioChannelLayoutTag_Hexagonal
struct SpeakerLayoutTable : AudioChannelSet // save us some typing
{
template <typename... Items>
static constexpr auto getArray (Items... items)
{
return std::array<LayoutTagSpeakerList, sizeof... (items)> { { items... } };
}
static constexpr auto get()
{
using List = LayoutTagSpeakerList;
return getArray (List { kAudioChannelLayoutTag_Mono, { centre } },
List { kAudioChannelLayoutTag_Stereo, { left, right } },
List { kAudioChannelLayoutTag_MPEG_3_0_A, { left, right, centre } },
List { kAudioChannelLayoutTag_ITU_2_1, { left, right, centreSurround } },
List { kAudioChannelLayoutTag_MPEG_4_0_A, { left, right, centre, centreSurround } },
List { kAudioChannelLayoutTag_MPEG_5_0_A, { left, right, centre, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_MPEG_5_1_A, { left, right, centre, LFE, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_AudioUnit_6_0, { left, right, leftSurround, rightSurround, centre, centreSurround } },
List { kAudioChannelLayoutTag_MPEG_6_1_A, { left, right, centre, LFE, leftSurround, rightSurround, centreSurround } },
List { kAudioChannelLayoutTag_DTS_6_0_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_DTS_6_1_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE } },
List { kAudioChannelLayoutTag_AudioUnit_7_0, { left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear } },
List { kAudioChannelLayoutTag_AudioUnit_7_0_Front, { left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre } },
List { kAudioChannelLayoutTag_MPEG_7_1_C, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear } },
List { kAudioChannelLayoutTag_MPEG_7_1_A, { left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre } },
List { kAudioChannelLayoutTag_Ambisonic_B_Format, { ambisonicW, ambisonicX, ambisonicY, ambisonicZ } },
List { kAudioChannelLayoutTag_Quadraphonic, { left, right, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_Pentagonal, { left, right, leftSurroundRear, rightSurroundRear, centre } },
List { kAudioChannelLayoutTag_Hexagonal, { left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround } },
List { kAudioChannelLayoutTag_Octagonal, { left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight } },
#if defined (MAC_OS_VERSION_11_0)
List { kAudioChannelLayoutTag_Atmos_5_1_4, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } },
List { kAudioChannelLayoutTag_Atmos_7_1_2, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight } },
#endif
#if defined (MAC_OS_X_VERSION_10_15)
List { kAudioChannelLayoutTag_Atmos_5_1_2, { left, right, centre, LFE, leftSurround, rightSurround, topSideLeft, topSideRight } },
List { kAudioChannelLayoutTag_Atmos_7_1_4, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topFrontLeft, topFrontRight, topRearLeft, topRearRight } },
List { kAudioChannelLayoutTag_Atmos_9_1_6, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, wideLeft, wideRight, topFrontLeft, topFrontRight, topSideLeft, topSideRight, topRearLeft, topRearRight } },
#endif
// More uncommon layouts...
List { kAudioChannelLayoutTag_StereoHeadphones, { left, right } },
List { kAudioChannelLayoutTag_MatrixStereo, { left, right } },
List { kAudioChannelLayoutTag_MidSide, { centre, discreteChannel0 } },
List { kAudioChannelLayoutTag_XY, { ambisonicX, ambisonicY } },
List { kAudioChannelLayoutTag_Binaural, { left, right } },
List { kAudioChannelLayoutTag_Cube, { left, right, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } },
List { kAudioChannelLayoutTag_MPEG_3_0_B, { centre, left, right } },
List { kAudioChannelLayoutTag_MPEG_4_0_B, { centre, left, right, centreSurround } },
List { kAudioChannelLayoutTag_MPEG_5_0_B, { left, right, leftSurround, rightSurround, centre } },
List { kAudioChannelLayoutTag_MPEG_5_0_C, { left, centre, right, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_MPEG_5_0_D, { centre, left, right, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_MPEG_5_1_B, { left, right, leftSurround, rightSurround, centre, LFE } },
List { kAudioChannelLayoutTag_MPEG_5_1_C, { left, centre, right, leftSurround, rightSurround, LFE } },
List { kAudioChannelLayoutTag_MPEG_5_1_D, { centre, left, right, leftSurround, rightSurround, LFE } },
List { kAudioChannelLayoutTag_MPEG_7_1_B, { centre, leftCentre, rightCentre, left, right, leftSurround, rightSurround, LFE } },
List { kAudioChannelLayoutTag_Emagic_Default_7_1, { left, right, leftSurround, rightSurround, centre, LFE, leftCentre, rightCentre } },
List { kAudioChannelLayoutTag_SMPTE_DTV, { left, right, centre, LFE, leftSurround, rightSurround, discreteChannel0 /* leftMatrixTotal */, (ChannelType) (discreteChannel0 + 1) /* rightMatrixTotal */} },
List { kAudioChannelLayoutTag_ITU_2_2, { left, right, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_DVD_4, { left, right, LFE } },
List { kAudioChannelLayoutTag_DVD_5, { left, right, LFE, centreSurround } },
List { kAudioChannelLayoutTag_DVD_6, { left, right, LFE, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_DVD_10, { left, right, centre, LFE } },
List { kAudioChannelLayoutTag_DVD_11, { left, right, centre, LFE, centreSurround } },
List { kAudioChannelLayoutTag_DVD_18, { left, right, leftSurround, rightSurround, LFE } },
List { kAudioChannelLayoutTag_AAC_6_0, { centre, left, right, leftSurround, rightSurround, centreSurround } },
List { kAudioChannelLayoutTag_AAC_6_1, { centre, left, right, leftSurround, rightSurround, centreSurround, LFE } },
List { kAudioChannelLayoutTag_AAC_7_0, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } },
List { kAudioChannelLayoutTag_AAC_7_1_B, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } },
List { kAudioChannelLayoutTag_AAC_7_1_C, { centre, left, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } },
List { kAudioChannelLayoutTag_AAC_Octagonal, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, centreSurround } },
List { kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } },
List { kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } },
List { kAudioChannelLayoutTag_AC3_3_0, { left, centre, right } },
List { kAudioChannelLayoutTag_AC3_3_1, { left, centre, right, centreSurround } },
List { kAudioChannelLayoutTag_AC3_3_0_1, { left, centre, right, LFE } },
List { kAudioChannelLayoutTag_AC3_2_1_1, { left, right, centreSurround, LFE } },
List { kAudioChannelLayoutTag_AC3_3_1_1, { left, centre, right, centreSurround, LFE } },
List { kAudioChannelLayoutTag_EAC_6_0_A, { left, centre, right, leftSurround, rightSurround, centreSurround } },
List { kAudioChannelLayoutTag_EAC_7_0_A, { left, centre, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } },
List { kAudioChannelLayoutTag_EAC3_6_1_A, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } },
List { kAudioChannelLayoutTag_EAC3_6_1_B, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } },
List { kAudioChannelLayoutTag_EAC3_6_1_C, { left, centre, right, leftSurround, rightSurround, LFE, topFrontCentre } },
List { kAudioChannelLayoutTag_EAC3_7_1_A, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundRear, rightSurroundRear } },
List { kAudioChannelLayoutTag_EAC3_7_1_B, { left, centre, right, leftSurround, rightSurround, LFE, leftCentre, rightCentre } },
List { kAudioChannelLayoutTag_EAC3_7_1_C, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide } },
List { kAudioChannelLayoutTag_EAC3_7_1_D, { left, centre, right, leftSurround, rightSurround, LFE, wideLeft, wideRight } },
List { kAudioChannelLayoutTag_EAC3_7_1_E, { left, centre, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } },
List { kAudioChannelLayoutTag_EAC3_7_1_F, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topMiddle } },
List { kAudioChannelLayoutTag_EAC3_7_1_G, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } },
List { kAudioChannelLayoutTag_EAC3_7_1_H, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } },
List { kAudioChannelLayoutTag_DTS_3_1, { centre, left, right, LFE } },
List { kAudioChannelLayoutTag_DTS_4_1, { centre, left, right, centreSurround, LFE } },
List { kAudioChannelLayoutTag_DTS_6_0_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround } },
List { kAudioChannelLayoutTag_DTS_6_0_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear } },
List { kAudioChannelLayoutTag_DTS_6_1_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround, LFE } },
List { kAudioChannelLayoutTag_DTS_6_1_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear, LFE } },
List { kAudioChannelLayoutTag_DTS_6_1_D, { centre, left, right, leftSurround, rightSurround, LFE, centreSurround } },
List { kAudioChannelLayoutTag_DTS_7_0, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround } },
List { kAudioChannelLayoutTag_DTS_7_1, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE } },
List { kAudioChannelLayoutTag_DTS_8_0_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } },
List { kAudioChannelLayoutTag_DTS_8_0_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround } },
List { kAudioChannelLayoutTag_DTS_8_1_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } },
List { kAudioChannelLayoutTag_DTS_8_1_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround, LFE } });
}
};
public:
//==============================================================================
enum
{
@@ -61,16 +191,16 @@ struct CoreAudioLayouts
if (set.getAmbisonicOrder() >= 0)
return coreAudioHOASN3DLayoutTag | static_cast<unsigned> (set.size());
for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl)
for (const auto& item : SpeakerLayoutTable::get())
{
AudioChannelSet caSet;
for (int i = 0; i < numElementsInArray (tbl->channelTypes)
&& tbl->channelTypes[i] != AudioChannelSet::unknown; ++i)
caSet.addChannel (tbl->channelTypes[i]);
for (int i = 0; i < numElementsInArray (item.channelTypes)
&& item.channelTypes[i] != AudioChannelSet::unknown; ++i)
caSet.addChannel (item.channelTypes[i]);
if (caSet == set)
return tbl->tag;
return item.tag;
}
return kAudioChannelLayoutTag_DiscreteInOrder | static_cast<AudioChannelLayoutTag> (set.size());
@@ -121,13 +251,13 @@ struct CoreAudioLayouts
Array<AudioChannelSet::ChannelType> speakers;
for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl)
for (const auto& item : SpeakerLayoutTable::get())
{
if (tag == tbl->tag)
if (tag == item.tag)
{
for (int i = 0; i < numElementsInArray (tbl->channelTypes)
&& tbl->channelTypes[i] != AudioChannelSet::unknown; ++i)
speakers.add (tbl->channelTypes[i]);
for (int i = 0; i < numElementsInArray (item.channelTypes)
&& item.channelTypes[i] != AudioChannelSet::unknown; ++i)
speakers.add (item.channelTypes[i]);
return speakers;
}
@@ -150,19 +280,12 @@ struct CoreAudioLayouts
}
private:
//==============================================================================
struct LayoutTagSpeakerList
{
AudioChannelLayoutTag tag;
AudioChannelSet::ChannelType channelTypes[16];
};
static Array<AudioChannelLayoutTag> createKnownCoreAudioTags()
{
Array<AudioChannelLayoutTag> tags;
for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl)
tags.addIfNotAlreadyThere (tbl->tag);
for (const auto& item : SpeakerLayoutTable::get())
tags.addIfNotAlreadyThere (item.tag);
for (unsigned order = 0; order <= 5; ++order)
tags.addIfNotAlreadyThere (coreAudioHOASN3DLayoutTag | ((order + 1) * (order + 1)));
@@ -170,117 +293,6 @@ private:
return tags;
}
//==============================================================================
// This list has been derived from https://pastebin.com/24dQ4BPJ
// Apple channel labels have been replaced by JUCE channel names
// This means that some layouts will be identical in JUCE but not in CoreAudio
// In Apple's official definition the following tags exist with the same speaker layout and order
// even when *not* represented in JUCE channels
// kAudioChannelLayoutTag_Binaural = kAudioChannelLayoutTag_Stereo
// kAudioChannelLayoutTag_MPEG_5_0_B = kAudioChannelLayoutTag_Pentagonal
// kAudioChannelLayoutTag_ITU_2_2 = kAudioChannelLayoutTag_Quadraphonic
// kAudioChannelLayoutTag_AudioUnit_6_0 = kAudioChannelLayoutTag_Hexagonal
struct SpeakerLayoutTable : AudioChannelSet // save us some typing
{
static LayoutTagSpeakerList* get() noexcept
{
static LayoutTagSpeakerList tbl[] = {
// list layouts for which there is a corresponding named AudioChannelSet first
{ kAudioChannelLayoutTag_Mono, { centre } },
{ kAudioChannelLayoutTag_Stereo, { left, right } },
{ kAudioChannelLayoutTag_MPEG_3_0_A, { left, right, centre } },
{ kAudioChannelLayoutTag_ITU_2_1, { left, right, centreSurround } },
{ kAudioChannelLayoutTag_MPEG_4_0_A, { left, right, centre, centreSurround } },
{ kAudioChannelLayoutTag_MPEG_5_0_A, { left, right, centre, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_MPEG_5_1_A, { left, right, centre, LFE, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_AudioUnit_6_0, { left, right, leftSurround, rightSurround, centre, centreSurround } },
{ kAudioChannelLayoutTag_MPEG_6_1_A, { left, right, centre, LFE, leftSurround, rightSurround, centreSurround } },
{ kAudioChannelLayoutTag_DTS_6_0_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_DTS_6_1_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE } },
{ kAudioChannelLayoutTag_AudioUnit_7_0, { left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear } },
{ kAudioChannelLayoutTag_AudioUnit_7_0_Front, { left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre } },
{ kAudioChannelLayoutTag_MPEG_7_1_C, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear } },
{ kAudioChannelLayoutTag_MPEG_7_1_A, { left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre } },
{ kAudioChannelLayoutTag_Ambisonic_B_Format, { ambisonicW, ambisonicX, ambisonicY, ambisonicZ } },
{ kAudioChannelLayoutTag_Quadraphonic, { left, right, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_Pentagonal, { left, right, leftSurroundRear, rightSurroundRear, centre } },
{ kAudioChannelLayoutTag_Hexagonal, { left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround } },
{ kAudioChannelLayoutTag_Octagonal, { left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight } },
// more uncommon layouts
{ kAudioChannelLayoutTag_StereoHeadphones, { left, right } },
{ kAudioChannelLayoutTag_MatrixStereo, { left, right } },
{ kAudioChannelLayoutTag_MidSide, { centre, discreteChannel0 } },
{ kAudioChannelLayoutTag_XY, { ambisonicX, ambisonicY } },
{ kAudioChannelLayoutTag_Binaural, { left, right } },
{ kAudioChannelLayoutTag_Cube, { left, right, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } },
{ kAudioChannelLayoutTag_MPEG_3_0_B, { centre, left, right } },
{ kAudioChannelLayoutTag_MPEG_4_0_B, { centre, left, right, centreSurround } },
{ kAudioChannelLayoutTag_MPEG_5_0_B, { left, right, leftSurround, rightSurround, centre } },
{ kAudioChannelLayoutTag_MPEG_5_0_C, { left, centre, right, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_MPEG_5_0_D, { centre, left, right, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_MPEG_5_1_B, { left, right, leftSurround, rightSurround, centre, LFE } },
{ kAudioChannelLayoutTag_MPEG_5_1_C, { left, centre, right, leftSurround, rightSurround, LFE } },
{ kAudioChannelLayoutTag_MPEG_5_1_D, { centre, left, right, leftSurround, rightSurround, LFE } },
{ kAudioChannelLayoutTag_MPEG_7_1_B, { centre, leftCentre, rightCentre, left, right, leftSurround, rightSurround, LFE } },
{ kAudioChannelLayoutTag_Emagic_Default_7_1, { left, right, leftSurround, rightSurround, centre, LFE, leftCentre, rightCentre } },
{ kAudioChannelLayoutTag_SMPTE_DTV, { left, right, centre, LFE, leftSurround, rightSurround, discreteChannel0 /* leftMatrixTotal */, (ChannelType) (discreteChannel0 + 1) /* rightMatrixTotal */} },
{ kAudioChannelLayoutTag_ITU_2_2, { left, right, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_DVD_4, { left, right, LFE } },
{ kAudioChannelLayoutTag_DVD_5, { left, right, LFE, centreSurround } },
{ kAudioChannelLayoutTag_DVD_6, { left, right, LFE, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_DVD_10, { left, right, centre, LFE } },
{ kAudioChannelLayoutTag_DVD_11, { left, right, centre, LFE, centreSurround } },
{ kAudioChannelLayoutTag_DVD_18, { left, right, leftSurround, rightSurround, LFE } },
{ kAudioChannelLayoutTag_AAC_6_0, { centre, left, right, leftSurround, rightSurround, centreSurround } },
{ kAudioChannelLayoutTag_AAC_6_1, { centre, left, right, leftSurround, rightSurround, centreSurround, LFE } },
{ kAudioChannelLayoutTag_AAC_7_0, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } },
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_8
{ kAudioChannelLayoutTag_AAC_7_1_B, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } },
{ kAudioChannelLayoutTag_AAC_7_1_C, { centre, left, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } },
#endif
{ kAudioChannelLayoutTag_AAC_Octagonal, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, centreSurround } },
{ kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } },
{ kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } },
{ kAudioChannelLayoutTag_AC3_3_0, { left, centre, right } },
{ kAudioChannelLayoutTag_AC3_3_1, { left, centre, right, centreSurround } },
{ kAudioChannelLayoutTag_AC3_3_0_1, { left, centre, right, LFE } },
{ kAudioChannelLayoutTag_AC3_2_1_1, { left, right, centreSurround, LFE } },
{ kAudioChannelLayoutTag_AC3_3_1_1, { left, centre, right, centreSurround, LFE } },
{ kAudioChannelLayoutTag_EAC_6_0_A, { left, centre, right, leftSurround, rightSurround, centreSurround } },
{ kAudioChannelLayoutTag_EAC_7_0_A, { left, centre, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } },
{ kAudioChannelLayoutTag_EAC3_6_1_A, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } },
{ kAudioChannelLayoutTag_EAC3_6_1_B, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } },
{ kAudioChannelLayoutTag_EAC3_6_1_C, { left, centre, right, leftSurround, rightSurround, LFE, topFrontCentre } },
{ kAudioChannelLayoutTag_EAC3_7_1_A, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundRear, rightSurroundRear } },
{ kAudioChannelLayoutTag_EAC3_7_1_B, { left, centre, right, leftSurround, rightSurround, LFE, leftCentre, rightCentre } },
{ kAudioChannelLayoutTag_EAC3_7_1_C, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide } },
{ kAudioChannelLayoutTag_EAC3_7_1_D, { left, centre, right, leftSurround, rightSurround, LFE, wideLeft, wideRight } },
{ kAudioChannelLayoutTag_EAC3_7_1_E, { left, centre, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } },
{ kAudioChannelLayoutTag_EAC3_7_1_F, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topMiddle } },
{ kAudioChannelLayoutTag_EAC3_7_1_G, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } },
{ kAudioChannelLayoutTag_EAC3_7_1_H, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } },
{ kAudioChannelLayoutTag_DTS_3_1, { centre, left, right, LFE } },
{ kAudioChannelLayoutTag_DTS_4_1, { centre, left, right, centreSurround, LFE } },
{ kAudioChannelLayoutTag_DTS_6_0_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround } },
{ kAudioChannelLayoutTag_DTS_6_0_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear } },
{ kAudioChannelLayoutTag_DTS_6_1_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround, LFE } },
{ kAudioChannelLayoutTag_DTS_6_1_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear, LFE } },
{ kAudioChannelLayoutTag_DTS_6_1_D, { centre, left, right, leftSurround, rightSurround, LFE, centreSurround } },
{ kAudioChannelLayoutTag_DTS_7_0, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround } },
{ kAudioChannelLayoutTag_DTS_7_1, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE } },
{ kAudioChannelLayoutTag_DTS_8_0_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } },
{ kAudioChannelLayoutTag_DTS_8_0_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround } },
{ kAudioChannelLayoutTag_DTS_8_1_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } },
{ kAudioChannelLayoutTag_DTS_8_1_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround, LFE } },
{ 0, {} }
};
return tbl;
}
};
//==============================================================================
static AudioChannelSet::ChannelType getChannelTypeFromAudioChannelLabel (AudioChannelLabel label) noexcept
{


+ 80
- 0
libs/juce-current/source/modules/juce_audio_basics/native/juce_mac_CoreAudioTimeConversions.h View File

@@ -0,0 +1,80 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
// This file will be included directly by macOS/iOS-specific .cpps
#pragma once
#if ! DOXYGEN
#include <mach/mach_time.h>
namespace juce
{
struct CoreAudioTimeConversions
{
public:
CoreAudioTimeConversions()
{
mach_timebase_info_data_t info{};
mach_timebase_info (&info);
numerator = info.numer;
denominator = info.denom;
}
uint64_t hostTimeToNanos (uint64_t hostTime) const
{
return multiplyByRatio (hostTime, numerator, denominator);
}
uint64_t nanosToHostTime (uint64_t nanos) const
{
return multiplyByRatio (nanos, denominator, numerator);
}
private:
// Adapted from CAHostTimeBase.h in the Core Audio Utility Classes
static uint64_t multiplyByRatio (uint64_t toMultiply, uint64_t numerator, uint64_t denominator)
{
#if defined (__SIZEOF_INT128__)
unsigned __int128
#else
long double
#endif
result = toMultiply;
if (numerator != denominator)
{
result *= numerator;
result /= denominator;
}
return (uint64_t) result;
}
uint64_t numerator = 0, denominator = 0;
};
} // namespace juce
#endif

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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 87
- 73
libs/juce-current/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -65,6 +65,8 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne
buffer.setSize (numberOfChannels, bufferSizeNeeded);
buffer.clear();
const ScopedLock sl (bufferRangeLock);
bufferValidStart = 0;
bufferValidEnd = 0;
@@ -72,6 +74,8 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne
do
{
const ScopedUnlock ul (bufferRangeLock);
backgroundThread.moveToFrontOfQueue (this);
Thread::sleep (5);
}
@@ -96,100 +100,97 @@ void BufferingAudioSource::releaseResources()
void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
{
const ScopedLock sl (bufferStartPosLock);
auto start = bufferValidStart.load();
auto end = bufferValidEnd.load();
auto pos = nextPlayPos.load();
auto validStart = (int) (jlimit (start, end, pos) - pos);
auto validEnd = (int) (jlimit (start, end, pos + info.numSamples) - pos);
const auto bufferRange = getValidBufferRange (info.numSamples);
if (validStart == validEnd)
if (bufferRange.isEmpty())
{
// total cache miss
info.clearActiveBufferRegion();
return;
}
else
{
if (validStart > 0)
info.buffer->clear (info.startSample, validStart); // partial cache miss at start
if (validEnd < info.numSamples)
info.buffer->clear (info.startSample + validEnd,
info.numSamples - validEnd); // partial cache miss at end
const auto validStart = bufferRange.getStart();
const auto validEnd = bufferRange.getEnd();
const ScopedLock sl (callbackLock);
if (validStart > 0)
info.buffer->clear (info.startSample, validStart); // partial cache miss at start
if (validStart < validEnd)
if (validEnd < info.numSamples)
info.buffer->clear (info.startSample + validEnd,
info.numSamples - validEnd); // partial cache miss at end
if (validStart < validEnd)
{
for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
{
for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;)
jassert (buffer.getNumSamples() > 0);
const auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
const auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
if (startBufferIndex < endBufferIndex)
{
jassert (buffer.getNumSamples() > 0);
auto startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples());
auto endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples());
if (startBufferIndex < endBufferIndex)
{
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
validEnd - validStart);
}
else
{
auto initialSize = buffer.getNumSamples() - startBufferIndex;
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
initialSize);
info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
buffer,
chan, 0,
(validEnd - validStart) - initialSize);
}
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
validEnd - validStart);
}
}
else
{
const auto initialSize = buffer.getNumSamples() - startBufferIndex;
nextPlayPos += info.numSamples;
info.buffer->copyFrom (chan, info.startSample + validStart,
buffer,
chan, startBufferIndex,
initialSize);
info.buffer->copyFrom (chan, info.startSample + validStart + initialSize,
buffer,
chan, 0,
(validEnd - validStart) - initialSize);
}
}
}
nextPlayPos += info.numSamples;
}
bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, uint32 timeout)
{
if (!source || source->getTotalLength() <= 0)
if (source == nullptr || source->getTotalLength() <= 0)
return false;
if (nextPlayPos + info.numSamples < 0)
return true;
if (! isLooping() && nextPlayPos > getTotalLength())
if ((nextPlayPos + info.numSamples < 0)
|| (! isLooping() && nextPlayPos > getTotalLength()))
return true;
auto now = Time::getMillisecondCounter();
auto startTime = now;
const auto startTime = Time::getMillisecondCounter();
auto now = startTime;
auto elapsed = (now >= startTime ? now - startTime
: (std::numeric_limits<uint32>::max() - startTime) + now);
while (elapsed <= timeout)
{
{
const ScopedLock sl (bufferStartPosLock);
auto start = bufferValidStart.load();
auto end = bufferValidEnd.load();
auto pos = nextPlayPos.load();
const auto bufferRange = getValidBufferRange (info.numSamples);
auto validStart = static_cast<int> (jlimit (start, end, pos) - pos);
auto validEnd = static_cast<int> (jlimit (start, end, pos + info.numSamples) - pos);
const auto validStart = bufferRange.getStart();
const auto validEnd = bufferRange.getEnd();
if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples)
return true;
if (validStart <= 0
&& validStart < validEnd
&& validEnd >= info.numSamples)
{
return true;
}
if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed))))
if (elapsed < timeout
&& ! bufferReadyEvent.wait (static_cast<int> (timeout - elapsed)))
{
return false;
}
now = Time::getMillisecondCounter();
elapsed = (now >= startTime ? now - startTime
@@ -202,7 +203,7 @@ bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelI
int64 BufferingAudioSource::getNextReadPosition() const
{
jassert (source->getTotalLength() > 0);
auto pos = nextPlayPos.load();
const auto pos = nextPlayPos.load();
return (source->isLooping() && nextPlayPos > 0)
? pos % source->getTotalLength()
@@ -211,18 +212,28 @@ int64 BufferingAudioSource::getNextReadPosition() const
void BufferingAudioSource::setNextReadPosition (int64 newPosition)
{
const ScopedLock sl (bufferStartPosLock);
const ScopedLock sl (bufferRangeLock);
nextPlayPos = newPosition;
backgroundThread.moveToFrontOfQueue (this);
}
Range<int> BufferingAudioSource::getValidBufferRange (int numSamples) const
{
const ScopedLock sl (bufferRangeLock);
const auto pos = nextPlayPos.load();
return { (int) (jlimit (bufferValidStart, bufferValidEnd, pos) - pos),
(int) (jlimit (bufferValidStart, bufferValidEnd, pos + numSamples) - pos) };
}
bool BufferingAudioSource::readNextBufferChunk()
{
int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd;
{
const ScopedLock sl (bufferStartPosLock);
const ScopedLock sl (bufferRangeLock);
if (wasSourceLooping != isLooping())
{
@@ -236,7 +247,7 @@ bool BufferingAudioSource::readNextBufferChunk()
sectionToReadStart = 0;
sectionToReadEnd = 0;
const int maxChunkSize = 2048;
constexpr int maxChunkSize = 2048;
if (newBVS < bufferValidStart || newBVS >= bufferValidEnd)
{
@@ -257,7 +268,7 @@ bool BufferingAudioSource::readNextBufferChunk()
sectionToReadEnd = newBVE;
bufferValidStart = newBVS;
bufferValidEnd = jmin (bufferValidEnd.load(), newBVE);
bufferValidEnd = jmin (bufferValidEnd, newBVE);
}
}
@@ -265,8 +276,9 @@ bool BufferingAudioSource::readNextBufferChunk()
return false;
jassert (buffer.getNumSamples() > 0);
auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
const auto bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples());
const auto bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples());
if (bufferIndexStart < bufferIndexEnd)
{
@@ -276,7 +288,7 @@ bool BufferingAudioSource::readNextBufferChunk()
}
else
{
auto initialSize = buffer.getNumSamples() - bufferIndexStart;
const auto initialSize = buffer.getNumSamples() - bufferIndexStart;
readBufferSection (sectionToReadStart,
initialSize,
@@ -288,7 +300,7 @@ bool BufferingAudioSource::readNextBufferChunk()
}
{
const ScopedLock sl2 (bufferStartPosLock);
const ScopedLock sl2 (bufferRangeLock);
bufferValidStart = newBVS;
bufferValidEnd = newBVE;
@@ -304,6 +316,8 @@ void BufferingAudioSource::readBufferSection (int64 start, int length, int buffe
source->setNextReadPosition (start);
AudioSourceChannelInfo info (&buffer, bufferOffset, length);
const ScopedLock sl (callbackLock);
source->getNextAudioBlock (info);
}


+ 13
- 8
libs/juce-current/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -98,21 +98,26 @@ public:
bool waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout);
private:
//==============================================================================
Range<int> getValidBufferRange (int numSamples) const;
bool readNextBufferChunk();
void readBufferSection (int64 start, int length, int bufferOffset);
int useTimeSlice() override;
//==============================================================================
OptionalScopedPointer<PositionableAudioSource> source;
TimeSliceThread& backgroundThread;
int numberOfSamplesToBuffer, numberOfChannels;
AudioBuffer<float> buffer;
CriticalSection bufferStartPosLock;
CriticalSection callbackLock, bufferRangeLock;
WaitableEvent bufferReadyEvent;
std::atomic<int64> bufferValidStart { 0 }, bufferValidEnd { 0 }, nextPlayPos { 0 };
int64 bufferValidStart = 0, bufferValidEnd = 0;
std::atomic<int64> nextPlayPos { 0 };
double sampleRate = 0;
bool wasSourceLooping = false, isPrepared = false, prefillBuffer;
bool readNextBufferChunk();
void readBufferSection (int64 start, int length, int bufferOffset);
int useTimeSlice() override;
bool wasSourceLooping = false, isPrepared = false;
const bool prefillBuffer;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource)
};


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 1
- 9
libs/juce-current/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -631,14 +631,6 @@ private:
template <typename floatType>
void processNextBlock (AudioBuffer<floatType>&, const MidiBuffer&, int startSample, int numSamples);
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// Note the new parameters for these methods.
virtual int findFreeVoice (const bool) const { return 0; }
virtual int noteOff (int, int, int) { return 0; }
virtual int findFreeVoice (SynthesiserSound*, const bool) { return 0; }
virtual int findVoiceToSteal (SynthesiserSound*) const { return 0; }
#endif
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser)
};


+ 128
- 79
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_ADSR.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -31,16 +31,19 @@ namespace juce
with setParameters() then call getNextSample() to get the envelope value to be applied
to each audio sample or applyEnvelopeToBuffer() to apply the envelope to a whole buffer.
Do not change the parameters during playback. If you change the parameters before the
release stage has completed then you must call reset() before the next call to
noteOn().
@tags{Audio}
*/
class ADSR
class JUCE_API ADSR
{
public:
//==============================================================================
ADSR()
{
setSampleRate (44100.0);
setParameters ({});
recalculateRates();
}
//==============================================================================
@@ -49,19 +52,22 @@ public:
@tags{Audio}
*/
struct Parameters
struct JUCE_API Parameters
{
/** Attack time in seconds. */
float attack = 0.1f;
/** Decay time in seconds. */
float decay = 0.1f;
/** Sustain level. */
float sustain = 1.0f;
Parameters() = default;
Parameters (float attackTimeSeconds,
float decayTimeSeconds,
float sustainLevel,
float releaseTimeSeconds)
: attack (attackTimeSeconds),
decay (decayTimeSeconds),
sustain (sustainLevel),
release (releaseTimeSeconds)
{
}
/** Release time in seconds. */
float release = 0.1f;
float attack = 0.1f, decay = 0.1f, sustain = 1.0f, release = 0.1f;
};
/** Sets the parameters that will be used by an ADSR object.
@@ -73,70 +79,69 @@ public:
*/
void setParameters (const Parameters& newParameters)
{
currentParameters = newParameters;
sustainLevel = newParameters.sustain;
calculateRates (newParameters);
// need to call setSampleRate() first!
jassert (sampleRate > 0.0);
if (currentState != State::idle)
checkCurrentState();
parameters = newParameters;
recalculateRates();
}
/** Returns the parameters currently being used by an ADSR object.
@see setParameters
*/
const Parameters& getParameters() const { return currentParameters; }
const Parameters& getParameters() const noexcept { return parameters; }
/** Returns true if the envelope is in its attack, decay, sustain or release stage. */
bool isActive() const noexcept { return currentState != State::idle; }
bool isActive() const noexcept { return state != State::idle; }
//==============================================================================
/** Sets the sample rate that will be used for the envelope.
This must be called before the getNextSample() or setParameters() methods.
*/
void setSampleRate (double sampleRate)
void setSampleRate (double newSampleRate) noexcept
{
jassert (sampleRate > 0.0);
sr = sampleRate;
jassert (newSampleRate > 0.0);
sampleRate = newSampleRate;
}
//==============================================================================
/** Resets the envelope to an idle state. */
void reset()
void reset() noexcept
{
envelopeVal = 0.0f;
currentState = State::idle;
state = State::idle;
}
/** Starts the attack phase of the envelope. */
void noteOn()
void noteOn() noexcept
{
if (attackRate > 0.0f)
{
currentState = State::attack;
state = State::attack;
}
else if (decayRate > 0.0f)
{
envelopeVal = 1.0f;
currentState = State::decay;
state = State::decay;
}
else
{
currentState = State::sustain;
envelopeVal = parameters.sustain;
state = State::sustain;
}
}
/** Starts the release phase of the envelope. */
void noteOff()
void noteOff() noexcept
{
if (currentState != State::idle)
if (state != State::idle)
{
if (currentParameters.release > 0.0f)
if (parameters.release > 0.0f)
{
releaseRate = static_cast<float> (envelopeVal / (currentParameters.release * sr));
currentState = State::release;
releaseRate = (float) (envelopeVal / (parameters.release * sampleRate));
state = State::release;
}
else
{
@@ -150,45 +155,56 @@ public:
@see applyEnvelopeToBuffer
*/
float getNextSample()
float getNextSample() noexcept
{
if (currentState == State::idle)
return 0.0f;
if (currentState == State::attack)
switch (state)
{
envelopeVal += attackRate;
case State::idle:
{
return 0.0f;
}
if (envelopeVal >= 1.0f)
case State::attack:
{
envelopeVal = 1.0f;
envelopeVal += attackRate;
if (decayRate > 0.0f)
currentState = State::decay;
else
currentState = State::sustain;
if (envelopeVal >= 1.0f)
{
envelopeVal = 1.0f;
goToNextState();
}
break;
}
}
else if (currentState == State::decay)
{
envelopeVal -= decayRate;
if (envelopeVal <= sustainLevel)
case State::decay:
{
envelopeVal = sustainLevel;
currentState = State::sustain;
envelopeVal -= decayRate;
if (envelopeVal <= parameters.sustain)
{
envelopeVal = parameters.sustain;
goToNextState();
}
break;
}
}
else if (currentState == State::sustain)
{
envelopeVal = sustainLevel;
}
else if (currentState == State::release)
{
envelopeVal -= releaseRate;
if (envelopeVal <= 0.0f)
reset();
case State::sustain:
{
envelopeVal = parameters.sustain;
break;
}
case State::release:
{
envelopeVal -= releaseRate;
if (envelopeVal <= 0.0f)
goToNextState();
break;
}
}
return envelopeVal;
@@ -204,6 +220,18 @@ public:
{
jassert (startSample + numSamples <= buffer.getNumSamples());
if (state == State::idle)
{
buffer.clear (startSample, numSamples);
return;
}
if (state == State::sustain)
{
buffer.applyGain (startSample, numSamples, parameters.sustain);
return;
}
auto numChannels = buffer.getNumChannels();
while (--numSamples >= 0)
@@ -219,30 +247,51 @@ public:
private:
//==============================================================================
void calculateRates (const Parameters& parameters)
void recalculateRates() noexcept
{
// need to call setSampleRate() first!
jassert (sr > 0.0);
auto getRate = [] (float distance, float timeInSeconds, double sr)
{
return timeInSeconds > 0.0f ? (float) (distance / (timeInSeconds * sr)) : -1.0f;
};
attackRate = getRate (1.0f, parameters.attack, sampleRate);
decayRate = getRate (1.0f - parameters.sustain, parameters.decay, sampleRate);
releaseRate = getRate (parameters.sustain, parameters.release, sampleRate);
attackRate = (parameters.attack > 0.0f ? static_cast<float> (1.0f / (parameters.attack * sr)) : -1.0f);
decayRate = (parameters.decay > 0.0f ? static_cast<float> ((1.0f - sustainLevel) / (parameters.decay * sr)) : -1.0f);
if ((state == State::attack && attackRate <= 0.0f)
|| (state == State::decay && (decayRate <= 0.0f || envelopeVal <= parameters.sustain))
|| (state == State::release && releaseRate <= 0.0f))
{
goToNextState();
}
}
void checkCurrentState()
void goToNextState() noexcept
{
if (currentState == State::attack && attackRate <= 0.0f) currentState = decayRate > 0.0f ? State::decay : State::sustain;
else if (currentState == State::decay && decayRate <= 0.0f) currentState = State::sustain;
else if (currentState == State::release && releaseRate <= 0.0f) reset();
if (state == State::attack)
{
state = (decayRate > 0.0f ? State::decay : State::sustain);
return;
}
if (state == State::decay)
{
state = State::sustain;
return;
}
if (state == State::release)
reset();
}
//==============================================================================
enum class State { idle, attack, decay, sustain, release };
State currentState = State::idle;
Parameters currentParameters;
State state = State::idle;
Parameters parameters;
double sr = 0.0;
float envelopeVal = 0.0f, sustainLevel = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f;
double sampleRate = 44100.0;
float envelopeVal = 0.0f, attackRate = 0.0f, decayRate = 0.0f, releaseRate = 0.0f;
};
} // namespace juce

+ 257
- 0
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_ADSR_test.cpp View File

@@ -0,0 +1,257 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct ADSRTests : public UnitTest
{
ADSRTests() : UnitTest ("ADSR", UnitTestCategories::audio) {}
void runTest() override
{
constexpr double sampleRate = 44100.0;
const ADSR::Parameters parameters { 0.1f, 0.1f, 0.5f, 0.1f };
ADSR adsr;
adsr.setSampleRate (sampleRate);
adsr.setParameters (parameters);
beginTest ("Idle");
{
adsr.reset();
expect (! adsr.isActive());
expectEquals (adsr.getNextSample(), 0.0f);
}
beginTest ("Attack");
{
adsr.reset();
adsr.noteOn();
expect (adsr.isActive());
auto buffer = getTestBuffer (sampleRate, parameters.attack);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isIncreasing (buffer));
}
beginTest ("Decay");
{
adsr.reset();
adsr.noteOn();
advanceADSR (adsr, roundToInt (parameters.attack * sampleRate));
auto buffer = getTestBuffer (sampleRate, parameters.decay);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isDecreasing (buffer));
}
beginTest ("Sustain");
{
adsr.reset();
adsr.noteOn();
advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay + 0.01) * sampleRate));
auto random = getRandom();
for (int numTests = 0; numTests < 100; ++numTests)
{
const auto sustainLevel = random.nextFloat();
const auto sustainLength = jmax (0.1f, random.nextFloat());
adsr.setParameters ({ parameters.attack, parameters.decay, sustainLevel, parameters.release });
auto buffer = getTestBuffer (sampleRate, sustainLength);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isSustained (buffer, sustainLevel));
}
}
beginTest ("Release");
{
adsr.reset();
adsr.noteOn();
advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay) * sampleRate));
adsr.noteOff();
auto buffer = getTestBuffer (sampleRate, parameters.release);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isDecreasing (buffer));
}
beginTest ("Zero-length attack jumps to decay");
{
adsr.reset();
adsr.setParameters ({ 0.0f, parameters.decay, parameters.sustain, parameters.release });
adsr.noteOn();
auto buffer = getTestBuffer (sampleRate, parameters.decay);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isDecreasing (buffer));
}
beginTest ("Zero-length decay jumps to sustain");
{
adsr.reset();
adsr.setParameters ({ parameters.attack, 0.0f, parameters.sustain, parameters.release });
adsr.noteOn();
advanceADSR (adsr, roundToInt (parameters.attack * sampleRate));
adsr.getNextSample();
expectEquals (adsr.getNextSample(), parameters.sustain);
auto buffer = getTestBuffer (sampleRate, 1);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isSustained (buffer, parameters.sustain));
}
beginTest ("Zero-length attack and decay jumps to sustain");
{
adsr.reset();
adsr.setParameters ({ 0.0f, 0.0f, parameters.sustain, parameters.release });
adsr.noteOn();
expectEquals (adsr.getNextSample(), parameters.sustain);
auto buffer = getTestBuffer (sampleRate, 1);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isSustained (buffer, parameters.sustain));
}
beginTest ("Zero-length attack and decay releases correctly");
{
adsr.reset();
adsr.setParameters ({ 0.0f, 0.0f, parameters.sustain, parameters.release });
adsr.noteOn();
adsr.noteOff();
auto buffer = getTestBuffer (sampleRate, parameters.release);
adsr.applyEnvelopeToBuffer (buffer, 0, buffer.getNumSamples());
expect (isDecreasing (buffer));
}
beginTest ("Zero-length release resets to idle");
{
adsr.reset();
adsr.setParameters ({ parameters.attack, parameters.decay, parameters.sustain, 0.0f });
adsr.noteOn();
advanceADSR (adsr, roundToInt ((parameters.attack + parameters.decay) * sampleRate));
adsr.noteOff();
expect (! adsr.isActive());
}
}
static void advanceADSR (ADSR& adsr, int numSamplesToAdvance)
{
while (--numSamplesToAdvance >= 0)
adsr.getNextSample();
}
static AudioBuffer<float> getTestBuffer (double sampleRate, float lengthInSeconds)
{
AudioBuffer<float> buffer { 2, roundToInt (lengthInSeconds * sampleRate) };
for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
buffer.setSample (channel, sample, 1.0f);
return buffer;
}
static bool isIncreasing (const AudioBuffer<float>& b)
{
jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0);
for (int channel = 0; channel < b.getNumChannels(); ++channel)
{
float previousSample = -1.0f;
for (int sample = 0; sample < b.getNumSamples(); ++sample)
{
const auto currentSample = b.getSample (channel, sample);
if (currentSample <= previousSample)
return false;
previousSample = currentSample;
}
}
return true;
}
static bool isDecreasing (const AudioBuffer<float>& b)
{
jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0);
for (int channel = 0; channel < b.getNumChannels(); ++channel)
{
float previousSample = std::numeric_limits<float>::max();
for (int sample = 0; sample < b.getNumSamples(); ++sample)
{
const auto currentSample = b.getSample (channel, sample);
if (currentSample >= previousSample)
return false;
previousSample = currentSample;
}
}
return true;
}
static bool isSustained (const AudioBuffer<float>& b, float sustainLevel)
{
jassert (b.getNumChannels() > 0 && b.getNumSamples() > 0);
for (int channel = 0; channel < b.getNumChannels(); ++channel)
if (b.findMinMax (channel, 0, b.getNumSamples()) != Range<float> { sustainLevel, sustainLevel })
return false;
return true;
}
};
static ADSRTests adsrTests;
} // namespace juce

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

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 7
- 10
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_GenericInterpolator.h View File

@@ -2,19 +2,16 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
@@ -415,7 +412,7 @@ private:
pos -= 1.0;
}
*output++ += gain * InterpolatorTraits::valueAtOffset ((float) pos);
*output++ += gain * InterpolatorTraits::valueAtOffset (lastInputSamples, (float) pos, indexBuffer);
pos += speedRatio;
}
}


+ 23
- 19
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_IIRFilter.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -258,42 +258,42 @@ IIRCoefficients IIRCoefficients::makePeakFilter (double sampleRate,
}
//==============================================================================
IIRFilter::IIRFilter() noexcept
{
}
template <typename Mutex>
IIRFilterBase<Mutex>::IIRFilterBase() noexcept = default;
IIRFilter::IIRFilter (const IIRFilter& other) noexcept : active (other.active)
template <typename Mutex>
IIRFilterBase<Mutex>::IIRFilterBase (const IIRFilterBase& other) noexcept : active (other.active)
{
const SpinLock::ScopedLockType sl (other.processLock);
const typename Mutex::ScopedLockType sl (other.processLock);
coefficients = other.coefficients;
}
IIRFilter::~IIRFilter() noexcept
{
}
//==============================================================================
void IIRFilter::makeInactive() noexcept
template <typename Mutex>
void IIRFilterBase<Mutex>::makeInactive() noexcept
{
const SpinLock::ScopedLockType sl (processLock);
const typename Mutex::ScopedLockType sl (processLock);
active = false;
}
void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept
template <typename Mutex>
void IIRFilterBase<Mutex>::setCoefficients (const IIRCoefficients& newCoefficients) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
const typename Mutex::ScopedLockType sl (processLock);
coefficients = newCoefficients;
active = true;
}
//==============================================================================
void IIRFilter::reset() noexcept
template <typename Mutex>
void IIRFilterBase<Mutex>::reset() noexcept
{
const SpinLock::ScopedLockType sl (processLock);
const typename Mutex::ScopedLockType sl (processLock);
v1 = v2 = 0.0;
}
float IIRFilter::processSingleSampleRaw (float in) noexcept
template <typename Mutex>
float IIRFilterBase<Mutex>::processSingleSampleRaw (float in) noexcept
{
auto out = coefficients.coefficients[0] * in + v1;
@@ -305,9 +305,10 @@ float IIRFilter::processSingleSampleRaw (float in) noexcept
return out;
}
void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept
template <typename Mutex>
void IIRFilterBase<Mutex>::processSamples (float* const samples, const int numSamples) noexcept
{
const SpinLock::ScopedLockType sl (processLock);
const typename Mutex::ScopedLockType sl (processLock);
if (active)
{
@@ -333,4 +334,7 @@ void IIRFilter::processSamples (float* const samples, const int numSamples) noex
}
}
template class IIRFilterBase<SpinLock>;
template class IIRFilterBase<DummyCriticalSection>;
} // namespace juce

+ 45
- 8
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_IIRFilter.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -153,7 +153,8 @@ public:
@tags{Audio}
*/
class JUCE_API IIRFilter
template <typename Mutex>
class JUCE_API IIRFilterBase
{
public:
//==============================================================================
@@ -163,13 +164,10 @@ public:
you process with it. Use the setCoefficients() method to turn it into the
type of filter needed.
*/
IIRFilter() noexcept;
IIRFilterBase() noexcept;
/** Creates a copy of another filter. */
IIRFilter (const IIRFilter&) noexcept;
/** Destructor. */
~IIRFilter() noexcept;
IIRFilterBase (const IIRFilterBase&) noexcept;
//==============================================================================
/** Clears the filter so that any incoming data passes through unchanged. */
@@ -202,7 +200,7 @@ public:
protected:
//==============================================================================
SpinLock processLock;
Mutex processLock;
IIRCoefficients coefficients;
float v1 = 0, v2 = 0;
bool active = false;
@@ -214,4 +212,43 @@ protected:
JUCE_LEAK_DETECTOR (IIRFilter)
};
/**
An IIR filter that can perform low, high, or band-pass filtering on an
audio signal, and which attempts to implement basic thread-safety.
This class synchronises calls to some of its member functions, making it
safe (although not necessarily real-time-safe) to reset the filter or
apply new coefficients while the filter is processing on another thread.
In most cases this style of internal locking should not be used, and you
should attempt to provide thread-safety at a higher level in your program.
If you can guarantee that calls to the filter will be synchronised externally,
you could consider switching to SingleThreadedIIRFilter instead.
@see SingleThreadedIIRFilter, IIRCoefficient, IIRFilterAudioSource
@tags{Audio}
*/
class IIRFilter : public IIRFilterBase<SpinLock>
{
public:
using IIRFilterBase::IIRFilterBase;
};
/**
An IIR filter that can perform low, high, or band-pass filtering on an
audio signal, with no thread-safety guarantees.
You should use this class if you need an IIR filter, and don't plan to
call its member functions from multiple threads at once.
@see IIRFilter, IIRCoefficient, IIRFilterAudioSource
@tags{Audio}
*/
class SingleThreadedIIRFilter : public IIRFilterBase<DummyCriticalSection>
{
public:
using IIRFilterBase::IIRFilterBase;
};
} // namespace juce

+ 6
- 9
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Interpolators.cpp View File

@@ -2,19 +2,16 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE


+ 6
- 9
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Interpolators.h View File

@@ -2,19 +2,16 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
End User License Agreement: www.juce.com/juce-6-licence
Privacy Policy: www.juce.com/juce-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. Permission
To use, copy, modify, and/or distribute this software for any purpose with or
without fee is hereby granted provided that the above copyright notice and
this permission notice appear in all copies.
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_LagrangeInterpolator.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 5
- 1
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_Reverb.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -131,6 +131,7 @@ public:
/** Applies the reverb to two stereo channels of audio data. */
void processStereo (float* const left, float* const right, const int numSamples) noexcept
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
jassert (left != nullptr && right != nullptr);
for (int i = 0; i < numSamples; ++i)
@@ -160,11 +161,13 @@ public:
left[i] = outL * wet1 + outR * wet2 + left[i] * dry;
right[i] = outR * wet1 + outL * wet2 + right[i] * dry;
}
JUCE_END_IGNORE_WARNINGS_MSVC
}
/** Applies the reverb to a single mono channel of audio data. */
void processMono (float* const samples, const int numSamples) noexcept
{
JUCE_BEGIN_IGNORE_WARNINGS_MSVC (6011)
jassert (samples != nullptr);
for (int i = 0; i < numSamples; ++i)
@@ -186,6 +189,7 @@ public:
samples[i] = output * wet1 + samples[i] * dry;
}
JUCE_END_IGNORE_WARNINGS_MSVC
}
private:


+ 1
- 1
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_SmoothedValue.cpp View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.


+ 9
- 10
libs/juce-current/source/modules/juce_audio_basics/utilities/juce_SmoothedValue.h View File

@@ -2,7 +2,7 @@
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2020 - Raw Material Software Limited
Copyright (c) 2022 - Raw Material Software Limited
JUCE is an open source library subject to commercial or open-source
licensing.
@@ -58,8 +58,6 @@ public:
/** Constructor. */
SmoothedValueBase() = default;
virtual ~SmoothedValueBase() {}
//==============================================================================
/** Returns true if the current value is currently being interpolated. */
bool isSmoothing() const noexcept { return countdown > 0; }
@@ -330,9 +328,8 @@ public:
}
//==============================================================================
/** THIS FUNCTION IS DEPRECATED.
Use `setTargetValue (float)` and `setCurrentAndTargetValue()` instead:
#ifndef DOXYGEN
/** Using the new methods:
lsv.setValue (x, false); -> lsv.setTargetValue (x);
lsv.setValue (x, true); -> lsv.setCurrentAndTargetValue (x);
@@ -340,7 +337,8 @@ public:
@param newValue The new target value
@param force If true, the value will be set immediately, bypassing the ramp
*/
JUCE_DEPRECATED_WITH_BODY (void setValue (FloatType newValue, bool force = false) noexcept,
[[deprecated ("Use setTargetValue and setCurrentAndTargetValue instead.")]]
void setValue (FloatType newValue, bool force = false) noexcept
{
if (force)
{
@@ -349,7 +347,8 @@ public:
}
setTargetValue (newValue);
})
}
#endif
private:
//==============================================================================
@@ -510,7 +509,7 @@ public:
expect (referenceData.getSample (0, 10) < sv.getTargetValue());
expectWithinAbsoluteError (referenceData.getSample (0, 11),
sv.getTargetValue(),
1.0e-7f);
2.0e-7f);
auto getUnitData = [] (int numSamplesToGenerate)
{
@@ -528,7 +527,7 @@ public:
for (int i = 0; i < test.getNumSamples(); ++i)
expectWithinAbsoluteError (test.getSample (0, i),
reference.getSample (0, i),
1.0e-7f);
2.0e-7f);
};
auto testData = getUnitData (numSamples);


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

Loading…
Cancel
Save