Browse Source

Update to latest juce

tags/1.9.7
falkTX 8 years ago
parent
commit
f9387f9a34
100 changed files with 7933 additions and 2411 deletions
  1. +2
    -2
      data/copy-juce-carla
  2. +1
    -0
      source/modules/AppConfig.h
  3. +336
    -0
      source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp
  4. +361
    -0
      source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h
  5. +2
    -2
      source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h
  6. +27
    -11
      source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
  7. +2
    -2
      source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h
  8. +0
    -1
      source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.cpp
  9. +0
    -1
      source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.h
  10. +117
    -16
      source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp
  11. +39
    -0
      source/modules/juce_audio_basics/effects/juce_IIRFilter.h
  12. +0
    -1
      source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h
  13. +14
    -4
      source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h
  14. +10
    -0
      source/modules/juce_audio_basics/juce_audio_basics.cpp
  15. +28
    -2
      source/modules/juce_audio_basics/juce_audio_basics.h
  16. +1
    -1
      source/modules/juce_audio_basics/midi/juce_MidiFile.cpp
  17. +99
    -67
      source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
  18. +27
    -13
      source/modules/juce_audio_basics/midi/juce_MidiMessage.h
  19. +1
    -1
      source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp
  20. +13
    -1
      source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h
  21. +1
    -2
      source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp
  22. +184
    -174
      source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp
  23. +1
    -30
      source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h
  24. +0
    -1
      source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp
  25. +10
    -5
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp
  26. +9
    -3
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h
  27. +14
    -6
      source/modules/juce_audio_basics/mpe/juce_MPEZone.h
  28. +1
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp
  29. +3
    -2
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h
  30. +50
    -5
      source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp
  31. +21
    -11
      source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h
  32. +1
    -1
      source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h
  33. +17
    -5
      source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
  34. +8
    -1
      source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h
  35. +72
    -226
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  36. +31
    -67
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h
  37. +7
    -32
      source/modules/juce_audio_devices/juce_audio_devices.cpp
  38. +30
    -20
      source/modules/juce_audio_devices/juce_audio_devices.h
  39. +10
    -3
      source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h
  40. +1
    -1
      source/modules/juce_audio_devices/native/juce_android_Midi.cpp
  41. +2
    -2
      source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  42. +48
    -5
      source/modules/juce_audio_devices/native/juce_ios_Audio.cpp
  43. +3
    -1
      source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
  44. +93
    -77
      source/modules/juce_audio_devices/native/juce_linux_Midi.cpp
  45. +43
    -18
      source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  46. +12
    -0
      source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp
  47. +4
    -3
      source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp
  48. +7
    -0
      source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
  49. +1
    -1
      source/modules/juce_audio_devices/sources/juce_AudioTransportSource.h
  50. +8
    -5
      source/modules/juce_audio_formats/codecs/flac/alloc.h
  51. +4
    -1
      source/modules/juce_audio_formats/codecs/flac/assert.h
  52. +4
    -1
      source/modules/juce_audio_formats/codecs/flac/callback.h
  53. +0
    -33
      source/modules/juce_audio_formats/codecs/flac/compat.h
  54. +3
    -1
      source/modules/juce_audio_formats/codecs/flac/endswap.h
  55. +0
    -1
      source/modules/juce_audio_formats/codecs/flac/metadata.h
  56. +0
    -1
      source/modules/juce_audio_formats/codecs/flac/stream_decoder.h
  57. +0
    -1
      source/modules/juce_audio_formats/codecs/flac/stream_encoder.h
  58. +0
    -5
      source/modules/juce_audio_formats/codecs/flac/win_utf8_io.h
  59. +6
    -8
      source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
  60. +68
    -3
      source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
  61. +1
    -1
      source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp
  62. +7
    -3
      source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
  63. +4
    -1
      source/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp
  64. +2
    -0
      source/modules/juce_audio_formats/juce_audio_formats.cpp
  65. +27
    -1
      source/modules/juce_audio_formats/juce_audio_formats.h
  66. +183
    -0
      source/modules/juce_audio_processors/format/juce_AudioPluginFormat.cpp
  67. +65
    -7
      source/modules/juce_audio_processors/format/juce_AudioPluginFormat.h
  68. +86
    -10
      source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp
  69. +42
    -0
      source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.h
  70. +778
    -0
      source/modules/juce_audio_processors/format_types/juce_AU_Shared.h
  71. +14
    -4
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h
  72. +882
    -221
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
  73. +17
    -4
      source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp
  74. +9
    -3
      source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.h
  75. +72
    -34
      source/modules/juce_audio_processors/format_types/juce_VST3Common.h
  76. +9
    -1
      source/modules/juce_audio_processors/format_types/juce_VST3Headers.h
  77. +625
    -284
      source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
  78. +16
    -20
      source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h
  79. +238
    -0
      source/modules/juce_audio_processors/format_types/juce_VSTCommon.h
  80. +458
    -0
      source/modules/juce_audio_processors/format_types/juce_VSTInterface.h
  81. +39
    -38
      source/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h
  82. +619
    -542
      source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
  83. +24
    -11
      source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.h
  84. +27
    -13
      source/modules/juce_audio_processors/juce_audio_processors.cpp
  85. +34
    -7
      source/modules/juce_audio_processors/juce_audio_processors.h
  86. +3
    -0
      source/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h
  87. +773
    -157
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
  88. +628
    -83
      source/modules/juce_audio_processors/processors/juce_AudioProcessor.h
  89. +129
    -2
      source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp
  90. +85
    -1
      source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h
  91. +48
    -24
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp
  92. +8
    -0
      source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h
  93. +10
    -0
      source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp
  94. +1
    -1
      source/modules/juce_audio_processors/processors/juce_PluginDescription.h
  95. +69
    -27
      source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp
  96. +3
    -2
      source/modules/juce_audio_processors/scanning/juce_KnownPluginList.h
  97. +5
    -3
      source/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp
  98. +11
    -7
      source/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h
  99. +18
    -9
      source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp
  100. +7
    -3
      source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h

+ 2
- 2
data/copy-juce-carla View File

@@ -2,8 +2,8 @@

set -e

JUCE_MODULES_DIR="/home/falktx/Projects/FOSS/GIT-mine/DISTRHO-Ports/libs/juce/source/modules/"
CARLA_MODULES_DIR="/home/falktx/Projects/FOSS/GIT-mine/Carla/source/modules/"
JUCE_MODULES_DIR="/Shared/Personal/FOSS/GIT/DISTRHO/DISTRHO-Ports/libs/juce/source/modules/"
CARLA_MODULES_DIR="/home/falktx/FOSS/GIT-mine/falkTX/Carla/source/modules/"

MODULES=("juce_audio_basics juce_audio_devices juce_audio_formats juce_audio_processors juce_core juce_data_structures juce_events juce_graphics juce_gui_basics juce_gui_extra")



+ 1
- 0
source/modules/AppConfig.h View File

@@ -60,6 +60,7 @@
// misc
#define JUCE_DISABLE_JUCE_VERSION_PRINTING 1
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1
#define JUCE_STANDALONE_APPLICATION 0
#define JUCE_STRING_UTF_TYPE 8
#define JUCE_USE_VFORK 1


+ 336
- 0
source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp View File

@@ -0,0 +1,336 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
AudioChannelSet::AudioChannelSet (uint32 c) : channels (c) {}
bool AudioChannelSet::operator== (const AudioChannelSet& other) const noexcept { return channels == other.channels; }
bool AudioChannelSet::operator!= (const AudioChannelSet& other) const noexcept { return channels != other.channels; }
bool AudioChannelSet::operator< (const AudioChannelSet& other) const noexcept { return channels < other.channels; }
String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type)
{
if (type >= discreteChannel0)
return String ("Discrete ") + String (type - discreteChannel0 + 1);
switch (type)
{
case left: return NEEDS_TRANS("Left");
case right: return NEEDS_TRANS("Right");
case centre: return NEEDS_TRANS("Centre");
case LFE: return NEEDS_TRANS("LFE");
case leftSurround: return NEEDS_TRANS("Left Surround");
case rightSurround: return NEEDS_TRANS("Right Surround");
case leftCentre: return NEEDS_TRANS("Left Centre");
case rightCentre: return NEEDS_TRANS("Right Centre");
case centreSurround: return NEEDS_TRANS("Centre Surround");
case leftSurroundRear: return NEEDS_TRANS("Left Surround Rear");
case rightSurroundRear: return NEEDS_TRANS("Right Surround Rear");
case topMiddle: return NEEDS_TRANS("Top Middle");
case topFrontLeft: return NEEDS_TRANS("Top Front Left");
case topFrontCentre: return NEEDS_TRANS("Top Front Centre");
case topFrontRight: return NEEDS_TRANS("Top Front Right");
case topRearLeft: return NEEDS_TRANS("Top Rear Left");
case topRearCentre: return NEEDS_TRANS("Top Rear Centre");
case topRearRight: return NEEDS_TRANS("Top Rear Right");
case wideLeft: return NEEDS_TRANS("Wide Left");
case wideRight: return NEEDS_TRANS("Wide Right");
case LFE2: return NEEDS_TRANS("LFE 2");
case leftSurroundSide: return NEEDS_TRANS ("Left Surround Side");
case rightSurroundSide: return NEEDS_TRANS ("Right Surround Side");
case ambisonicW: return NEEDS_TRANS("Ambisonic W");
case ambisonicX: return NEEDS_TRANS("Ambisonic X");
case ambisonicY: return NEEDS_TRANS("Ambisonic Y");
case ambisonicZ: return NEEDS_TRANS("Ambisonic Z");
default: break;
}
return "Unknown";
}
String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type)
{
if (type >= discreteChannel0)
return String (type - discreteChannel0 + 1);
switch (type)
{
case left: return "L";
case right: return "R";
case centre: return "C";
case LFE: return "Lfe";
case leftSurround: return "Ls";
case rightSurround: return "Rs";
case leftCentre: return "Lc";
case rightCentre: return "Rc";
case centreSurround: return "Cs";
case leftSurroundRear: return "Lrs";
case rightSurroundRear: return "Rrs";
case topMiddle: return "Tm";
case topFrontLeft: return "Tfl";
case topFrontCentre: return "Tfc";
case topFrontRight: return "Tfr";
case topRearLeft: return "Trl";
case topRearCentre: return "Trc";
case topRearRight: return "Trr";
case wideLeft: return "Wl";
case wideRight: return "Wr";
case LFE2: return "Lfe2";
case leftSurroundSide: return "Lss";
case rightSurroundSide: return "Rss";
case ambisonicW: return "W";
case ambisonicX: return "X";
case ambisonicY: return "Y";
case ambisonicZ: return "Z";
default: break;
}
return "";
}
String AudioChannelSet::getSpeakerArrangementAsString() const
{
StringArray speakerTypes;
Array<AudioChannelSet::ChannelType> speakers = getChannelTypes();
for (int i = 0; i < speakers.size(); ++i)
{
String name = getAbbreviatedChannelTypeName (speakers.getReference (i));
if (name.isNotEmpty())
speakerTypes.add (name);
}
return speakerTypes.joinIntoString (" ");
}
String AudioChannelSet::getDescription() const
{
if (isDiscreteLayout()) return String ("Discrete #") + String (size());
if (*this == disabled()) return "Disabled";
if (*this == mono()) return "Mono";
if (*this == stereo()) return "Stereo";
if (*this == createLCR()) return "LCR";
if (*this == createLRS()) return "LRS";
if (*this == createLCRS()) return "LCRS";
if (*this == create5point0()) return "5.1 Surround";
if (*this == create5point1()) return "5.1 Surround (+Lfe)";
if (*this == create6point0()) return "6.1 Surround";
if (*this == create6point1()) return "6.1 Surround (+Lfe)";
if (*this == create6point0Music()) return "6.1 (Music) Surround";
if (*this == create6point1Music()) return "6.1 (Music) Surround (+Lfe)";
if (*this == create7point0()) return "7.1 Surround";
if (*this == create7point1()) return "7.1 Surround (Lfe)";
if (*this == create7point0SDDS()) return "7.1 Surround SDDS";
if (*this == create7point1SDDS()) return "7.1 Surround SDDS (+Lfe)";
if (*this == quadraphonic()) return "Quadraphonic";
if (*this == pentagonal()) return "Pentagonal";
if (*this == hexagonal()) return "Hexagonal";
if (*this == octagonal()) return "Octagonal";
if (*this == ambisonic()) return "Ambisonic";
return "Unknown";
}
bool AudioChannelSet::isDiscreteLayout() const noexcept
{
Array<AudioChannelSet::ChannelType> speakers = getChannelTypes();
for (int i = 0; i < speakers.size(); ++i)
if (speakers.getReference (i) > ambisonicZ)
return true;
return false;
}
int AudioChannelSet::size() const noexcept
{
return channels.countNumberOfSetBits();
}
AudioChannelSet::ChannelType AudioChannelSet::getTypeOfChannel (int index) const noexcept
{
int bit = channels.findNextSetBit(0);
for (int i = 0; i < index && bit >= 0; ++i)
bit = channels.findNextSetBit (bit + 1);
return static_cast<ChannelType> (bit);
}
int AudioChannelSet::getChannelIndexForType (AudioChannelSet::ChannelType type) const noexcept
{
int idx = 0;
for (int bit = channels.findNextSetBit (0); bit >= 0; bit = channels.findNextSetBit (bit + 1))
{
if (static_cast<ChannelType> (bit) == type)
return idx;
idx++;
}
return -1;
}
Array<AudioChannelSet::ChannelType> AudioChannelSet::getChannelTypes() const
{
Array<ChannelType> result;
for (int bit = channels.findNextSetBit(0); bit >= 0; bit = channels.findNextSetBit (bit + 1))
result.add (static_cast<ChannelType> (bit));
return result;
}
void AudioChannelSet::addChannel (ChannelType newChannel)
{
const int bit = static_cast<int> (newChannel);
jassert (bit >= 0 && bit < 1024);
channels.setBit (bit);
}
void AudioChannelSet::removeChannel (ChannelType newChannel)
{
const int bit = static_cast<int> (newChannel);
jassert (bit >= 0 && bit < 1024);
channels.clearBit (bit);
}
AudioChannelSet AudioChannelSet::disabled() { return AudioChannelSet(); }
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 << leftSurround) | (1u << rightSurround) | (1u << LFE)); }
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 << leftSurround) | (1u << rightSurround) | (1u << centreSurround) | (1u << LFE)); }
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 << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << LFE)); }
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 << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << LFE)); }
AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre) | (1u << LFE)); }
AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); }
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 << leftSurroundRear) | (1u << rightSurroundRear) | (1u << centre) | (1u << centreSurround)); }
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << centre) | (1u << centreSurround) | (1u << wideLeft) | (1u << wideRight)); }
AudioChannelSet AudioChannelSet::discreteChannels (int numChannels)
{
AudioChannelSet s;
s.channels.setRange (discreteChannel0, numChannels, true);
return s;
}
AudioChannelSet AudioChannelSet::canonicalChannelSet (int numChannels)
{
if (numChannels == 1) return AudioChannelSet::mono();
if (numChannels == 2) return AudioChannelSet::stereo();
if (numChannels == 3) return AudioChannelSet::createLCR();
if (numChannels == 4) return AudioChannelSet::quadraphonic();
if (numChannels == 5) return AudioChannelSet::create5point0();
if (numChannels == 6) return AudioChannelSet::create5point1();
if (numChannels == 7) return AudioChannelSet::create7point0();
if (numChannels == 8) return AudioChannelSet::create7point1();
return discreteChannels (numChannels);
}
AudioChannelSet AudioChannelSet::namedChannelSet (int numChannels)
{
if (numChannels == 1) return AudioChannelSet::mono();
if (numChannels == 2) return AudioChannelSet::stereo();
if (numChannels == 3) return AudioChannelSet::createLCR();
if (numChannels == 4) return AudioChannelSet::quadraphonic();
if (numChannels == 5) return AudioChannelSet::create5point0();
if (numChannels == 6) return AudioChannelSet::create5point1();
if (numChannels == 7) return AudioChannelSet::create7point0();
if (numChannels == 8) return AudioChannelSet::create7point1();
return AudioChannelSet();
}
Array<AudioChannelSet> AudioChannelSet::channelSetsWithNumberOfChannels (int numChannels)
{
Array<AudioChannelSet> retval;
if (numChannels != 0)
{
retval.add (AudioChannelSet::discreteChannels (numChannels));
if (numChannels == 1)
{
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());
retval.add (AudioChannelSet::ambisonic());
}
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());
}
}
return retval;
}

+ 361
- 0
source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h View File

@@ -0,0 +1,361 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_AUDIOCHANNELSET_H_INCLUDED
#define JUCE_AUDIOCHANNELSET_H_INCLUDED
//==============================================================================
/**
Represents a set of audio channel types.
For example, you might have a set of left + right channels, which is a stereo
channel set. It is a collection of values from the AudioChannelSet::ChannelType
enum, where each type may only occur once within the set.
The documentation below lists which AudioChannelSet corresponds to which native
layouts used by AAX, VST2/VST3 and CoreAudio/AU. The layout tags in CoreAudio
are particularly confusing. For example, the layout which is labeled as "7.1 SDDS"
in Logic Pro, corresponds to CoreAudio/AU's kAudioChannelLayoutTag_DTS_7_0 tag, whereas
AAX's DTS 7.1 Layout corresponds to CoreAudio/AU's
kAudioChannelLayoutTag_MPEG_7_1_A format, etc. Please do not use the CoreAudio tag
as an indication to the actual layout of the speakers.
@see Bus
*/
class JUCE_API AudioChannelSet
{
public:
/** Creates an empty channel set.
You can call addChannel to add channels to the set.
*/
AudioChannelSet() noexcept {}
/** Creates a zero-channel set which can be used to indicate that a
bus is disabled. */
static AudioChannelSet disabled();
//==============================================================================
/** Creates a one-channel mono set (centre).
Is equivalent to: kMonoAAX (VST), AAX_eStemFormat_Mono (AAX), kAudioChannelLayoutTag_Mono (CoreAudio)
*/
static AudioChannelSet mono();
/** Creates a set containing a stereo set (left, right).
Is equivalent to: kStereo (VST), AAX_eStemFormat_Stereo (AAX), kAudioChannelLayoutTag_Stereo (CoreAudio)
*/
static AudioChannelSet stereo();
//==============================================================================
/** Creates a set containing an LCR set (left, right, centre).
Is equivalent to: k30Cine (VST), AAX_eStemFormat_LCR (AAX), kAudioChannelLayoutTag_MPEG_3_0_A (CoreAudio)
This format is referred to as "LRC" in Cubase.
This format is referred to as "LCR" in Pro Tools.
*/
static AudioChannelSet createLCR();
/** Creates a set containing an LRS set (left, right, surround).
Is equivalent to: k30Music (VST), n/a (AAX), kAudioChannelLayoutTag_ITU_2_1 (CoreAudio)
This format is referred to as "LRS" in Cubase.
*/
static AudioChannelSet createLRS();
/** Creates a set containing an LCRS set (left, right, centre, surround).
Is equivalent to: k40Cine (VST), AAX_eStemFormat_LCRS (AAX), kAudioChannelLayoutTag_MPEG_4_0_A (CoreAudio)
This format is referred to as "LCRS (Pro Logic)" in Logic Pro.
This format is referred to as "LRCS" in Cubase.
This format is referred to as "LCRS" in Pro Tools.
*/
static AudioChannelSet createLCRS();
//==============================================================================
/** Creates a set for a 5.0 surround setup (left, right, centre, leftSurround, rightSurround).
Is equivalent to: k50 (VST), AAX_eStemFormat_5_0 (AAX), kAudioChannelLayoutTag_MPEG_5_0_A (CoreAudio)
This format is referred to as "5.0" in Cubase.
This format is referred to as "5.0" in Pro Tools.
*/
static AudioChannelSet create5point0();
/** Creates a set for a 5.1 surround setup (left, right, centre, leftSurround, rightSurround, LFE).
Is equivalent to: k51 (VST), AAX_eStemFormat_5_1 (AAX), kAudioChannelLayoutTag_MPEG_5_1_A (CoreAudio)
This format is referred to as "5.1 (ITU 775)" in Logic Pro.
This format is referred to as "5.1" in Cubase.
This format is referred to as "5.1" in Pro Tools.
*/
static AudioChannelSet create5point1();
/** Creates a set for a 6.0 Cine surround setup (left, right, centre, leftSurround, rightSurround, centreSurround).
Is equivalent to: k60Cine (VST), AAX_eStemFormat_6_0 (AAX), kAudioChannelLayoutTag_AudioUnit_6_0 (CoreAudio)
Logic Pro incorrectly uses this for the surround format labeled "6.1 (ES/EX)".
This format is referred to as "6.0 Cine" in Cubase.
This format is referred to as "6.0" in Pro Tools.
*/
static AudioChannelSet create6point0();
/** Creates a set for a 6.1 Cine surround setup (left, right, centre, leftSurround, rightSurround, centreSurround, LFE).
Is equivalent to: k61Cine (VST), AAX_eStemFormat_6_1 (AAX), kAudioChannelLayoutTag_MPEG_6_1_A (CoreAudio)
This format is referred to as "6.1" in Pro Tools.
*/
static AudioChannelSet create6point1();
/** Creates a set for a 6.0 Music surround setup (left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide).
Is equivalent to: k60Music (VST), n/a (AAX), kAudioChannelLayoutTag_DTS_6_0_A (CoreAudio)
This format is referred to as "6.0 Music" in Cubase.
*/
static AudioChannelSet create6point0Music();
/** Creates a set for a 6.0 Music surround setup (left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide, LFE).
Is equivalent to: k61Music (VST), n/a (AAX), kAudioChannelLayoutTag_DTS_6_1_A (CoreAudio)
*/
static AudioChannelSet create6point1Music();
/** Creates a set for a DTS 7.0 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear).
Is equivalent to: k70Music (VST), AAX_eStemFormat_7_0_DTS (AAX), kAudioChannelLayoutTag_AudioUnit_7_0 (CoreAudio)
This format is referred to as "7.0" in Pro Tools.
*/
static AudioChannelSet create7point0();
/** Creates a set for a SDDS 7.0 surround setup (left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre).
Is equivalent to: k70Cine (VST), AAX_eStemFormat_7_0_SDDS (AAX), kAudioChannelLayoutTag_AudioUnit_7_0_Front (CoreAudio)
This format is referred to as "7.0 SDDS" in Pro Tools.
*/
static AudioChannelSet create7point0SDDS();
/** Creates a set for a DTS 7.1 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE).
Is equivalent to: k71CineSideFill (VST), AAX_eStemFormat_7_1_DTS (AAX), kAudioChannelLayoutTag_MPEG_7_1_C/kAudioChannelLayoutTag_ITU_3_4_1 (CoreAudio)
This format is referred to as "7.1 (3/4.1)" in Logic Pro.
This format is referred to as "7.1" in Pro Tools.
*/
static AudioChannelSet create7point1();
/** Creates a set for a 7.1 surround setup (left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre, LFE).
Is equivalent to: k71Cine (VST), AAX_eStemFormat_7_1_SDDS (AAX), kAudioChannelLayoutTag_MPEG_7_1_A (CoreAudio)
This format is referred to as "7.1 (SDDS)" in Logic Pro.
This format is referred to as "7.1 SDDS" in Pro Tools.
*/
static AudioChannelSet create7point1SDDS();
//==============================================================================
/** Creates a set for ambisonic surround setups (ambisonicW, ambisonicX, ambisonicY, ambisonicZ).
Is equivalent to: kBFormat (VST), n/a (AAX), kAudioChannelLayoutTag_Ambisonic_B_Format (CoreAudio)
*/
static AudioChannelSet ambisonic();
/** Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround)
Is equivalent to: k40Music (VST), AAX_eStemFormat_Quad (AAX), kAudioChannelLayoutTag_Quadraphonic (CoreAudio)
This format is referred to as "Quadraphonic" in Logic Pro.
This format is referred to as "Quadro" in Cubase.
This format is referred to as "Quad" in Pro Tools.
*/
static AudioChannelSet quadraphonic();
/** Creates a set for pentagonal surround setup (left, right, centre, leftSurroundRear, rightSurroundRear).
Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Pentagonal (CoreAudio)
*/
static AudioChannelSet pentagonal();
/** Creates a set for hexagonal surround setup (left, right, leftSurroundRear, rightSurroundRear, centre, surroundCentre).
Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Hexagonal (CoreAudio)
*/
static AudioChannelSet hexagonal();
/** Creates a set for octagonal surround setup (left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight).
Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Octagonal (CoreAudio)
*/
static AudioChannelSet octagonal();
//==============================================================================
/** Creates a set of untyped discrete channels. */
static AudioChannelSet discreteChannels (int numChannels);
/** Create a canonical channel set for a given number of channels.
For example, numChannels = 1 will return mono, numChannels = 2 will return stereo, etc. */
static AudioChannelSet canonicalChannelSet (int numChannels);
/** Create a channel set for a given number of channels which is non-discrete.
If numChannels is larger than the number of channels of the surround format
with the maximum amount of channels (currently 7.1 Surround), then this
function returns an empty set.*/
static AudioChannelSet namedChannelSet (int numChannels);
/** Return an array of channel sets which have a given number of channels */
static Array<AudioChannelSet> channelSetsWithNumberOfChannels (int numChannels);
//==============================================================================
/** Represents different audio channel types. */
enum ChannelType
{
unknown = 0,
left = 1, // L
right = 2, // R
centre = 3, // C (sometimes M for mono)
LFE = 4,
leftSurround = 5, // Ls
rightSurround = 6, // Rs
leftCentre = 7, // Lc (AAX/VST), Lc used as Lss in AU for most layouts
rightCentre = 8, // Rc (AAX/VST), Rc used as Rss in AU for most layouts
centreSurround = 9, // Cs/S
surround = centreSurround, // Cs/S
leftSurroundSide = 10, // Lss (AXX), Side Left "Sl" (VST), Left Centre "LC" (AU)
rightSurroundSide = 11, // Rss (AXX), Side right "Sr" (VST), Right Centre "Rc" (AU)
topMiddle = 12,
topFrontLeft = 13,
topFrontCentre = 14,
topFrontRight = 15,
topRearLeft = 16,
topRearCentre = 17,
topRearRight = 18,
LFE2 = 19,
leftSurroundRear = 20, // Lsr (AAX), Lcs (VST), Rls (AU)
rightSurroundRear = 21, // Rsr (AAX), Rcs (VST), Rrs (AU)
wideLeft = 22,
wideRight = 23,
ambisonicW = 24,
ambisonicX = 25,
ambisonicY = 26,
ambisonicZ = 27,
discreteChannel0 = 64 /**< Non-typed individual channels are indexed upwards from this value. */
};
/** Returns the name of a given channel type. For example, this method may return "Surround Left". */
static String getChannelTypeName (ChannelType);
/** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */
static String getAbbreviatedChannelTypeName (ChannelType);
//==============================================================================
enum
{
maxChannelsOfNamedLayout = 8
};
/** Adds a channel to the set. */
void addChannel (ChannelType newChannelType);
/** Removes a channel from the set. */
void removeChannel (ChannelType newChannelType);
/** Returns the number of channels in the set. */
int size() const noexcept;
/** Returns true if there are no channels in the set. */
bool isDisabled() const noexcept { return size() == 0; }
/** Returns an array of all the types in this channel set. */
Array<ChannelType> getChannelTypes() const;
/** Returns the type of one of the channels in the set, by index. */
ChannelType getTypeOfChannel (int channelIndex) const noexcept;
/** Returns the index for a particular channel-type.
Will return -1 if the this set does not contain a channel of this type. */
int getChannelIndexForType (ChannelType type) const noexcept;
/** Returns a string containing a whitespace-separated list of speaker types
corresponding to each channel. For example in a 5.1 arrangement,
the string may be "L R C Lfe Ls Rs". If the speaker arrangement is unknown,
the returned string will be empty.*/
String getSpeakerArrangementAsString() const;
/** Returns the description of the current layout. For example, this method may return
"Quadraphonic". Note that the returned string may not be unique. */
String getDescription() const;
/** Returns if this is a channel layout made-up of discrete channels. */
bool isDiscreteLayout() const noexcept;
/** Intersect two channel layouts. */
void intersect (const AudioChannelSet& other) { channels &= other.channels; }
//==============================================================================
bool operator== (const AudioChannelSet&) const noexcept;
bool operator!= (const AudioChannelSet&) const noexcept;
bool operator< (const AudioChannelSet&) const noexcept;
private:
BigInteger channels;
explicit AudioChannelSet (uint32);
};
#endif // JUCE_AUDIOCHANNELSET_H_INCLUDED

+ 2
- 2
source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h View File

@@ -397,9 +397,9 @@ public:
convert between 32 and 64 bit float buffer types.
*/
template <typename OtherType>
void makeCopyOf (const AudioBuffer<OtherType>& other)
void makeCopyOf (const AudioBuffer<OtherType>& other, bool avoidReallocating = false)
{
setSize (other.getNumChannels(), other.getNumSamples());
setSize (other.getNumChannels(), other.getNumSamples(), false, false, avoidReallocating);
if (other.hasBeenCleared())
{


+ 27
- 11
source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp View File

@@ -204,10 +204,11 @@ namespace FloatVectorHelpers
typedef float Type;
typedef float32x4_t ParallelType;
typedef uint32x4_t IntegerType;
union signMaskUnion { ParallelType f; IntegerType i; };
enum { numParallel = 4 };
static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; }
static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; }
static forcedinline IntegerType toint (ParallelType v) noexcept { signMaskUnion u; u.f = v; return u.i; }
static forcedinline ParallelType toflt (IntegerType v) noexcept { signMaskUnion u; u.i = v; return u.f; }
static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); }
static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); }
@@ -235,10 +236,11 @@ namespace FloatVectorHelpers
typedef double Type;
typedef double ParallelType;
typedef uint64 IntegerType;
union signMaskUnion { ParallelType f; IntegerType i; };
enum { numParallel = 1 };
static forcedinline IntegerType toint (ParallelType v) noexcept { union { ParallelType f; IntegerType i; } u; u.f = v; return u.i; }
static forcedinline ParallelType toflt (IntegerType v) noexcept { union { ParallelType f; IntegerType i; } u; u.i = v; return u.f; }
static forcedinline IntegerType toint (ParallelType v) noexcept { signMaskUnion u; u.f = v; return u.i; }
static forcedinline ParallelType toflt (IntegerType v) noexcept { signMaskUnion u; u.i = v; return u.f; }
static forcedinline ParallelType load1 (Type v) noexcept { return v; }
static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; }
@@ -346,6 +348,9 @@ namespace FloatVectorHelpers
#define JUCE_LOAD_SRC1_SRC2_DEST(src1Load, src2Load, dstLoad) const Mode::ParallelType d = dstLoad (dest), s1 = src1Load (src1), s2 = src2Load (src2);
#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src);
union signMask32 { float f; uint32 i; };
union signMask64 { double d; uint64 i; };
#if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON
template<int typeSize> struct ModeType { typedef BasicOps32 Mode; };
template<> struct ModeType<8> { typedef BasicOps64 Mode; };
@@ -481,6 +486,17 @@ namespace FloatVectorHelpers
#endif
}
//==============================================================================
namespace
{
#if JUCE_USE_VDSP_FRAMEWORK
// This casts away constness to account for slightly different vDSP function signatures
// in OSX 10.8 SDK and below. Can be safely removed once those SDKs are obsolete.
template <typename ValueType>
ValueType* osx108sdkCompatibilityCast (const ValueType* arg) noexcept { return const_cast<ValueType*> (arg); }
#endif
}
//==============================================================================
void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept
{
@@ -568,10 +584,10 @@ void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int
const Mode::ParallelType amountToAdd = Mode::load1 (amount);)
}
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float* src, float amount, int num) noexcept
void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, float amount, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsadd (src, 1, &amount, dest, 1, (vDSP_Length) num);
vDSP_vsadd (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
@@ -579,10 +595,10 @@ void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float* src, float am
#endif
}
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double* src, double amount, int num) noexcept
void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, double amount, int num) noexcept
{
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vsaddD (src, 1, &amount, dest, 1, (vDSP_Length) num);
vDSP_vsaddD (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num);
#else
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
@@ -795,7 +811,7 @@ void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcep
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num);
#else
union { float f; uint32 i; } signMask;
FloatVectorHelpers::signMask32 signMask;
signMask.i = 0x7fffffffUL;
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask),
JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST,
@@ -810,7 +826,7 @@ void FloatVectorOperations::abs (double* dest, const double* src, int num) noexc
#if JUCE_USE_VDSP_FRAMEWORK
vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num);
#else
union {double d; uint64 i;} signMask;
FloatVectorHelpers::signMask64 signMask;
signMask.i = 0x7fffffffffffffffULL;
JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabs (src[i]), Mode::bit_and (s, mask),
@@ -991,7 +1007,7 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab
void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport() noexcept
{
#if JUCE_USE_SSE_INTRINSICS
const int mxcsr = _mm_getcsr();
const unsigned int mxcsr = _mm_getcsr();
_mm_setcsr (mxcsr | 0x8040); // add the DAZ and FZ bits
#endif
}


+ 2
- 2
source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h View File

@@ -66,10 +66,10 @@ public:
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, float* src, float amount, int numValues) noexcept;
static void JUCE_CALLTYPE add (float* dest, const float* src, float amount, int numValues) noexcept;
/** Adds a fixed value to each source value and stores it in the destination array. */
static void JUCE_CALLTYPE add (double* dest, double* src, double amount, int numValues) noexcept;
static void JUCE_CALLTYPE add (double* dest, const double* src, double amount, int numValues) noexcept;
/** Adds the source values to the destination values. */
static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept;


+ 0
- 1
source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.cpp View File

@@ -22,7 +22,6 @@
==============================================================================
*/
struct CatmullRomAlgorithm
{
static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept


+ 0
- 1
source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.h View File

@@ -22,7 +22,6 @@
==============================================================================
*/
/**
Interpolator for resampling a stream of floats using Catmull-Rom interpolation.


+ 117
- 16
source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp View File

@@ -62,33 +62,131 @@ IIRCoefficients::IIRCoefficients (double c1, double c2, double c3,
IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate,
const double frequency) noexcept
{
jassert (sampleRate > 0);
return makeLowPass (sampleRate, frequency, 1.0 / std::sqrt (2.0));
}
IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate,
const double frequency,
const double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate);
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1,
c1 * 2.0,
c1,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
c1 * (1.0 - 1.0 / Q * n + nSquared));
}
IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate,
const double frequency) noexcept
{
return makeHighPass (sampleRate, frequency, 1.0 / std::sqrt(2.0));
}
IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate,
const double frequency,
const double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double n = std::tan (double_Pi * frequency / sampleRate);
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared);
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1,
c1 * -2.0,
c1,
1.0,
c1 * 2.0 * (nSquared - 1.0),
c1 * (1.0 - std::sqrt (2.0) * n + nSquared));
c1 * (1.0 - 1.0 / Q * n + nSquared));
}
IIRCoefficients IIRCoefficients::makeBandPass (const double sampleRate,
const double frequency) noexcept
{
return makeBandPass (sampleRate, frequency, 1.0 / std::sqrt (2.0));
}
IIRCoefficients IIRCoefficients::makeBandPass (const double sampleRate,
const double frequency,
const double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate);
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1 * n / Q,
0.0,
-c1 * n / Q,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - 1.0 / Q * n + nSquared));
}
IIRCoefficients IIRCoefficients::makeNotchFilter (const double sampleRate,
const double frequency) noexcept
{
return makeNotchFilter (sampleRate, frequency, 1.0 / std::sqrt (2.0));
}
IIRCoefficients IIRCoefficients::makeNotchFilter (const double sampleRate,
const double frequency,
const double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate);
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + n / Q + nSquared);
return IIRCoefficients (c1 * (1.0 + nSquared),
2.0 * c1 * (1.0 - nSquared),
c1 * (1.0 + nSquared),
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - n / Q + nSquared));
}
IIRCoefficients IIRCoefficients::makeAllPass (const double sampleRate,
const double frequency) noexcept
{
return makeAllPass (sampleRate, frequency, 1.0 / std::sqrt (2.0));
}
IIRCoefficients IIRCoefficients::makeAllPass (const double sampleRate,
const double frequency,
const double Q) noexcept
{
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate);
const double nSquared = n * n;
const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared);
return IIRCoefficients (c1 * (1.0 - n / Q + nSquared),
c1 * 2.0 * (1.0 - nSquared),
1.0,
1.0,
c1 * 2.0 * (1.0 - nSquared),
c1 * (1.0 - n / Q + nSquared));
}
IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate,
@@ -96,8 +194,9 @@ IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate,
const double Q,
const float gainFactor) noexcept
{
jassert (sampleRate > 0);
jassert (Q > 0);
jassert (sampleRate > 0.0);
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double A = jmax (0.0f, std::sqrt (gainFactor));
const double aminus1 = A - 1.0;
@@ -120,8 +219,9 @@ IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate,
const double Q,
const float gainFactor) noexcept
{
jassert (sampleRate > 0);
jassert (Q > 0);
jassert (sampleRate > 0.0);
jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double A = jmax (0.0f, std::sqrt (gainFactor));
const double aminus1 = A - 1.0;
@@ -140,15 +240,16 @@ IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate,
}
IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate,
const double centreFrequency,
const double frequency,
const double Q,
const float gainFactor) noexcept
{
jassert (sampleRate > 0);
jassert (Q > 0);
jassert (sampleRate > 0.0);
jassert (frequency > 0.0 && frequency <= sampleRate * 0.5);
jassert (Q > 0.0);
const double A = jmax (0.0f, std::sqrt (gainFactor));
const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate;
const double omega = (double_Pi * 2.0 * jmax (frequency, 2.0)) / sampleRate;
const double alpha = 0.5 * std::sin (omega) / Q;
const double c2 = -2.0 * std::cos (omega);
const double alphaTimesA = alpha * A;
@@ -164,12 +265,12 @@ IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate,
//==============================================================================
IIRFilter::IIRFilter() noexcept
: v1 (0), v2 (0), active (false)
: v1 (0.0), v2 (0.0), active (false)
{
}
IIRFilter::IIRFilter (const IIRFilter& other) noexcept
: v1 (0), v2 (0), active (other.active)
: v1 (0.0), v2 (0.0), active (other.active)
{
const SpinLock::ScopedLockType sl (other.processLock);
coefficients = other.coefficients;
@@ -198,7 +299,7 @@ void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcep
void IIRFilter::reset() noexcept
{
const SpinLock::ScopedLockType sl (processLock);
v1 = v2 = 0;
v1 = v2 = 0.0;
}
float IIRFilter::processSingleSampleRaw (const float in) noexcept


+ 39
- 0
source/modules/juce_audio_basics/effects/juce_IIRFilter.h View File

@@ -55,14 +55,53 @@ public:
/** Destructor. */
~IIRCoefficients() noexcept;
//==============================================================================
/** Returns the coefficients for a low-pass filter. */
static IIRCoefficients makeLowPass (double sampleRate,
double frequency) noexcept;
/** Returns the coefficients for a low-pass filter with variable Q. */
static IIRCoefficients makeLowPass (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for a high-pass filter. */
static IIRCoefficients makeHighPass (double sampleRate,
double frequency) noexcept;
/** Returns the coefficients for a high-pass filter with variable Q. */
static IIRCoefficients makeHighPass (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for a band-pass filter. */
static IIRCoefficients makeBandPass (double sampleRate, double frequency) noexcept;
/** Returns the coefficients for a band-pass filter with variable Q. */
static IIRCoefficients makeBandPass (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for a notch filter. */
static IIRCoefficients makeNotchFilter (double sampleRate, double frequency) noexcept;
/** Returns the coefficients for a notch filter with variable Q. */
static IIRCoefficients makeNotchFilter (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for an all-pass filter. */
static IIRCoefficients makeAllPass (double sampleRate, double frequency) noexcept;
/** Returns the coefficients for an all-pass filter with variable Q. */
static IIRCoefficients makeAllPass (double sampleRate,
double frequency,
double Q) noexcept;
//==============================================================================
/** Returns the coefficients for a low-pass shelf filter with variable Q and gain.


+ 0
- 1
source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h View File

@@ -22,7 +22,6 @@
==============================================================================
*/
/**
Interpolator for resampling a stream of floats using 4-point lagrange interpolation.


+ 14
- 4
source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h View File

@@ -33,8 +33,8 @@
*/
//==============================================================================
template<typename FloatType>
class JUCE_API LinearSmoothedValue
template <typename FloatType>
class LinearSmoothedValue
{
public:
/** Constructor. */
@@ -59,7 +59,6 @@ public:
countdown = 0;
}
//==============================================================================
/** Set a new target value. */
void setValue (FloatType newValue) noexcept
{
@@ -75,7 +74,6 @@ public:
}
}
//==============================================================================
/** Compute the next value. */
FloatType getNextValue() noexcept
{
@@ -87,6 +85,18 @@ public:
return currentValue;
}
/** Returns true if the current value is currently being interpolated. */
bool isSmoothing() const noexcept
{
return countdown > 0;
}
/** Returns the target value towards which the smoothed value is currently moving. */
FloatType getTargetValue() const noexcept
{
return target;
}
private:
//==============================================================================
FloatType currentValue, target, step;


+ 10
- 0
source/modules/juce_audio_basics/juce_audio_basics.cpp View File

@@ -31,6 +31,8 @@
#error "Incorrect use of JUCE cpp file"
#endif
#include "AppConfig.h"
#include "juce_audio_basics.h"
#if JUCE_MINGW && ! defined (__SSE2__)
@@ -67,6 +69,13 @@
#define JUCE_USE_ARM_NEON 1
#endif
#if TARGET_IPHONE_SIMULATOR
#ifdef JUCE_USE_ARM_NEON
#undef JUCE_USE_ARM_NEON
#endif
#define JUCE_USE_ARM_NEON 0
#endif
#if JUCE_USE_ARM_NEON
#include <arm_neon.h>
#endif
@@ -76,6 +85,7 @@ namespace juce
#include "buffers/juce_AudioDataConverters.cpp"
#include "buffers/juce_FloatVectorOperations.cpp"
#include "buffers/juce_AudioChannelSet.cpp"
#include "effects/juce_IIRFilter.cpp"
#include "effects/juce_IIRFilterOld.cpp"
#include "effects/juce_LagrangeInterpolator.cpp"


+ 28
- 2
source/modules/juce_audio_basics/juce_audio_basics.h View File

@@ -22,12 +22,37 @@
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.txt file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_audio_basics
vendor: juce
version: 4.3.0
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: GPL/Commercial
dependencies: juce_core
OSXFrameworks: Accelerate
iOSFrameworks: Accelerate
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#ifndef JUCE_AUDIO_BASICS_H_INCLUDED
#define JUCE_AUDIO_BASICS_H_INCLUDED
#include "../juce_core/juce_core.h"
#include "juce_core/juce_core.h"
//==============================================================================
namespace juce
{
@@ -37,6 +62,7 @@ namespace juce
#include "buffers/juce_AudioDataConverters.h"
#include "buffers/juce_FloatVectorOperations.h"
#include "buffers/juce_AudioSampleBuffer.h"
#include "buffers/juce_AudioChannelSet.h"
#include "effects/juce_Decibels.h"
#include "effects/juce_IIRFilter.h"
#include "effects/juce_IIRFilterOld.h"


+ 1
- 1
source/modules/juce_audio_basics/midi/juce_MidiFile.cpp View File

@@ -248,7 +248,7 @@ bool MidiFile::readFrom (InputStream& sourceStream)
clear();
MemoryBlock data;
const int maxSensibleMidiFileSize = 2 * 1024 * 1024;
const int maxSensibleMidiFileSize = 200 * 1024 * 1024;
// (put a sanity-check on the file size, as midi files are generally small)
if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize))


+ 99
- 67
source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp View File

@@ -33,11 +33,23 @@ namespace MidiHelpers
{
return (uint8) jlimit (0, 127, v);
}
}
inline uint8 floatVelocityToByte (const float v) noexcept
{
return validVelocity (roundToInt (v * 127.0f));
}
//==============================================================================
uint8 MidiMessage::floatValueToMidiByte (const float v) noexcept
{
return MidiHelpers::validVelocity (roundToInt (v * 127.0f));
}
uint16 MidiMessage::pitchbendToPitchwheelPos (const float pitchbend,
const float pitchbendRange) noexcept
{
// can't translate a pitchbend value that is outside of the given range!
jassert (std::abs (pitchbend) <= pitchbendRange);
return static_cast<uint16> (pitchbend > 0.0f
? jmap (pitchbend, 0.0f, pitchbendRange, 8192.0f, 16383.0f)
: jmap (pitchbend, -pitchbendRange, 0.0f, 0.0f, 8192.0f));
}
//==============================================================================
@@ -84,25 +96,24 @@ int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept
MidiMessage::MidiMessage() noexcept
: timeStamp (0), size (2)
{
preallocatedData.asBytes[0] = 0xf0;
preallocatedData.asBytes[1] = 0xf7;
packedData.asBytes[0] = 0xf0;
packedData.asBytes[1] = 0xf7;
}
MidiMessage::MidiMessage (const void* const d, const int dataSize, const double t)
: timeStamp (t),
size (dataSize)
: timeStamp (t), size (dataSize)
{
jassert (dataSize > 0);
memcpy (allocateSpace (dataSize), d, (size_t) dataSize);
// this checks that the length matches the data..
jassert (dataSize > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size);
// check that the length matches the data..
jassert (size > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size);
memcpy (allocateSpace (dataSize), d, (size_t) dataSize);
}
MidiMessage::MidiMessage (const int byte1, const double t) noexcept
: timeStamp (t), size (1)
{
preallocatedData.asBytes[0] = (uint8) byte1;
packedData.asBytes[0] = (uint8) byte1;
// check that the length matches the data..
jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1);
@@ -111,8 +122,8 @@ MidiMessage::MidiMessage (const int byte1, const double t) noexcept
MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept
: timeStamp (t), size (2)
{
preallocatedData.asBytes[0] = (uint8) byte1;
preallocatedData.asBytes[1] = (uint8) byte2;
packedData.asBytes[0] = (uint8) byte1;
packedData.asBytes[1] = (uint8) byte2;
// check that the length matches the data..
jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2);
@@ -121,9 +132,9 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noex
MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept
: timeStamp (t), size (3)
{
preallocatedData.asBytes[0] = (uint8) byte1;
preallocatedData.asBytes[1] = (uint8) byte2;
preallocatedData.asBytes[2] = (uint8) byte3;
packedData.asBytes[0] = (uint8) byte1;
packedData.asBytes[1] = (uint8) byte2;
packedData.asBytes[2] = (uint8) byte3;
// check that the length matches the data..
jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3);
@@ -132,29 +143,19 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, con
MidiMessage::MidiMessage (const MidiMessage& other)
: timeStamp (other.timeStamp), size (other.size)
{
if (other.allocatedData != nullptr)
{
allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size);
}
if (isHeapAllocated())
memcpy (allocateSpace (size), other.getData(), (size_t) size);
else
{
preallocatedData.asInt32 = other.preallocatedData.asInt32;
}
packedData.allocatedData = other.packedData.allocatedData;
}
MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp)
: timeStamp (newTimeStamp), size (other.size)
{
if (other.allocatedData != nullptr)
{
allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size);
}
if (isHeapAllocated())
memcpy (allocateSpace (size), other.getData(), (size_t) size);
else
{
preallocatedData.asInt32 = other.preallocatedData.asInt32;
}
packedData.allocatedData = other.packedData.allocatedData;
}
MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte,
@@ -229,16 +230,15 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const
}
else
{
preallocatedData.asInt32 = 0;
size = getMessageLengthFromFirstByte ((uint8) byte);
preallocatedData.asBytes[0] = (uint8) byte;
packedData.asBytes[0] = (uint8) byte;
if (size > 1)
{
preallocatedData.asBytes[1] = src[0];
packedData.asBytes[1] = src[0];
if (size > 2)
preallocatedData.asBytes[2] = src[1];
packedData.asBytes[2] = src[1];
}
}
@@ -246,7 +246,7 @@ MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const
}
else
{
preallocatedData.asInt32 = 0;
packedData.allocatedData = nullptr;
size = 0;
}
}
@@ -255,19 +255,25 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other)
{
if (this != &other)
{
timeStamp = other.timeStamp;
size = other.size;
if (other.allocatedData != nullptr)
if (other.isHeapAllocated())
{
allocatedData.malloc ((size_t) size);
memcpy (allocatedData, other.allocatedData, (size_t) size);
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));
memcpy (packedData.allocatedData, other.packedData.allocatedData, (size_t) other.size);
}
else
{
allocatedData.free();
preallocatedData.asInt32 = other.preallocatedData.asInt32;
if (isHeapAllocated())
std::free (packedData.allocatedData);
packedData.allocatedData = other.packedData.allocatedData;
}
timeStamp = other.timeStamp;
size = other.size;
}
return *this;
@@ -277,36 +283,61 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other)
MidiMessage::MidiMessage (MidiMessage&& other) noexcept
: timeStamp (other.timeStamp), size (other.size)
{
if (other.allocatedData != nullptr)
allocatedData.swapWith (other.allocatedData);
else
preallocatedData.asInt32 = other.preallocatedData.asInt32;
packedData.allocatedData = other.packedData.allocatedData;
other.size = 0;
}
MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept
{
jassert (this != &other); // shouldn't be possible
packedData.allocatedData = other.packedData.allocatedData;
timeStamp = other.timeStamp;
size = other.size;
allocatedData.swapWith (other.allocatedData);
preallocatedData.asInt32 = other.preallocatedData.asInt32;
other.size = 0;
return *this;
}
#endif
MidiMessage::~MidiMessage() {}
MidiMessage::~MidiMessage() noexcept
{
if (isHeapAllocated())
std::free (packedData.allocatedData);
}
uint8* MidiMessage::allocateSpace (int bytes)
{
if (bytes > 4)
if (bytes > (int) sizeof (packedData))
{
uint8* d = static_cast<uint8*> (std::malloc ((size_t) bytes));
packedData.allocatedData = d;
return d;
}
return packedData.asBytes;
}
String MidiMessage::getDescription() const
{
if (isNoteOn()) return "Note on " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
if (isNoteOff()) return "Note off " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel());
if (isProgramChange()) return "Program change " + String (getProgramChangeNumber()) + " Channel " + String (getChannel());
if (isPitchWheel()) return "Pitch wheel " + String (getPitchWheelValue()) + " Channel " + String (getChannel());
if (isAftertouch()) return "Aftertouch " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + ": " + String (getAfterTouchValue()) + " Channel " + String (getChannel());
if (isChannelPressure()) return "Channel pressure " + String (getChannelPressureValue()) + " Channel " + String (getChannel());
if (isAllNotesOff()) return "All notes off Channel " + String (getChannel());
if (isAllSoundOff()) return "All sound off Channel " + String (getChannel());
if (isMetaEvent()) return "Meta event";
if (isController())
{
allocatedData.malloc ((size_t) bytes);
return allocatedData;
String name (MidiMessage::getControllerName (getControllerNumber()));
if (name.isEmpty())
name = String (getControllerNumber());
return "Controller " + name + ": " + String (getControllerValue()) + " Channel " + String (getChannel());
}
return preallocatedData.asBytes;
return String::toHexString (getRawData(), getRawDataSize());
}
int MidiMessage::getChannel() const noexcept
@@ -391,7 +422,7 @@ float MidiMessage::getFloatVelocity() const noexcept
void MidiMessage::setVelocity (const float newVelocity) noexcept
{
if (isNoteOnOrOff())
getData()[2] = MidiHelpers::floatVelocityToByte (newVelocity);
getData()[2] = floatValueToMidiByte (newVelocity);
}
void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept
@@ -538,7 +569,7 @@ MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const
MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept
{
return noteOn (channel, noteNumber, MidiHelpers::floatVelocityToByte (velocity));
return noteOn (channel, noteNumber, floatValueToMidiByte (velocity));
}
MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept
@@ -552,7 +583,7 @@ MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8
MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, float velocity) noexcept
{
return noteOff (channel, noteNumber, MidiHelpers::floatVelocityToByte (velocity));
return noteOff (channel, noteNumber, floatValueToMidiByte (velocity));
}
MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber) noexcept
@@ -697,9 +728,10 @@ MidiMessage MidiMessage::textMetaEvent (int type, StringRef text)
header[--n] = 0xff;
const size_t headerLen = sizeof (header) - n;
const int totalSize = (int) (headerLen + textSize);
uint8* const dest = result.allocateSpace ((int) (headerLen + textSize));
result.size = (int) (headerLen + textSize);
uint8* const dest = result.allocateSpace (totalSize);
result.size = totalSize;
memcpy (dest, header + n, headerLen);
memcpy (dest + headerLen, text.text.getAddress(), textSize);
@@ -816,7 +848,7 @@ bool MidiMessage::isKeySignatureMetaEvent() const noexcept
int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept
{
return (int) getMetaEventData()[0];
return (int) (int8) getMetaEventData()[0];
}
bool MidiMessage::isKeySignatureMajorKey() const noexcept
@@ -985,7 +1017,7 @@ String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctav
return String();
}
double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept
double MidiMessage::getMidiNoteInHertz (const int noteNumber, const double frequencyOfA) noexcept
{
return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0);
}


+ 27
- 13
source/modules/juce_audio_basics/midi/juce_MidiMessage.h View File

@@ -104,7 +104,7 @@ public:
MidiMessage (const MidiMessage&, double newTimeStamp);
/** Destructor. */
~MidiMessage();
~MidiMessage() noexcept;
/** Copies this message from another one. */
MidiMessage& operator= (const MidiMessage& other);
@@ -118,13 +118,19 @@ public:
/** Returns a pointer to the raw midi data.
@see getRawDataSize
*/
const uint8* getRawData() const noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; }
const uint8* getRawData() const noexcept { return getData(); }
/** Returns the number of bytes of data in the message.
@see getRawData
*/
int getRawDataSize() const noexcept { return size; }
//==============================================================================
/** Returns a human-readable description of the midi message as a string,
for example "Note On C#3 Velocity 120 Channel 1".
*/
String getDescription() const;
//==============================================================================
/** Returns the timestamp associated with this message.
@@ -845,7 +851,7 @@ public:
The value passed in must be 0x80 or higher.
*/
static int getMessageLengthFromFirstByte (const uint8 firstByte) noexcept;
static int getMessageLengthFromFirstByte (uint8 firstByte) noexcept;
//==============================================================================
/** Returns the name of a midi note number.
@@ -872,7 +878,7 @@ public:
The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch.
@see getMidiNoteName
*/
static double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept;
static double getMidiNoteInHertz (int noteNumber, double frequencyOfA = 440.0) noexcept;
/** Returns true if the given midi note number is a black key. */
static bool isMidiNoteBlack (int noteNumber) noexcept;
@@ -899,21 +905,29 @@ public:
*/
static const char* getControllerName (int controllerNumber);
/** Converts a floating-point value between 0 and 1 to a MIDI 7-bit value between 0 and 127. */
static uint8 floatValueToMidiByte (float valueBetween0and1) noexcept;
/** Converts a pitchbend value in semitones to a MIDI 14-bit pitchwheel position value. */
static uint16 pitchbendToPitchwheelPos (float pitchbendInSemitones,
float pitchbendRangeInSemitones) noexcept;
private:
//==============================================================================
double timeStamp;
HeapBlock<uint8> allocatedData;
int size;
#ifndef DOXYGEN
union
union PackedData
{
uint8 asBytes[4];
uint32 asInt32;
} preallocatedData;
uint8* allocatedData;
uint8 asBytes[sizeof (uint8*)];
};
PackedData packedData;
double timeStamp;
int size;
#endif
inline uint8* getData() noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; }
inline bool isHeapAllocated() const noexcept { return size > (int) sizeof (packedData); }
inline uint8* getData() const noexcept { return isHeapAllocated() ? packedData.allocatedData : (uint8*) packedData.asBytes; }
uint8* allocateSpace (int);
};


+ 1
- 1
source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp View File

@@ -80,7 +80,7 @@ int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcep
return -1;
}
int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const noexcept
int MidiMessageSequence::getIndexOf (const MidiEventHolder* const event) const noexcept
{
return list.indexOf (event);
}


+ 13
- 1
source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h View File

@@ -48,6 +48,18 @@ public:
/** Replaces this sequence with another one. */
MidiMessageSequence& operator= (const MidiMessageSequence&);
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
MidiMessageSequence (MidiMessageSequence&& other) noexcept
: list (static_cast<OwnedArray<MidiEventHolder>&&> (other.list))
{}
MidiMessageSequence& operator= (MidiMessageSequence&& other) noexcept
{
list = static_cast<OwnedArray<MidiEventHolder>&&> (other.list);
return *this;
}
#endif
/** Destructor. */
~MidiMessageSequence();
@@ -109,7 +121,7 @@ public:
int getIndexOfMatchingKeyUp (int index) const noexcept;
/** Returns the index of an event. */
int getIndexOf (MidiEventHolder* event) const noexcept;
int getIndexOf (const MidiEventHolder* event) const noexcept;
/** Returns the index of the first event on or after the given timestamp.
If the time is beyond the end of the sequence, this will return the


+ 1
- 2
source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp View File

@@ -22,7 +22,6 @@
==============================================================================
*/
MidiRPNDetector::MidiRPNDetector() noexcept
{
}
@@ -55,7 +54,7 @@ void MidiRPNDetector::reset() noexcept
}
//==============================================================================
MidiRPNDetector::ChannelState::ChannelState () noexcept
MidiRPNDetector::ChannelState::ChannelState() noexcept
: parameterMSB (0xff), parameterLSB (0xff), valueMSB (0xff), valueLSB (0xff), isNRPN (false)
{
}


+ 184
- 174
source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp View File

@@ -39,6 +39,9 @@ MPEInstrument::MPEInstrument() noexcept
pressureDimension.value = &MPENote::pressure;
timbreDimension.value = &MPENote::timbre;
// the default value for pressure is 0, for all other dimension it is centre (= default MPEValue)
std::fill_n (pressureDimension.lastValueReceivedOnChannel, 16, MPEValue::minValue());
legacyMode.isEnabled = false;
legacyMode.pitchbendRange = 2;
legacyMode.channelRange = Range<int> (1, 17);
@@ -271,22 +274,6 @@ void MPEInstrument::handleTimbreLSB (int midiChannel, int value) noexcept
lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value);
}
//==============================================================================
MPEValue MPEInstrument::getInitialPitchbendForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const
{
return pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1];
}
MPEValue MPEInstrument::getInitialPressureForNoteOn (int /*midiChannel*/, int /*midiNoteNumber*/, MPEValue midiNoteOnVelocity) const
{
return midiNoteOnVelocity;
}
MPEValue MPEInstrument::getInitialTimbreForNoteOn (int midiChannel, int /*midiNoteNumber*/, MPEValue /*midiNoteOnVelocity*/) const
{
return timbreDimension.lastValueReceivedOnChannel[midiChannel - 1];
}
//==============================================================================
void MPEInstrument::noteOn (int midiChannel,
int midiNoteNumber,
@@ -298,9 +285,9 @@ void MPEInstrument::noteOn (int midiChannel,
MPENote newNote (midiChannel,
midiNoteNumber,
midiNoteOnVelocity,
getInitialPitchbendForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity),
getInitialPressureForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity),
getInitialTimbreForNoteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity),
getInitialValueForNewNote (midiChannel, pitchbendDimension),
getInitialValueForNewNote (midiChannel, pressureDimension),
getInitialValueForNewNote (midiChannel, timbreDimension),
isNoteChannelSustained[midiChannel - 1] ? MPENote::keyDownAndSustained : MPENote::keyDown);
const ScopedLock sl (lock);
@@ -324,7 +311,7 @@ void MPEInstrument::noteOff (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOffVelocity)
{
if (notes.empty() || ! isNoteChannel (midiChannel))
if (notes.isEmpty() || ! isNoteChannel (midiChannel))
return;
const ScopedLock sl (lock);
@@ -334,10 +321,11 @@ void MPEInstrument::noteOff (int midiChannel,
note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off;
note->noteOffVelocity = midiNoteOffVelocity;
// last pitchbend and timbre values received for this note should not be re-used for
// last dimension values received for this note should not be re-used for
// any new notes, so reset them:
pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue();
timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue();
pressureDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::minValue();
pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue();
timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue();
if (note->keyState == MPENote::off)
{
@@ -370,12 +358,20 @@ void MPEInstrument::timbre (int midiChannel, MPEValue value)
updateDimension (midiChannel, timbreDimension, value);
}
MPEValue MPEInstrument::getInitialValueForNewNote (int midiChannel, MPEDimension& dimension) const
{
if (getLastNotePlayedPtr (midiChannel) != nullptr)
return &dimension == &pressureDimension ? MPEValue::minValue() : MPEValue::centreValue();
return dimension.lastValueReceivedOnChannel[midiChannel - 1];
}
//==============================================================================
void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, MPEValue value)
{
dimension.lastValueReceivedOnChannel[midiChannel - 1] = value;
if (notes.empty())
if (notes.isEmpty())
return;
if (MPEZone* zone = zoneLayout.getZoneByMasterChannel (midiChannel))
@@ -758,7 +754,7 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 1);
expectEquals (test.noteAddedCallCounter, 1);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
// note-off
test.noteOff (3, 60, MPEValue::from7BitInt (33));
@@ -774,13 +770,13 @@ public:
// note off with non-matching note number shouldn't do anything
test.noteOff (3, 61, MPEValue::from7BitInt (33));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteReleasedCallCounter, 0);
// note off with non-matching midi channel shouldn't do anything
test.noteOff (2, 60, MPEValue::from7BitInt (33));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteReleasedCallCounter, 0);
}
{
@@ -791,9 +787,9 @@ public:
test.noteOn (3, 1, MPEValue::from7BitInt (100));
test.noteOn (3, 2, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 3);
expectNote (test.getNote (3, 0), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 1), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 2), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 0), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 1), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 2), 100, 0, 8192, 64, MPENote::keyDown);
}
{
// pathological case: second note-on for same note should retrigger it.
@@ -802,7 +798,7 @@ public:
test.noteOn (3, 0, MPEValue::from7BitInt (100));
test.noteOn (3, 0, MPEValue::from7BitInt (60));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (3, 0), 60, 60, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 0), 60, 0, 8192, 64, MPENote::keyDown);
}
}
@@ -844,42 +840,42 @@ public:
// sustain pedal on per-note channel shouldn't do anything.
test.sustainPedal (3, true);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 0);
// sustain pedal on non-zone channel shouldn't do anything either.
test.sustainPedal (1, true);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 0);
// sustain pedal on master channel should sustain notes on *that* zone.
test.sustainPedal (2, true);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 1);
// release
test.sustainPedal (2, false);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 2);
// should also sustain new notes added after the press
test.sustainPedal (2, true);
expectEquals (test.noteKeyStateChangedCallCounter, 3);
test.noteOn (4, 51, MPEValue::from7BitInt (100));
expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
expectEquals (test.noteKeyStateChangedCallCounter, 3);
// ...but only if that sustain came on the master channel of that zone!
test.sustainPedal (11, true);
test.noteOn (11, 52, MPEValue::from7BitInt (100));
expectNote (test.getNote (11, 52), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (11, 52), 100, 0, 8192, 64, MPENote::keyDown);
test.noteOff (11, 52, MPEValue::from7BitInt (100));
expectEquals (test.noteReleasedCallCounter, 1);
@@ -890,8 +886,8 @@ public:
expectEquals (test.getNumPlayingNotes(), 2);
expectEquals (test.noteReleasedCallCounter, 2);
expectEquals (test.noteKeyStateChangedCallCounter, 5);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::sustained);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::sustained);
// notes should be turned off when pedal is released
test.sustainPedal (2, false);
@@ -908,26 +904,26 @@ public:
// sostenuto pedal on per-note channel shouldn't do anything.
test.sostenutoPedal (3, true);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 0);
// sostenuto pedal on non-zone channel shouldn't do anything either.
test.sostenutoPedal (1, true);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 0);
// sostenuto pedal on master channel should sustain notes on *that* zone.
test.sostenutoPedal (2, true);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 1);
// release
test.sostenutoPedal (2, false);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 2);
// should only sustain notes turned on *before* the press (difference to sustain pedal)
@@ -935,9 +931,9 @@ public:
expectEquals (test.noteKeyStateChangedCallCounter, 3);
test.noteOn (4, 51, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 3);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (4, 51), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteKeyStateChangedCallCounter, 3);
// note-off should not turn off sustained notes inside the same zone,
@@ -946,7 +942,7 @@ public:
test.noteOff (4, 51, MPEValue::from7BitInt (100));
test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected!
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
expectEquals (test.noteReleasedCallCounter, 2);
expectEquals (test.noteKeyStateChangedCallCounter, 4);
@@ -1047,22 +1043,22 @@ public:
// applying pressure on a per-note channel should modulate one note
test.pressure (3, MPEValue::from7BitInt (33));
expectNote (test.getNote (3, 60), 100, 33, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 1);
// applying pressure on a master channel should modulate all notes in this zone
test.pressure (2, MPEValue::from7BitInt (44));
expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 3);
// applying pressure on an unrelated channel should be ignored
test.pressure (1, MPEValue::from7BitInt (55));
expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 3);
}
{
@@ -1073,7 +1069,7 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pressure (3, MPEValue::from7BitInt (66));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 66, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 1);
}
@@ -1091,6 +1087,49 @@ public:
expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 1);
}
{
UnitTestInstrument test;
test.setZoneLayout (testLayout);
// if no pressure is sent before note-on, default = 0 should be used
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
}
{
UnitTestInstrument test;
test.setZoneLayout (testLayout);
// if pressure is sent before note-on, use that
test.pressure (3, MPEValue::from7BitInt (77));
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown);
}
{
UnitTestInstrument test;
test.setZoneLayout (testLayout);
// if pressure is sent before note-on, but it belonged to another note
// on the same channel that has since been turned off, use default = 0
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pressure (3, MPEValue::from7BitInt (77));
test.noteOff (3, 61, MPEValue::from7BitInt (100));
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
}
{
UnitTestInstrument test;
test.setZoneLayout (testLayout);
// edge case: two notes on the same channel simultaneously. the second one should use
// pressure = 0 initially but then react to additional pressure messages
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pressure (3, MPEValue::from7BitInt (77));
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
test.pressure (3, MPEValue::from7BitInt (78));
expectNote (test.getNote (3, 60), 100, 78, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 77, 8192, 64, MPENote::keyDown);
}
}
beginTest ("pitchbend");
@@ -1105,9 +1144,9 @@ public:
// applying pitchbend on a per-note channel should modulate one note
test.pitchbend (3, MPEValue::from14BitInt (1111));
expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 1);
// applying pitchbend on a master channel should be ignored for the
@@ -1115,16 +1154,16 @@ public:
// Note: noteChanged will be called anyway for notes in that zone
// because the total pitchbend for those notes has changed
test.pitchbend (2, MPEValue::from14BitInt (2222));
expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 3);
// applying pitchbend on an unrelated channel should do nothing.
test.pitchbend (1, MPEValue::from14BitInt (3333));
expectNote (test.getNote (3, 60), 100, 100, 1111, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 3);
}
{
@@ -1135,8 +1174,8 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (4444));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 4444, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 4444, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 1);
}
{
@@ -1150,7 +1189,7 @@ public:
test.noteOff (3, 61, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (5555));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 1);
}
{
@@ -1169,14 +1208,14 @@ public:
test.sustainPedal (2, true);
test.noteOff (3, 60, MPEValue::from7BitInt (64));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
expectEquals (test.noteKeyStateChangedCallCounter, 2);
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (6666));
expectEquals (test.getNumPlayingNotes(), 2);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::sustained);
expectNote (test.getNote (3, 61), 100, 100, 6666, 64, MPENote::keyDownAndSustained);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained);
expectNote (test.getNote (3, 61), 100, 0, 6666, 64, MPENote::keyDownAndSustained);
expectEquals (test.notePitchbendChangedCallCounter, 1);
}
{
@@ -1193,11 +1232,11 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (5555));
expectNote (test.getNote (3, 60), 100, 100, 5555, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown);
test.noteOff (3, 60, MPEValue::from7BitInt (100));
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
}
{
// applying per-note pitchbend should set the note's totalPitchbendInSemitones
@@ -1289,23 +1328,23 @@ public:
// modulating timbre on a per-note channel should modulate one note
test.timbre (3, MPEValue::from7BitInt (33));
expectNote (test.getNote (3, 60), 100, 100, 8192, 33, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 33, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 1);
// modulating timbre on a master channel should modulate all notes in this zone
test.timbre (2, MPEValue::from7BitInt (44));
expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 3);
// modulating timbre on an unrelated channel should be ignored
test.timbre (1, MPEValue::from7BitInt (55));
expectNote (test.getNote (3, 60), 100, 100, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown);
expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 3);
}
{
@@ -1316,8 +1355,8 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.timbre (3, MPEValue::from7BitInt (66));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 66, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 66, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 1);
}
{
@@ -1331,7 +1370,7 @@ public:
test.noteOff (3, 61, MPEValue::from7BitInt (100));
test.timbre (3, MPEValue::from7BitInt (77));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (3, 60), 100, 100, 8192, 77, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 77, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 1);
}
{
@@ -1341,11 +1380,11 @@ public:
// Zsolt's edge case for timbre
test.noteOn (3, 60, MPEValue::from7BitInt (100));
test.timbre (3, MPEValue::from7BitInt (42));
expectNote (test.getNote (3, 60), 100, 100, 8192, 42, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 42, MPENote::keyDown);
test.noteOff (3, 60, MPEValue::from7BitInt (100));
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
}
}
@@ -1361,8 +1400,8 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pressure (3, MPEValue::from7BitInt (99));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 1);
}
@@ -1377,8 +1416,8 @@ public:
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pressure (3, MPEValue::from7BitInt (99));
expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 1);
}
{
@@ -1391,9 +1430,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pressure (3, MPEValue::from7BitInt (99));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePressureChangedCallCounter, 1);
}
{
@@ -1425,9 +1464,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (9999));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 1);
}
{
@@ -1440,9 +1479,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (9999));
expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 1);
}
{
@@ -1455,9 +1494,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (9999));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 1);
}
{
@@ -1470,9 +1509,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.pitchbend (3, MPEValue::from14BitInt (9999));
expectNote (test.getNote (3, 60), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown);
expectEquals (test.notePitchbendChangedCallCounter, 3);
}
}
@@ -1489,9 +1528,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.timbre (3, MPEValue::from7BitInt (99));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 1);
}
{
@@ -1504,9 +1543,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.timbre (3, MPEValue::from7BitInt (99));
expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 1);
}
{
@@ -1519,9 +1558,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.timbre (3, MPEValue::from7BitInt (99));
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 1);
}
{
@@ -1534,9 +1573,9 @@ public:
test.noteOn (3, 62, MPEValue::from7BitInt (100));
test.noteOn (3, 61, MPEValue::from7BitInt (100));
test.timbre (3, MPEValue::from7BitInt (99));
expectNote (test.getNote (3, 60), 100, 100, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 100, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 100, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown);
expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown);
expectEquals (test.noteTimbreChangedCallCounter, 3);
}
}
@@ -1724,7 +1763,7 @@ public:
expectEquals (test.getNumPlayingNotes(), 0);
}
beginTest ("default getInitial...ForNoteOn");
beginTest ("default initial values for pitchbend and timbre");
{
MPEInstrument test;
test.setZoneLayout (testLayout);
@@ -1739,16 +1778,7 @@ public:
test.noteOn (3, 60, MPEValue::from7BitInt (100));
expectNote (test.getMostRecentNote (3), 100, 100, 3333, 66, MPENote::keyDown);
}
beginTest ("overriding getInitial...ForNoteOn");
{
CustomInitialValuesTest<33, 4444, 55> test;
test.setZoneLayout (testLayout);
test.noteOn (3, 61, MPEValue::from7BitInt (100));
expectNote (test.getMostRecentNote (3), 100, 33, 4444, 55, MPENote::keyDown);
expectNote (test.getMostRecentNote (3), 100, 0, 3333, 66, MPENote::keyDown);
}
beginTest ("Legacy mode");
@@ -1806,10 +1836,10 @@ public:
test.pressure (2, MPEValue::from7BitInt (88));
test.timbre (15, MPEValue::from7BitInt (77));
expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (2, 60), 100, 88, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (15, 60), 100, 100, 8192, 77, MPENote::keyDown);
expectNote (test.getNote (16, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (15, 60), 100, 0, 8192, 77, MPENote::keyDown);
expectNote (test.getNote (16, 60), 100, 0, 8192, 64, MPENote::keyDown);
// note off should work in legacy mode
@@ -1835,10 +1865,10 @@ public:
test.noteOn (16, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 4);
expectNote (test.getNote (3, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (6, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (7, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (6, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (7, 60), 100, 0, 8192, 64, MPENote::keyDown);
}
{
// tracking mode in legacy mode
@@ -1851,9 +1881,9 @@ public:
test.noteOn (1, 62, MPEValue::from7BitInt (100));
test.noteOn (1, 61, MPEValue::from7BitInt (100));
test.pitchbend (1, MPEValue::from14BitInt (9999));
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown);
}
{
UnitTestInstrument test;
@@ -1864,9 +1894,9 @@ public:
test.noteOn (1, 62, MPEValue::from7BitInt (100));
test.noteOn (1, 61, MPEValue::from7BitInt (100));
test.pitchbend (1, MPEValue::from14BitInt (9999));
expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown);
}
{
UnitTestInstrument test;
@@ -1877,9 +1907,9 @@ public:
test.noteOn (1, 62, MPEValue::from7BitInt (100));
test.noteOn (1, 61, MPEValue::from7BitInt (100));
test.pitchbend (1, MPEValue::from14BitInt (9999));
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 100, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown);
}
{
UnitTestInstrument test;
@@ -1890,9 +1920,9 @@ public:
test.noteOn (1, 62, MPEValue::from7BitInt (100));
test.noteOn (1, 61, MPEValue::from7BitInt (100));
test.pitchbend (1, MPEValue::from14BitInt (9999));
expectNote (test.getNote (1, 60), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 100, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown);
expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown);
}
}
{
@@ -1916,7 +1946,7 @@ public:
test.noteOff (1, 60, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained);
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained);
test.sustainPedal (1, false);
expectEquals (test.getNumPlayingNotes(), 0);
@@ -1939,7 +1969,7 @@ public:
test.noteOff (2, 61, MPEValue::from7BitInt (100));
expectEquals (test.getNumPlayingNotes(), 1);
expectNote (test.getNote (1, 60), 100, 100, 8192, 64, MPENote::sustained);
expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained);
test.sostenutoPedal (1, false);
expectEquals (test.getNumPlayingNotes(), 0);
@@ -2081,26 +2111,6 @@ private:
}
};
//==============================================================================
template <int initial7BitPressure, int initial14BitPitchbend, int initial7BitTimbre>
class CustomInitialValuesTest : public MPEInstrument
{
MPEValue getInitialPitchbendForNoteOn (int, int, MPEValue) const override
{
return MPEValue::from14BitInt (initial14BitPitchbend);
}
MPEValue getInitialPressureForNoteOn (int, int, MPEValue) const override
{
return MPEValue::from7BitInt (initial7BitPressure);
}
MPEValue getInitialTimbreForNoteOn (int, int, MPEValue) const override
{
return MPEValue::from7BitInt (initial7BitTimbre);
}
};
//==============================================================================
void expectNote (MPENote noteToTest,
int noteOnVelocity7Bit,


+ 1
- 30
source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h View File

@@ -320,36 +320,6 @@ public:
/** Re-sets the pitchbend range in semitones (0-96) to be used for notes when in legacy mode. */
void setLegacyModePitchbendRange (int pitchbendRange);
protected:
//==============================================================================
/** This method defines what initial pitchbend value should be used for newly
triggered notes. The default is to use the last pitchbend value
that has been received on the same MIDI channel (or no pitchbend
if no pitchbend messages have been received so far).
Override this method if you need different behaviour.
*/
virtual MPEValue getInitialPitchbendForNoteOn (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOnVelocity) const;
/** This method defines what initial pressure value should be used for newly
triggered notes. The default is to re-use the note-on velocity value.
Override this method if you need different behaviour.
*/
virtual MPEValue getInitialPressureForNoteOn (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOnVelocity) const;
/** This method defines what initial timbre value should be used for newly
triggered notes. The default is to use the last timbre value that has
that has been received on the same MIDI channel (or a neutral centred value
if no pitchbend messages have been received so far).
Override this method if you need different behaviour.
*/
virtual MPEValue getInitialTimbreForNoteOn (int midiChannel,
int midiNoteNumber,
MPEValue midiNoteOnVelocity) const;
private:
//==============================================================================
CriticalSection lock;
@@ -384,6 +354,7 @@ private:
void updateDimensionMaster (MPEZone&, MPEDimension&, MPEValue);
void updateDimensionForNote (MPENote&, MPEDimension&, MPEValue);
void callListenersDimensionChanged (MPENote&, MPEDimension&);
MPEValue getInitialValueForNewNote (int midiChannel, MPEDimension&) const;
void processMidiNoteOnMessage (const MidiMessage&);
void processMidiNoteOffMessage (const MidiMessage&);


+ 0
- 1
source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp View File

@@ -22,7 +22,6 @@
==============================================================================
*/
MidiBuffer MPEMessages::addZone (MPEZone zone)
{
MidiBuffer buffer (MidiRPNGenerator::generate (zone.getFirstNoteChannel(),


+ 10
- 5
source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp View File

@@ -25,7 +25,8 @@
MPESynthesiserBase::MPESynthesiserBase()
: instrument (new MPEInstrument),
sampleRate (0),
minimumSubBlockSize (32)
minimumSubBlockSize (32),
subBlockSubdivisionIsStrict (false)
{
instrument->addListener (this);
}
@@ -100,10 +101,11 @@ void MPESynthesiserBase::renderNextBlock (AudioBuffer<floatType>& outputAudio,
MidiBuffer::Iterator midiIterator (inputMidi);
midiIterator.setNextSamplePosition (startSample);
bool firstEvent = true;
int midiEventPos;
MidiMessage m;
const ScopedLock sl (renderAudioLock);
const ScopedLock sl (noteStateLock);
while (numSamples > 0)
{
@@ -122,12 +124,14 @@ void MPESynthesiserBase::renderNextBlock (AudioBuffer<floatType>& outputAudio,
break;
}
if (samplesToNextMidiMessage < minimumSubBlockSize)
if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize))
{
handleMidiEvent (m);
continue;
}
firstEvent = false;
renderNextSubBlock (outputAudio, startSample, samplesToNextMidiMessage);
handleMidiEvent (m);
startSample += samplesToNextMidiMessage;
@@ -147,15 +151,16 @@ void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate)
{
if (sampleRate != newRate)
{
const ScopedLock sl (renderAudioLock);
const ScopedLock sl (noteStateLock);
instrument->releaseAllNotes();
sampleRate = newRate;
}
}
//==============================================================================
void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples) noexcept
void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept
{
jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
minimumSubBlockSize = numSamples;
subBlockSubdivisionIsStrict = shouldBeStrict;
}

+ 9
- 3
source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h View File

@@ -127,8 +127,14 @@ public:
The default setting is 32, which means that midi messages are accurate to about < 1ms
accuracy, which is probably fine for most purposes, but you may want to increase or
decrease this value for your synth.
If shouldBeStrict is true, the audio sub-blocks will strictly never be smaller than numSamples.
If shouldBeStrict is false (default), the first audio sub-block in the buffer is allowed
to be smaller, to make sure that the first MIDI event in a buffer will always be sample-accurate
(this can sometimes help to avoid quantisation or phasing issues).
*/
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept;
void setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict = false) noexcept;
//==============================================================================
/** Puts the synthesiser into legacy mode.
@@ -179,13 +185,13 @@ protected:
//==============================================================================
/** @internal */
ScopedPointer<MPEInstrument> instrument;
/** @internal */
CriticalSection renderAudioLock;
private:
//==============================================================================
CriticalSection noteStateLock;
double sampleRate;
int minimumSubBlockSize;
bool subBlockSubdivisionIsStrict;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserBase)
};


+ 14
- 6
source/modules/juce_audio_basics/mpe/juce_MPEZone.h View File

@@ -69,27 +69,35 @@ struct JUCE_API MPEZone
int perNotePitchbendRange = 48,
int masterPitchbendRange = 2) noexcept;
/* Returns the MIDI master channel of this zone. */
/* Returns the MIDI master channel number (in the range 1-16) of this zone. */
int getMasterChannel() const noexcept;
/** Returns the number of note channels occupied by this zone. */
int getNumNoteChannels() const noexcept;
/* Returns the MIDI channel number of the lowest-numbered note channel of this zone. */
/* Returns the MIDI channel number (in the range 1-16) of the
lowest-numbered note channel of this zone.
*/
int getFirstNoteChannel() const noexcept;
/* Returns the MIDI channel number of the highest-numbered note channel of this zone. */
/* Returns the MIDI channel number (in the range 1-16) of the
highest-numbered note channel of this zone.
*/
int getLastNoteChannel() const noexcept;
/** Returns the MIDI channel numbers of the note channels of this zone as a Range. */
/** Returns the MIDI channel numbers (in the range 1-16) of the
note channels of this zone as a Range.
*/
Range<int> getNoteChannelRange() const noexcept;
/** Returns true if the MIDI channel (in the range 1-16) is used by this zone
either as a note channel or as the master channel; false otherwise. */
either as a note channel or as the master channel; false otherwise.
*/
bool isUsingChannel (int channel) const noexcept;
/** Returns true if the MIDI channel (in the range 1-16) is used by this zone
as a note channel; false otherwise. */
as a note channel; false otherwise.
*/
bool isUsingChannelAsNoteChannel (int channel) const noexcept;
/** Returns the per-note pitchbend range in semitones set for this zone. */


+ 1
- 0
source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp View File

@@ -34,6 +34,7 @@ MPEZoneLayout::MPEZoneLayout (const MPEZoneLayout& other)
MPEZoneLayout& MPEZoneLayout::operator= (const MPEZoneLayout& other)
{
zones = other.zones;
listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this);
return *this;
}


+ 3
- 2
source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h View File

@@ -100,8 +100,9 @@ public:
/** Returns the current number of MPE zones. */
int getNumZones() const noexcept;
/** Returns a pointer to the MPE zone at the given index,
or nullptr if there is no such zone.
/** Returns a pointer to the MPE zone at the given index, or nullptr if there
is no such zone. Zones are sorted by insertion order (most recently added
zone last).
*/
MPEZone* getZoneByIndex (int index) const noexcept;


+ 50
- 5
source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp View File

@@ -26,7 +26,8 @@ BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s,
TimeSliceThread& thread,
const bool deleteSourceWhenDeleted,
const int bufferSizeSamples,
const int numChannels)
const int numChannels,
bool prefillBufferOnPrepareToPlay)
: source (s, deleteSourceWhenDeleted),
backgroundThread (thread),
numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)),
@@ -36,7 +37,8 @@ BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s,
nextPlayPos (0),
sampleRate (0),
wasSourceLooping (false),
isPrepared (false)
isPrepared (false),
prefillBuffer (prefillBufferOnPrepareToPlay)
{
jassert (source != nullptr);
@@ -73,12 +75,13 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double ne
backgroundThread.addTimeSliceClient (this);
while (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4,
buffer.getNumSamples() / 2))
do
{
backgroundThread.moveToFrontOfQueue (this);
Thread::sleep (5);
}
while (prefillBuffer
&& (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2)));
}
}
@@ -88,7 +91,12 @@ void BufferingAudioSource::releaseResources()
backgroundThread.removeTimeSliceClient (this);
buffer.setSize (numberOfChannels, 0);
source->releaseResources();
// MSVC2015 seems to need this if statement to not generate a warning during linking.
// As source is set in the constructor, there is no way that source could
// ever equal this, but it seems to make MSVC2015 happy.
if (source != this)
source->releaseResources();
}
void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info)
@@ -148,6 +156,41 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info
}
}
bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout)
{
if (!source || source->getTotalLength() <= 0)
return false;
if (nextPlayPos + info.numSamples < 0)
return true;
if (! isLooping() && nextPlayPos > getTotalLength())
return true;
const uint32 endTime = Time::getMillisecondCounter() + timeout;
uint32 now = Time::getMillisecondCounter();
while (now < endTime)
{
{
const ScopedLock sl (bufferStartPosLock);
const int validStart = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos);
const int validEnd = static_cast<int> (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos);
if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples)
return true;
}
if (! bufferReadyEvent.wait (static_cast<int> (endTime - now)))
return false;
now = Time::getMillisecondCounter();
}
return false;
}
int64 BufferingAudioSource::getNextReadPosition() const
{
jassert (source->getTotalLength() > 0);
@@ -241,6 +284,8 @@ bool BufferingAudioSource::readNextBufferChunk()
bufferValidEnd = newBVE;
}
bufferReadyEvent.signal();
return true;
}


+ 21
- 11
source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h View File

@@ -43,21 +43,24 @@ public:
//==============================================================================
/** Creates a BufferingAudioSource.
@param source the input source to read from
@param backgroundThread a background thread that will be used for the
background read-ahead. This object must not be deleted
until after any BufferingAudioSources that are using it
have been deleted!
@param deleteSourceWhenDeleted if true, then the input source object will
be deleted when this object is deleted
@param numberOfSamplesToBuffer the size of buffer to use for reading ahead
@param numberOfChannels the number of channels that will be played
@param source the input source to read from
@param backgroundThread a background thread that will be used for the
background read-ahead. This object must not be deleted
until after any BufferingAudioSources that are using it
have been deleted!
@param deleteSourceWhenDeleted if true, then the input source object will
be deleted when this object is deleted
@param numberOfSamplesToBuffer the size of buffer to use for reading ahead
@param numberOfChannels the number of channels that will be played
@param prefillBufferOnPrepareToPlay if true, then calling prepareToPlay on this object will
block until the buffer has been filled
*/
BufferingAudioSource (PositionableAudioSource* source,
TimeSliceThread& backgroundThread,
bool deleteSourceWhenDeleted,
int numberOfSamplesToBuffer,
int numberOfChannels = 2);
int numberOfChannels = 2,
bool prefillBufferOnPrepareToPlay = true);
/** Destructor.
@@ -89,6 +92,12 @@ public:
/** Implements the PositionableAudioSource method. */
bool isLooping() const override { return source->isLooping(); }
/** A useful function to block until the next the buffer info can be filled.
This is useful for offline rendering.
*/
bool waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout);
private:
//==============================================================================
OptionalScopedPointer<PositionableAudioSource> source;
@@ -96,9 +105,10 @@ private:
int numberOfSamplesToBuffer, numberOfChannels;
AudioSampleBuffer buffer;
CriticalSection bufferStartPosLock;
WaitableEvent bufferReadyEvent;
int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos;
double volatile sampleRate;
bool wasSourceLooping, isPrepared;
bool wasSourceLooping, isPrepared, prefillBuffer;
bool readNextBufferChunk();
void readBufferSection (int64 start, int length, int bufferOffset);


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

@@ -30,7 +30,7 @@
/**
A type of AudioSource that takes an input source and changes its sample rate.
@see AudioSource
@see AudioSource, LagrangeInterpolator, CatmullRomInterpolator
*/
class JUCE_API ResamplingAudioSource : public AudioSource
{


+ 17
- 5
source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp View File

@@ -88,6 +88,7 @@ Synthesiser::Synthesiser()
: sampleRate (0),
lastNoteOnCounter (0),
minimumSubBlockSize (32),
subBlockSubdivisionIsStrict (false),
shouldStealNotes (true)
{
for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i)
@@ -147,10 +148,11 @@ void Synthesiser::setNoteStealingEnabled (const bool shouldSteal)
shouldStealNotes = shouldSteal;
}
void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples) noexcept
void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept
{
jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1
minimumSubBlockSize = numSamples;
subBlockSubdivisionIsStrict = shouldBeStrict;
}
//==============================================================================
@@ -177,10 +179,12 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
{
// must set the sample rate before using this!
jassert (sampleRate != 0);
const int targetChannels = outputAudio.getNumChannels();
MidiBuffer::Iterator midiIterator (midiData);
midiIterator.setNextSamplePosition (startSample);
bool firstEvent = true;
int midiEventPos;
MidiMessage m;
@@ -190,7 +194,9 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
{
if (! midiIterator.getNextEvent (m, midiEventPos))
{
renderVoices (outputAudio, startSample, numSamples);
if (targetChannels > 0)
renderVoices (outputAudio, startSample, numSamples);
return;
}
@@ -198,18 +204,24 @@ void Synthesiser::processNextBlock (AudioBuffer<floatType>& outputAudio,
if (samplesToNextMidiMessage >= numSamples)
{
renderVoices (outputAudio, startSample, numSamples);
if (targetChannels > 0)
renderVoices (outputAudio, startSample, numSamples);
handleMidiEvent (m);
break;
}
if (samplesToNextMidiMessage < minimumSubBlockSize)
if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize))
{
handleMidiEvent (m);
continue;
}
renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
firstEvent = false;
if (targetChannels > 0)
renderVoices (outputAudio, startSample, samplesToNextMidiMessage);
handleMidiEvent (m);
startSample += samplesToNextMidiMessage;
numSamples -= samplesToNextMidiMessage;


+ 8
- 1
source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h View File

@@ -539,8 +539,14 @@ public:
The default setting is 32, which means that midi messages are accurate to about < 1ms
accuracy, which is probably fine for most purposes, but you may want to increase or
decrease this value for your synth.
If shouldBeStrict is true, the audio sub-blocks will strictly never be smaller than numSamples.
If shouldBeStrict is false (default), the first audio sub-block in the buffer is allowed
to be smaller, to make sure that the first MIDI event in a buffer will always be sample-accurate
(this can sometimes help to avoid quantisation or phasing issues).
*/
void setMinimumRenderingSubdivisionSize (int numSamples) noexcept;
void setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict = false) noexcept;
protected:
//==============================================================================
@@ -615,6 +621,7 @@ private:
double sampleRate;
uint32 lastNoteOnCounter;
int minimumSubBlockSize;
bool subBlockSubdivisionIsStrict;
bool shouldStealNotes;
BigInteger sustainPedalsDown;


+ 72
- 226
source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp View File

@@ -86,72 +86,12 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
};
//==============================================================================
// This is an AudioTransportSource which will own it's assigned source
struct AudioSourceOwningTransportSource : public AudioTransportSource
{
AudioSourceOwningTransportSource (PositionableAudioSource* s) : source (s)
{
AudioTransportSource::setSource (s);
}
~AudioSourceOwningTransportSource()
{
setSource (nullptr);
}
private:
ScopedPointer<PositionableAudioSource> source;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource)
};
//==============================================================================
// An AudioSourcePlayer which will remove itself from the AudioDeviceManager's
// callback list once it finishes playing its source
struct AutoRemovingSourcePlayer : public AudioSourcePlayer,
private Timer
{
AutoRemovingSourcePlayer (AudioDeviceManager& dm, AudioTransportSource* ts, bool ownSource)
: manager (dm), transportSource (ts, ownSource)
{
jassert (ts != nullptr);
manager.addAudioCallback (this);
AudioSourcePlayer::setSource (transportSource);
startTimerHz (10);
}
~AutoRemovingSourcePlayer()
{
setSource (nullptr);
manager.removeAudioCallback (this);
}
void timerCallback() override
{
if (getCurrentSource() == nullptr || ! transportSource->isPlaying())
delete this;
}
void audioDeviceStopped() override
{
AudioSourcePlayer::audioDeviceStopped();
setSource (nullptr);
}
private:
AudioDeviceManager& manager;
OptionalScopedPointer<AudioTransportSource> transportSource;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer)
};
//==============================================================================
AudioDeviceManager::AudioDeviceManager()
: numInputChansNeeded (0),
numOutputChansNeeded (2),
listNeedsScanning (true),
inputLevel (0),
testSoundPosition (0),
cpuUsageMs (0),
timeToCpuScale (0)
{
@@ -162,10 +102,6 @@ AudioDeviceManager::~AudioDeviceManager()
{
currentAudioDevice = nullptr;
defaultMidiOutput = nullptr;
for (int i = 0; i < callbacks.size(); ++i)
if (AutoRemovingSourcePlayer* p = dynamic_cast<AutoRemovingSourcePlayer*> (callbacks.getUnchecked(i)))
delete p;
}
//==============================================================================
@@ -346,8 +282,8 @@ String AudioDeviceManager::initialiseFromXML (const XmlElement& xml,
currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName();
}
setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize");
setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate");
setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize", setup.bufferSize);
setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate", setup.sampleRate);
setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2);
setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2);
@@ -650,6 +586,8 @@ void AudioDeviceManager::stopDevice()
{
if (currentAudioDevice != nullptr)
currentAudioDevice->stop();
testSound = nullptr;
}
void AudioDeviceManager::closeAudioDevice()
@@ -761,31 +699,8 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
{
const ScopedLock sl (audioCallbackLock);
if (inputLevelMeasurementEnabledCount.get() > 0 && numInputChannels > 0)
{
for (int j = 0; j < numSamples; ++j)
{
float s = 0;
for (int i = 0; i < numInputChannels; ++i)
s += std::abs (inputChannelData[i][j]);
s /= numInputChannels;
const double decayFactor = 0.99992;
if (s > inputLevel)
inputLevel = s;
else if (inputLevel > 0.001f)
inputLevel *= decayFactor;
else
inputLevel = 0;
}
}
else
{
inputLevel = 0;
}
inputLevelMeter.updateLevel (inputChannelData, numInputChannels, numSamples);
outputLevelMeter.updateLevel (const_cast<const float**> (outputChannelData), numOutputChannels, numSamples);
if (callbacks.size() > 0)
{
@@ -821,6 +736,20 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
for (int i = 0; i < numOutputChannels; ++i)
zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples);
}
if (testSound != nullptr)
{
const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
const float* const src = testSound->getReadPointer (0, testSoundPosition);
for (int i = 0; i < numOutputChannels; ++i)
for (int j = 0; j < numSamps; ++j)
outputChannelData [i][j] += src[j];
testSoundPosition += numSamps;
if (testSoundPosition >= testSound->getNumSamples())
testSound = nullptr;
}
}
void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
@@ -989,170 +918,87 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
}
//==============================================================================
// An AudioSource which simply outputs a buffer
class AudioSampleBufferSource : public PositionableAudioSource
{
public:
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer, bool playOnAllChannels)
: buffer (audioBuffer, ownBuffer),
position (0), looping (false), playAcrossAllChannels (playOnAllChannels)
{}
AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {}
//==============================================================================
void setNextReadPosition (int64 newPosition) override
{
jassert (newPosition >= 0);
if (looping)
newPosition = newPosition % static_cast<int64> (buffer->getNumSamples());
position = jmin (buffer->getNumSamples(), static_cast<int> (newPosition));
}
int64 getNextReadPosition() const override { return static_cast<int64> (position); }
int64 getTotalLength() const override { return static_cast<int64> (buffer->getNumSamples()); }
bool isLooping() const override { return looping; }
void setLooping (bool shouldLoop) override { looping = shouldLoop; }
//==============================================================================
void prepareToPlay (int, double) override {}
void releaseResources() override {}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
void AudioDeviceManager::LevelMeter::updateLevel (const float* const* channelData, int numChannels, int numSamples) noexcept
{
if (enabled.get() != 0 && numChannels > 0)
{
bufferToFill.clearActiveBufferRegion();
const int bufferSize = buffer->getNumSamples();
const int samplesNeeded = bufferToFill.numSamples;
const int samplesToCopy = jmin (bufferSize - position, samplesNeeded);
if (samplesToCopy > 0)
for (int j = 0; j < numSamples; ++j)
{
int maxInChannels = buffer->getNumChannels();
int maxOutChannels = bufferToFill.buffer->getNumChannels();
if (! playAcrossAllChannels)
maxOutChannels = jmin (maxOutChannels, maxInChannels);
for (int i = 0; i < maxOutChannels; ++i)
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer,
i % maxInChannels, position, samplesToCopy);
}
position += samplesNeeded;
float s = 0;
if (looping)
position %= bufferSize;
}
for (int i = 0; i < numChannels; ++i)
s += std::abs (channelData[i][j]);
private:
//==============================================================================
OptionalScopedPointer<AudioSampleBuffer> buffer;
int position;
bool looping, playAcrossAllChannels;
s /= numChannels;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource)
};
void AudioDeviceManager::playSound (const File& file)
{
if (file.existsAsFile())
{
AudioFormatManager formatManager;
const double decayFactor = 0.99992;
formatManager.registerBasicFormats();
playSound (formatManager.createReaderFor (file), true);
if (s > level)
level = s;
else if (level > 0.001f)
level *= decayFactor;
else
level = 0;
}
}
}
void AudioDeviceManager::playSound (const void* resourceData, size_t resourceSize)
{
if (resourceData != nullptr && resourceSize > 0)
else
{
AudioFormatManager formatManager;
formatManager.registerBasicFormats();
MemoryInputStream* mem = new MemoryInputStream (resourceData, resourceSize, false);
playSound (formatManager.createReaderFor (mem), true);
level = 0;
}
}
void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFinished)
void AudioDeviceManager::LevelMeter::setEnabled (bool shouldBeEnabled) noexcept
{
if (reader != nullptr)
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true);
enabled.set (shouldBeEnabled ? 1 : 0);
level = 0;
}
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished, bool playOnAllOutputChannels)
double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept
{
if (buffer != nullptr)
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished, playOnAllOutputChannels), true);
jassert (enabled.get() != 0); // you need to call setEnabled (true) before using this!
return level;
}
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished)
void AudioDeviceManager::playTestSound()
{
if (audioSource != nullptr && currentAudioDevice != nullptr)
{
AudioTransportSource* transport = dynamic_cast<AudioTransportSource*> (audioSource);
{ // cunningly nested to swap, unlock and delete in that order.
ScopedPointer<AudioSampleBuffer> oldSound;
if (transport == nullptr)
{
if (deleteWhenFinished)
{
transport = new AudioSourceOwningTransportSource (audioSource);
}
else
{
transport = new AudioTransportSource();
transport->setSource (audioSource);
deleteWhenFinished = true;
}
const ScopedLock sl (audioCallbackLock);
oldSound = testSound;
}
transport->start();
new AutoRemovingSourcePlayer (*this, transport, deleteWhenFinished);
}
else
{
if (deleteWhenFinished)
delete audioSource;
}
}
void AudioDeviceManager::playTestSound()
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
const int soundLength = (int) sampleRate;
testSoundPosition = 0;
const double frequency = 440.0;
const float amplitude = 0.5f;
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
if (currentAudioDevice != nullptr)
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
const int soundLength = (int) sampleRate;
AudioSampleBuffer* newSound = new AudioSampleBuffer (1, soundLength);
const double frequency = 440.0;
const float amplitude = 0.5f;
for (int i = 0; i < soundLength; ++i)
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength);
playSound (newSound, true, true);
}
for (int i = 0; i < soundLength; ++i)
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
//==============================================================================
void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement)
{
if (enableMeasurement)
++inputLevelMeasurementEnabledCount;
else
--inputLevelMeasurementEnabledCount;
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
inputLevel = 0;
const ScopedLock sl (audioCallbackLock);
testSound = newSound;
}
}
double AudioDeviceManager::getCurrentInputLevel() const
{
jassert (inputLevelMeasurementEnabledCount.get() > 0); // you need to call enableInputLevelMeasurement() before using this!
return inputLevel;
}
double AudioDeviceManager::getCurrentInputLevel() const noexcept { return inputLevelMeter.getCurrentLevel(); }
double AudioDeviceManager::getCurrentOutputLevel() const noexcept { return outputLevelMeter.getCurrentLevel(); }
void AudioDeviceManager::enableInputLevelMeasurement (bool enable) noexcept { inputLevelMeter.setEnabled (enable); }
void AudioDeviceManager::enableOutputLevelMeasurement (bool enable) noexcept { outputLevelMeter.setEnabled (enable); }

+ 31
- 67
source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h View File

@@ -404,78 +404,28 @@ public:
*/
void playTestSound();
/** Plays a sound from a file. */
void playSound (const File& file);
/** Convenient method to play sound from a JUCE resource. */
void playSound (const void* resourceData, size_t resourceSize);
/** Plays the sound from an audio format reader.
If deleteWhenFinished is true then the format reader will be
automatically deleted once the sound has finished playing.
*/
void playSound (AudioFormatReader* buffer, bool deleteWhenFinished = false);
/** Plays the sound from a positionable audio source.
This will output the sound coming from a positionable audio source.
This gives you slightly more control over the sound playback compared
to the other playSound methods. For example, if you would like to
stop the sound prematurely you can call this method with a
TransportAudioSource and then call audioSource->stop. Note that,
you must call audioSource->start to start the playback, if your
audioSource is a TransportAudioSource.
The audio device manager will not hold any references to this audio
source once the audio source has stopped playing for any reason,
for example when the sound has finished playing or when you have
called audioSource->stop. Therefore, calling audioSource->start() on
a finished audioSource will not restart the sound again. If this is
desired simply call playSound with the same audioSource again.
@param audioSource the audio source to play
@param deleteWhenFinished If this is true then the audio source will
be deleted once the device manager has finished playing.
*/
void playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished = false);
/** Plays the sound from an audio sample buffer.
This will output the sound contained in an audio sample buffer. If
deleteWhenFinished is true then the audio sample buffer will be
automatically deleted once the sound has finished playing.
If playOnAllOutputChannels is true, then if there are more output channels
than buffer channels, then the ones that are available will be re-used on
multiple outputs so that something is sent to all output channels. If it
is false, then the buffer will just be played on the first output channels.
*/
void playSound (AudioSampleBuffer* buffer,
bool deleteWhenFinished = false,
bool playOnAllOutputChannels = false);
//==============================================================================
/** Turns on level-measuring.
When enabled, the device manager will measure the peak input level
across all channels, and you can get this level by calling getCurrentInputLevel().
This is mainly intended for audio setup UI panels to use to create a mic
level display, so that the user can check that they've selected the right
device.
/** Turns on level-measuring for input channels.
@see getCurrentInputLevel()
*/
void enableInputLevelMeasurement (bool enableMeasurement) noexcept;
A simple filter is used to make the level decay smoothly, but this is
only intended for giving rough feedback, and not for any kind of accurate
measurement.
/** Turns on level-measuring for output channels.
@see getCurrentOutputLevel()
*/
void enableInputLevelMeasurement (bool enableMeasurement);
void enableOutputLevelMeasurement (bool enableMeasurement) noexcept;
/** Returns the current input level.
To use this, you must first enable it by calling enableInputLevelMeasurement().
See enableInputLevelMeasurement() for more info.
@see enableInputLevelMeasurement()
*/
double getCurrentInputLevel() const;
double getCurrentInputLevel() const noexcept;
/** Returns the current output level.
To use this, you must first enable it by calling enableOutputLevelMeasurement().
@see enableOutputLevelMeasurement()
*/
double getCurrentOutputLevel() const noexcept;
/** Returns the a lock that can be used to synchronise access to the audio callback.
Obviously while this is locked, you're blocking the audio thread from running, so
@@ -502,8 +452,6 @@ private:
BigInteger inputChannels, outputChannels;
ScopedPointer<XmlElement> lastExplicitSettings;
mutable bool listNeedsScanning;
Atomic<int> inputLevelMeasurementEnabledCount;
double inputLevel;
AudioSampleBuffer tempBuffer;
struct MidiCallbackInfo
@@ -520,8 +468,24 @@ private:
ScopedPointer<MidiOutput> defaultMidiOutput;
CriticalSection audioCallbackLock, midiCallbackLock;
ScopedPointer<AudioSampleBuffer> testSound;
int testSoundPosition;
double cpuUsageMs, timeToCpuScale;
struct LevelMeter
{
LevelMeter() noexcept;
void updateLevel (const float* const*, int numChannels, int numSamples) noexcept;
void setEnabled (bool) noexcept;
double getCurrentLevel() const noexcept;
Atomic<int> enabled;
double level;
};
LevelMeter inputLevelMeter, outputLevelMeter;
//==============================================================================
class CallbackHandler;
friend class CallbackHandler;


+ 7
- 32
source/modules/juce_audio_devices/juce_audio_devices.cpp View File

@@ -31,6 +31,8 @@
#error "Incorrect use of JUCE cpp file"
#endif
#include "AppConfig.h"
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
@@ -45,7 +47,6 @@
#define Component CarbonDummyCompName
#import <CoreAudio/AudioHardware.h>
#import <CoreMIDI/MIDIServices.h>
#import <DiscRecording/DiscRecording.h>
#import <AudioToolbox/AudioServices.h>
#undef Point
#undef Component
@@ -55,10 +56,14 @@
#import <AVFoundation/AVFoundation.h>
#import <CoreMIDI/MIDIServices.h>
#if TARGET_OS_SIMULATOR
#import <CoreMIDI/MIDINetworkSession.h>
#endif
//==============================================================================
#elif JUCE_WINDOWS
#if JUCE_WASAPI
#include <MMReg.h>
#include <mmreg.h>
#endif
#if JUCE_ASIO
@@ -84,15 +89,6 @@
#include <iasiodrv.h>
#endif
#if JUCE_USE_CDBURNER
/* You'll need the Platform SDK for these headers - if you don't have it and don't
need to use CD-burning, then you might just want to set the JUCE_USE_CDBURNER flag
to 0, to avoid these includes.
*/
#include <imapi.h>
#include <imapierror.h>
#endif
//==============================================================================
#elif JUCE_LINUX
#if JUCE_ALSA
@@ -139,7 +135,6 @@ namespace juce
#include "audio_io/juce_AudioIODeviceType.cpp"
#include "midi_io/juce_MidiMessageCollector.cpp"
#include "midi_io/juce_MidiOutput.cpp"
#include "audio_cd/juce_AudioCDReader.cpp"
#include "sources/juce_AudioSourcePlayer.cpp"
#include "sources/juce_AudioTransportSource.cpp"
#include "native/juce_MidiDataConcatenator.h"
@@ -149,14 +144,6 @@ namespace juce
#include "native/juce_mac_CoreAudio.cpp"
#include "native/juce_mac_CoreMidi.cpp"
#if JUCE_USE_CDREADER
#include "native/juce_mac_AudioCDReader.mm"
#endif
#if JUCE_USE_CDBURNER
#include "native/juce_mac_AudioCDBurner.mm"
#endif
//==============================================================================
#elif JUCE_IOS
#include "native/juce_ios_Audio.cpp"
@@ -179,14 +166,6 @@ namespace juce
#include "native/juce_win32_ASIO.cpp"
#endif
#if JUCE_USE_CDREADER
#include "native/juce_win32_AudioCDReader.cpp"
#endif
#if JUCE_USE_CDBURNER
#include "native/juce_win32_AudioCDBurner.cpp"
#endif
//==============================================================================
#elif JUCE_LINUX
#if JUCE_ALSA
@@ -199,10 +178,6 @@ namespace juce
#include "native/juce_linux_JackAudio.cpp"
#endif
#if JUCE_USE_CDREADER
#include "native/juce_linux_AudioCDReader.cpp"
#endif
//==============================================================================
#elif JUCE_ANDROID
#include "native/juce_android_Audio.cpp"


+ 30
- 20
source/modules/juce_audio_devices/juce_audio_devices.h View File

@@ -22,12 +22,39 @@
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.txt file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_audio_devices
vendor: juce
version: 4.3.0
name: JUCE audio and MIDI I/O device classes
description: Classes to play and record from audio and MIDI I/O devices
website: http://www.juce.com/juce
license: GPL/Commercial
dependencies: juce_audio_basics, juce_events
OSXFrameworks: CoreAudio CoreMIDI AudioToolbox
iOSFrameworks: CoreAudio CoreMIDI AudioToolbox AVFoundation
linuxPackages: alsa
mingwLibs: winmm
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#ifndef JUCE_AUDIO_DEVICES_H_INCLUDED
#define JUCE_AUDIO_DEVICES_H_INCLUDED
#include "../juce_events/juce_events.h"
#include "../juce_audio_basics/juce_audio_basics.h"
#include "../juce_audio_formats/juce_audio_formats.h"
#include <juce_events/juce_events.h>
#include <juce_audio_basics/juce_audio_basics.h>
//==============================================================================
/** Config: JUCE_ASIO
@@ -90,21 +117,6 @@
#endif
#endif
//==============================================================================
/** Config: JUCE_USE_CDREADER
Enables the AudioCDReader class (on supported platforms).
*/
#ifndef JUCE_USE_CDREADER
#define JUCE_USE_CDREADER 0
#endif
/** Config: JUCE_USE_CDBURNER
Enables the AudioCDBurner class (on supported platforms).
*/
#ifndef JUCE_USE_CDBURNER
#define JUCE_USE_CDBURNER 0
#endif
//==============================================================================
namespace juce
{
@@ -117,8 +129,6 @@ namespace juce
#include "midi_io/juce_MidiOutput.h"
#include "sources/juce_AudioSourcePlayer.h"
#include "sources/juce_AudioTransportSource.h"
#include "audio_cd/juce_AudioCDBurner.h"
#include "audio_cd/juce_AudioCDReader.h"
#include "audio_io/juce_AudioDeviceManager.h"
}


+ 10
- 3
source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h View File

@@ -71,8 +71,7 @@ public:
// the normal message, handle it now..
if (*d >= 0xf8 && *d <= 0xfe)
{
const MidiMessage m (*d++, time);
callback.handleIncomingMidiMessage (input, m);
callback.handleIncomingMidiMessage (input, MidiMessage (*d++, time));
--numBytes;
}
else
@@ -83,7 +82,15 @@ public:
data[len++] = *d++;
--numBytes;
if (len >= MidiMessage::getMessageLengthFromFirstByte (data[0]))
const uint8 firstByte = data[0];
if (firstByte < 0x80 || firstByte == 0xf7)
{
len = 0;
break; // ignore this malformed MIDI message..
}
if (len >= MidiMessage::getMessageLengthFromFirstByte (firstByte))
break;
}
}


+ 1
- 1
source/modules/juce_audio_devices/native/juce_android_Midi.cpp View File

@@ -153,7 +153,7 @@ JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceM
class AndroidMidiDeviceManager
{
public:
AndroidMidiDeviceManager ()
AndroidMidiDeviceManager()
: deviceManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidMidiDeviceManager))
{
}


+ 2
- 2
source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp View File

@@ -332,7 +332,7 @@ private:
void run() override
{
setThreadToAudioPriority ();
setThreadToAudioPriority();
if (recorder != nullptr) recorder->start();
if (player != nullptr) player->start();
@@ -341,7 +341,7 @@ private:
processBuffers();
}
void setThreadToAudioPriority ()
void setThreadToAudioPriority()
{
// see android.os.Process.THREAD_PRIORITY_AUDIO
const int THREAD_PRIORITY_AUDIO = -16;


+ 48
- 5
source/modules/juce_audio_devices/native/juce_ios_Audio.cpp View File

@@ -287,7 +287,14 @@ public:
return r;
}
int getDefaultBufferSize() override { return 256; }
int getDefaultBufferSize() override
{
#if TARGET_IPHONE_SIMULATOR
return 512;
#else
return 256;
#endif
}
String open (const BigInteger& inputChannelsWanted,
const BigInteger& outputChannelsWanted,
@@ -431,6 +438,8 @@ public:
void handleStatusChange (bool enabled, const char* reason)
{
const ScopedLock myScopedLock (callbackLock);
JUCE_IOS_AUDIO_LOG ("handleStatusChange: enabled: " << (int) enabled << ", reason: " << reason);
isRunning = enabled;
@@ -447,6 +456,8 @@ public:
void handleRouteChange (const char* reason)
{
const ScopedLock myScopedLock (callbackLock);
JUCE_IOS_AUDIO_LOG ("handleRouteChange: reason: " << reason);
fixAudioRouteIfSetToReceiver();
@@ -518,9 +529,9 @@ private:
if (audioInputIsAvailable && numInputChannels > 0)
err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data);
const ScopedLock sl (callbackLock);
const ScopedTryLock stl (callbackLock);
if (callback != nullptr)
if (stl.isLocked() && callback != nullptr)
{
if ((int) numFrames > floatData.getNumSamples())
prepareFloatBuffers ((int) numFrames);
@@ -679,7 +690,23 @@ private:
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format));
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format));
UInt32 framesPerSlice;
UInt32 dataSize = sizeof (framesPerSlice);
AudioUnitInitialize (audioUnit);
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global, 0, &actualBufferSize, sizeof (actualBufferSize));
if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global, 0, &framesPerSlice, &dataSize) == noErr
&& dataSize == sizeof (framesPerSlice) && static_cast<int> (framesPerSlice) != actualBufferSize)
{
actualBufferSize = static_cast<int> (framesPerSlice);
prepareFloatBuffers (actualBufferSize);
}
return true;
}
@@ -755,8 +782,24 @@ void AudioSessionHolder::handleStatusChange (bool enabled, const char* reason) c
void AudioSessionHolder::handleRouteChange (const char* reason) const
{
for (auto device: activeDevices)
device->handleRouteChange (reason);
struct RouteChangeMessage : public CallbackMessage
{
RouteChangeMessage (Array<iOSAudioIODevice*> devs, const char* r)
: devices (devs), changeReason (r)
{
}
void messageCallback() override
{
for (auto device: devices)
device->handleRouteChange (changeReason);
}
Array<iOSAudioIODevice*> devices;
const char* changeReason;
};
(new RouteChangeMessage (activeDevices, reason))->post();
}
#undef JUCE_NSERROR_CHECK

+ 3
- 1
source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp View File

@@ -183,7 +183,7 @@ public:
// open output ports
const StringArray outputChannels (getOutputChannelNames());
for (int i = 0; i < outputChannels.size (); ++i)
for (int i = 0; i < outputChannels.size(); ++i)
{
String outputName;
outputName << "out_" << ++totalNumberOfOutputChannels;
@@ -290,6 +290,8 @@ public:
}
}
updateActivePorts();
return lastError;
}


+ 93
- 77
source/modules/juce_audio_devices/native/juce_linux_Midi.cpp View File

@@ -45,37 +45,17 @@ class AlsaClient : public ReferenceCountedObject
public:
typedef ReferenceCountedObjectPtr<AlsaClient> Ptr;
AlsaClient (bool forInput)
: input (forInput), handle (nullptr)
static Ptr getInstance (bool forInput)
{
snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT
: SND_SEQ_OPEN_OUTPUT, 0);
}
~AlsaClient()
{
if (handle != nullptr)
{
snd_seq_close (handle);
handle = nullptr;
}
jassert (activeCallbacks.size() == 0);
AlsaClient*& instance = (forInput ? inInstance : outInstance);
if (instance == nullptr)
instance = new AlsaClient (forInput);
if (inputThread)
{
inputThread->stopThread (3000);
inputThread = nullptr;
}
return instance;
}
bool isInput() const noexcept { return input; }
void setName (const String& name)
{
snd_seq_set_client_name (handle, name.toUTF8());
}
void registerCallback (AlsaPortAndCallback* cb)
{
if (cb != nullptr)
@@ -103,7 +83,8 @@ public:
inputThread->signalThreadShouldExit();
}
void handleIncomingMidiMessage (const MidiMessage& message, int port);
void handleIncomingMidiMessage (snd_seq_event*, const MidiMessage&);
void handlePartialSysexMessage (snd_seq_event*, const uint8*, int, double);
snd_seq_t* get() const noexcept { return handle; }
@@ -114,12 +95,56 @@ private:
Array<AlsaPortAndCallback*> activeCallbacks;
CriticalSection callbackLock;
static AlsaClient* inInstance;
static AlsaClient* outInstance;
//==============================================================================
friend class ReferenceCountedObjectPtr<AlsaClient>;
friend struct ContainerDeletePolicy<AlsaClient>;
AlsaClient (bool forInput)
: input (forInput), handle (nullptr)
{
AlsaClient*& instance = (input ? inInstance : outInstance);
jassert (instance == nullptr);
instance = this;
snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT
: SND_SEQ_OPEN_OUTPUT, 0);
snd_seq_set_client_name (handle, forInput ? JUCE_ALSA_MIDI_INPUT_NAME
: JUCE_ALSA_MIDI_OUTPUT_NAME);
}
~AlsaClient()
{
AlsaClient*& instance = (input ? inInstance : outInstance);
jassert (instance != nullptr);
instance = nullptr;
if (handle != nullptr)
{
snd_seq_close (handle);
handle = nullptr;
}
jassert (activeCallbacks.size() == 0);
if (inputThread)
{
inputThread->stopThread (3000);
inputThread = nullptr;
}
}
//==============================================================================
class MidiInputThread : public Thread
{
public:
MidiInputThread (AlsaClient& c)
: Thread ("Juce MIDI Input"), client (c)
: Thread ("Juce MIDI Input"), client (c), concatenator (2048)
{
jassert (client.input && client.get() != nullptr);
}
@@ -159,13 +184,9 @@ private:
snd_midi_event_reset_decode (midiParser);
if (numBytes > 0)
{
const MidiMessage message ((const uint8*) buffer, (int) numBytes,
Time::getMillisecondCounter() * 0.001);
client.handleIncomingMidiMessage (message, inputEvent->dest.port);
}
concatenator.pushMidiData (buffer, (int) numBytes,
Time::getMillisecondCounter() * 0.001,
inputEvent, client);
snd_seq_free_event (inputEvent);
}
@@ -180,29 +201,14 @@ private:
private:
AlsaClient& client;
MidiDataConcatenator concatenator;
};
ScopedPointer<MidiInputThread> inputThread;
};
static AlsaClient::Ptr globalAlsaSequencerIn()
{
static AlsaClient::Ptr global (new AlsaClient (true));
return global;
}
static AlsaClient::Ptr globalAlsaSequencerOut()
{
static AlsaClient::Ptr global (new AlsaClient (false));
return global;
}
static AlsaClient::Ptr globalAlsaSequencer (bool input)
{
return input ? globalAlsaSequencerIn()
: globalAlsaSequencerOut();
}
AlsaClient* AlsaClient::inInstance = nullptr;
AlsaClient* AlsaClient::outInstance = nullptr;
//==============================================================================
// represents an input or output port of the supplied AlsaClient
@@ -282,6 +288,11 @@ public:
callback->handleIncomingMidiMessage (midiInput, message);
}
void handlePartialSysexMessage (const uint8* messageData, int numBytesSoFar, double timeStamp)
{
callback->handlePartialSysexMessage (midiInput, messageData, numBytesSoFar, timeStamp);
}
private:
AlsaPort port;
MidiInput* midiInput;
@@ -291,14 +302,22 @@ private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlsaPortAndCallback)
};
void AlsaClient::handleIncomingMidiMessage (const MidiMessage& message, int port)
void AlsaClient::handleIncomingMidiMessage (snd_seq_event_t* event, const MidiMessage& message)
{
const ScopedLock sl (callbackLock);
if (AlsaPortAndCallback* const cb = activeCallbacks[port])
if (AlsaPortAndCallback* const cb = activeCallbacks[event->dest.port])
cb->handleIncomingMidiMessage (message);
}
void AlsaClient::handlePartialSysexMessage (snd_seq_event* event, const uint8* messageData, int numBytesSoFar, double timeStamp)
{
const ScopedLock sl (callbackLock);
if (AlsaPortAndCallback* const cb = activeCallbacks[event->dest.port])
cb->handlePartialSysexMessage (messageData, numBytesSoFar, timeStamp);
}
//==============================================================================
static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq,
snd_seq_client_info_t* clientInfo,
@@ -325,19 +344,23 @@ static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq,
&& (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_READ
: SND_SEQ_PORT_CAP_WRITE)) != 0)
{
deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo));
const String clientName = snd_seq_client_info_get_name (clientInfo);
const String portName = snd_seq_port_info_get_name(portInfo);
if (clientName == portName)
deviceNamesFound.add (clientName);
else
deviceNamesFound.add (clientName + ": " + portName);
if (deviceNamesFound.size() == deviceIndexToOpen + 1)
{
const int sourcePort = snd_seq_port_info_get_port (portInfo);
const int sourceClient = snd_seq_client_info_get_client (clientInfo);
if (sourcePort != -1)
{
const String name (forInput ? JUCE_ALSA_MIDI_INPUT_NAME
: JUCE_ALSA_MIDI_OUTPUT_NAME);
seq->setName (name);
port.createPort (seq, name, forInput);
const int sourceClient = snd_seq_client_info_get_client (clientInfo);
port.createPort (seq, portName, forInput);
port.connectWith (sourceClient, sourcePort);
}
}
@@ -355,7 +378,7 @@ static AlsaPort iterateMidiDevices (const bool forInput,
const int deviceIndexToOpen)
{
AlsaPort port;
const AlsaClient::Ptr client (globalAlsaSequencer (forInput));
const AlsaClient::Ptr client (AlsaClient::getInstance (forInput));
if (snd_seq_t* const seqHandle = client->get())
{
@@ -387,19 +410,6 @@ static AlsaPort iterateMidiDevices (const bool forInput,
return port;
}
AlsaPort createMidiDevice (const bool forInput, const String& deviceNameToOpen)
{
AlsaPort port;
AlsaClient::Ptr client (new AlsaClient (forInput));
if (client->get())
{
client->setName (deviceNameToOpen + (forInput ? " Input" : " Output"));
port.createPort (client, forInput ? "in" : "out", forInput);
}
return port;
}
//==============================================================================
class MidiOutputDevice
@@ -450,7 +460,7 @@ public:
numBytes -= numSent;
data += numSent;
snd_seq_ev_set_source (&event, 0);
snd_seq_ev_set_source (&event, port.portId);
snd_seq_ev_set_subs (&event);
snd_seq_ev_set_direct (&event);
@@ -507,8 +517,11 @@ MidiOutput* MidiOutput::openDevice (int deviceIndex)
MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
{
MidiOutput* newDevice = nullptr;
AlsaPort port;
const AlsaClient::Ptr client (AlsaClient::getInstance (false));
AlsaPort port (createMidiDevice (false, deviceName));
port.createPort (client, deviceName, false);
if (port.isValid())
{
@@ -584,8 +597,11 @@ MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
{
MidiInput* newDevice = nullptr;
AlsaPort port;
const AlsaClient::Ptr client (AlsaClient::getInstance (true));
AlsaPort port (createMidiDevice (true, deviceName));
port.createPort (client, deviceName, true);
if (port.isValid())
{


+ 43
- 18
source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp View File

@@ -149,6 +149,7 @@ bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return SystemVol
struct CoreAudioClasses
{
class CoreAudioIODeviceType;
class CoreAudioIODevice;
//==============================================================================
@@ -691,7 +692,7 @@ public:
{ const ScopedLock sl (callbackLock); }
// wait until it's definately stopped calling back..
// wait until it's definitely stopped calling back..
for (int i = 40; --i >= 0;)
{
Thread::sleep (50);
@@ -847,6 +848,11 @@ private:
intern->deviceDetailsChanged();
break;
case kAudioObjectPropertyOwnedObjects:
intern->stop (false);
intern->owner.deviceType.triggerAsyncAudioDeviceListChange();
break;
case kAudioDevicePropertyBufferSizeRange:
case kAudioDevicePropertyVolumeScalar:
case kAudioDevicePropertyMute:
@@ -902,10 +908,12 @@ private:
class CoreAudioIODevice : public AudioIODevice
{
public:
CoreAudioIODevice (const String& deviceName,
CoreAudioIODevice (CoreAudioIODeviceType& dt,
const String& deviceName,
AudioDeviceID inputDeviceId, const int inputIndex_,
AudioDeviceID outputDeviceId, const int outputIndex_)
: AudioIODevice (deviceName, "CoreAudio"),
deviceType (dt),
inputIndex (inputIndex_),
outputIndex (outputIndex_),
isOpen_ (false),
@@ -1060,6 +1068,12 @@ public:
return lastError;
}
void audioDeviceListChanged()
{
deviceType.audioDeviceListChanged();
}
CoreAudioIODeviceType& deviceType;
int inputIndex, outputIndex;
private:
@@ -1745,7 +1759,8 @@ private:
//==============================================================================
class CoreAudioIODeviceType : public AudioIODeviceType
class CoreAudioIODeviceType : public AudioIODeviceType,
private AsyncUpdater
{
public:
CoreAudioIODeviceType()
@@ -1771,7 +1786,7 @@ public:
}
//==============================================================================
void scanForDevices()
void scanForDevices() override
{
hasScanned = true;
@@ -1827,7 +1842,7 @@ public:
outputDeviceNames.appendNumbersToDuplicates (false, true);
}
StringArray getDeviceNames (bool wantInputNames) const
StringArray getDeviceNames (bool wantInputNames) const override
{
jassert (hasScanned); // need to call scanForDevices() before doing this
@@ -1835,7 +1850,7 @@ public:
: outputDeviceNames;
}
int getDefaultDeviceIndex (bool forInput) const
int getDefaultDeviceIndex (bool forInput) const override
{
jassert (hasScanned); // need to call scanForDevices() before doing this
@@ -1870,7 +1885,7 @@ public:
return 0;
}
int getIndexOfDevice (AudioIODevice* device, bool asInput) const
int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
{
jassert (hasScanned); // need to call scanForDevices() before doing this
@@ -1894,10 +1909,10 @@ public:
return -1;
}
bool hasSeparateInputsAndOutputs() const { return true; }
bool hasSeparateInputsAndOutputs() const override { return true; }
AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName)
const String& inputDeviceName) override
{
jassert (hasScanned); // need to call scanForDevices() before doing this
@@ -1913,15 +1928,15 @@ public:
String combinedName (outputDeviceName.isEmpty() ? inputDeviceName : outputDeviceName);
if (inputDeviceID == outputDeviceID)
return new CoreAudioIODevice (combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex);
return new CoreAudioIODevice (*this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex);
ScopedPointer<CoreAudioIODevice> in, out;
if (inputDeviceID != 0)
in = new CoreAudioIODevice (inputDeviceName, inputDeviceID, inputIndex, 0, -1);
in = new CoreAudioIODevice (*this, inputDeviceName, inputDeviceID, inputIndex, 0, -1);
if (outputDeviceID != 0)
out = new CoreAudioIODevice (outputDeviceName, 0, -1, outputDeviceID, outputIndex);
out = new CoreAudioIODevice (*this, outputDeviceName, 0, -1, outputDeviceID, outputIndex);
if (in == nullptr) return out.release();
if (out == nullptr) return in.release();
@@ -1932,6 +1947,17 @@ public:
return combo.release();
}
void audioDeviceListChanged()
{
scanForDevices();
callDeviceChangeListeners();
}
void triggerAsyncAudioDeviceListChange()
{
triggerAsyncUpdate();
}
//==============================================================================
private:
StringArray inputDeviceNames, outputDeviceNames;
@@ -1969,18 +1995,17 @@ private:
return total;
}
void audioDeviceListChanged()
{
scanForDevices();
callDeviceChangeListeners();
}
static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData)
{
static_cast<CoreAudioIODeviceType*> (clientData)->audioDeviceListChanged();
return noErr;
}
void handleAsyncUpdate() override
{
audioDeviceListChanged();
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType)
};


+ 12
- 0
source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp View File

@@ -171,6 +171,10 @@ namespace CoreMidiHelpers
static StringArray findDevices (const bool forInput)
{
// It seems that OSX can be a bit picky about the thread that's first used to
// search for devices. It's safest to use the message thread for calling this.
jassert (MessageManager::getInstance()->isThisTheMessageThread());
const ItemCount num = forInput ? MIDIGetNumberOfSources()
: MIDIGetNumberOfDestinations();
StringArray s;
@@ -216,6 +220,14 @@ namespace CoreMidiHelpers
// correctly when called from the message thread!
jassert (MessageManager::getInstance()->isThisTheMessageThread());
#if TARGET_OS_SIMULATOR
// Enable MIDI for iOS simulator
MIDINetworkSession* session = [MIDINetworkSession defaultSession];
session.enabled = YES;
session.connectionPolicy = MIDINetworkConnectionPolicy_Anyone;
#endif
CoreMidiHelpers::ScopedCFString name;
name.cfString = getGlobalMidiClientName().toCFString();
CHECK_ERROR (MIDIClientCreate (name.cfString, &globalSystemChangeCallback, nullptr, &globalMidiClient));


+ 4
- 3
source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp View File

@@ -365,17 +365,18 @@ public:
void updateSampleRates()
{
// find a list of sample rates..
const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
Array<double> newRates;
if (asioObject != nullptr)
{
const int possibleSampleRates[] = { 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
for (int index = 0; index < numElementsInArray (possibleSampleRates); ++index)
if (asioObject->canSampleRate ((double) possibleSampleRates[index]) == 0)
newRates.add ((double) possibleSampleRates[index]);
}
if (newRates.size() == 0)
if (newRates.isEmpty())
{
double cr = getSampleRate();
JUCE_ASIO_LOG ("No sample rates supported - current rate: " + String ((int) cr));
@@ -1571,7 +1572,7 @@ private:
DWORD dsize = sizeof (pathName);
if (RegQueryValueEx (pathKey, 0, 0, &dtype, (LPBYTE) pathName, &dsize) == ERROR_SUCCESS)
// In older code, this used to check for the existance of the file, but there are situations
// In older code, this used to check for the existence of the file, but there are situations
// where our process doesn't have access to it, but where the driver still loads ok..
ok = (pathName[0] != 0);


+ 7
- 0
source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp View File

@@ -96,11 +96,18 @@ bool check (HRESULT hr)
}
#if JUCE_MINGW
#define JUCE_COMCLASS(name, guid) \
struct name; \
template<> struct UUIDGetter<name> { static CLSID get() { return uuidFromString (guid); } }; \
struct name
#ifdef __uuidof
#undef __uuidof
#endif
#define __uuidof(cls) UUIDGetter<cls>::get()
struct PROPERTYKEY
{
GUID fmtid;


+ 1
- 1
source/modules/juce_audio_devices/sources/juce_AudioTransportSource.h View File

@@ -60,7 +60,7 @@ public:
The source passed in will not be deleted by this object, so must be managed by
the caller.
@param newSource the new input source to use. This may be zero
@param newSource the new input source to use. This may be a nullptr
@param readAheadBufferSize a size of buffer to use for reading ahead. If this
is zero, no reading ahead will be done; if it's
greater than zero, a BufferingAudioSource will be used


+ 8
- 5
source/modules/juce_audio_formats/codecs/flac/alloc.h View File

@@ -41,11 +41,14 @@
* before #including this file, otherwise SIZE_MAX might not be defined
*/
#include <limits.h> /* for SIZE_MAX */
#if HAVE_STDINT_H
#include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */
#endif
#include <stdlib.h> /* for size_t, malloc(), etc */
// JUCE: removed as JUCE already includes standard headers and including
// these in FlacNamespace will cause problems
//#include <limits.h> /* for SIZE_MAX */
//#if HAVE_STDINT_H
//#include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */
//#endif
//#include <stdlib.h> /* for size_t, malloc(), etc */
#include "compat.h"
#ifndef SIZE_MAX


+ 4
- 1
source/modules/juce_audio_formats/codecs/flac/assert.h View File

@@ -35,7 +35,10 @@
/* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */
#ifdef DEBUG
#include <assert.h>
// JUCE: removed as JUCE already includes standard headers and including
// these in FlacNamespace will cause problems
//#include <assert.h>
#define FLAC__ASSERT(x) assert(x)
#define FLAC__ASSERT_DECLARATION(x) x
#else


+ 4
- 1
source/modules/juce_audio_formats/codecs/flac/callback.h View File

@@ -34,7 +34,10 @@
#define FLAC__CALLBACK_H
#include "ordinals.h"
#include <stdlib.h> /* for size_t */
// JUCE: removed as JUCE already includes this and including stdlib
// in FlacNamespace will cause problems
//#include <stdlib.h>
/** \file include/FLAC/callback.h
*


+ 0
- 33
source/modules/juce_audio_formats/codecs/flac/compat.h View File

@@ -39,15 +39,7 @@
#ifndef FLAC__SHARE__COMPAT_H
#define FLAC__SHARE__COMPAT_H
#if defined _WIN32 && !defined __CYGWIN__
/* where MSVC puts unlink() */
# include <io.h>
#else
# include <unistd.h>
#endif
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
#include <sys/types.h> /* for off_t */
#define FLAC__off_t __int64 /* use this instead of off_t to fix the 2 GB limit */
#if !defined __MINGW32__
#define fseeko _fseeki64
@@ -62,11 +54,6 @@
#define FLAC__off_t off_t
#endif
#if HAVE_INTTYPES_H
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#endif
#if defined(_MSC_VER)
#define strtoll _strtoi64
#define strtoull _strtoui64
@@ -95,33 +82,13 @@
#define FLAC__STRNCASECMP strncasecmp
#endif
#if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__
#include <io.h> /* for _setmode(), chmod() */
#include <fcntl.h> /* for _O_BINARY */
#else
#include <unistd.h> /* for chown(), unlink() */
#endif
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
#if defined __BORLANDC__
#include <utime.h> /* for utime() */
#else
#include <sys/utime.h> /* for utime() */
#endif
#else
#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
#include <utime.h> /* for utime() */
#endif
#if defined _MSC_VER
# if _MSC_VER >= 1600
/* Visual Studio 2010 has decent C99 support */
# include <stdint.h>
# define PRIu64 "llu"
# define PRId64 "lld"
# define PRIx64 "llx"
# else
# include <limits.h>
# ifndef UINT32_MAX
# define UINT32_MAX _UI32_MAX
# endif


+ 3
- 1
source/modules/juce_audio_formats/codecs/flac/endswap.h View File

@@ -51,7 +51,9 @@ static inline unsigned short __builtin_bswap16(unsigned short a)
#elif defined HAVE_BYTESWAP_H /* Linux */
#include <byteswap.h>
// JUCE: removed as JUCE already includes standard headers and including
// these in FlacNamespace will cause problems
//#include <byteswap.h>
#define ENDSWAP_16(x) (bswap_16 (x))
#define ENDSWAP_32(x) (bswap_32 (x))


+ 0
- 1
source/modules/juce_audio_formats/codecs/flac/metadata.h View File

@@ -33,7 +33,6 @@
#ifndef FLAC__METADATA_H
#define FLAC__METADATA_H
#include <sys/types.h> /* for off_t */
#include "export.h"
#include "callback.h"
#include "format.h"


+ 0
- 1
source/modules/juce_audio_formats/codecs/flac/stream_decoder.h View File

@@ -33,7 +33,6 @@
#ifndef FLAC__STREAM_DECODER_H
#define FLAC__STREAM_DECODER_H
#include <stdio.h> /* for FILE */
#include "export.h"
#include "format.h"


+ 0
- 1
source/modules/juce_audio_formats/codecs/flac/stream_encoder.h View File

@@ -33,7 +33,6 @@
#ifndef FLAC__STREAM_ENCODER_H
#define FLAC__STREAM_ENCODER_H
#include <stdio.h> /* for FILE */
#include "export.h"
#include "format.h"
#include "stream_decoder.h"


+ 0
- 5
source/modules/juce_audio_formats/codecs/flac/win_utf8_io.h View File

@@ -38,11 +38,6 @@
extern "C" {
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <windows.h>
int get_utf8_argv(int *argc, char ***argv);
int printf_utf8(const char *format, ...);


+ 6
- 8
source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp View File

@@ -668,13 +668,14 @@ public:
//==============================================================================
bool write (const int** data, int numSamples) override
{
jassert (numSamples >= 0);
jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel!
if (writeFailed)
return false;
const size_t bytes = (size_t) numSamples * numChannels * bitsPerSample / 8;
tempBlock.ensureSize ((size_t) bytes, false);
const size_t bytes = numChannels * (size_t) numSamples * bitsPerSample / 8;
tempBlock.ensureSize (bytes, false);
switch (bitsPerSample)
{
@@ -695,13 +696,10 @@ public:
writeFailed = true;
return false;
}
else
{
bytesWritten += bytes;
lengthInSamples += (uint64) numSamples;
return true;
}
bytesWritten += bytes;
lengthInSamples += (uint64) numSamples;
return true;
}
private:


+ 68
- 3
source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp View File

@@ -24,17 +24,79 @@
#if JUCE_USE_FLAC
}
#if defined _WIN32 && !defined __CYGWIN__
#include <io.h>
#else
#include <unistd.h>
#endif
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
#include <sys/types.h> /* for off_t */
#endif
#if HAVE_INTTYPES_H
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#endif
#if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__
#include <io.h> /* for _setmode(), chmod() */
#include <fcntl.h> /* for _O_BINARY */
#else
#include <unistd.h> /* for chown(), unlink() */
#endif
#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__
#if defined __BORLANDC__
#include <utime.h> /* for utime() */
#else
#include <sys/utime.h> /* for utime() */
#endif
#else
#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
#include <utime.h> /* for utime() */
#endif
#if defined _MSC_VER
#if _MSC_VER >= 1600
#include <stdint.h>
#else
#include <limits.h>
#endif
#endif
#ifdef _WIN32
#include <stdio.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <windows.h>
#endif
#ifdef DEBUG
#include <assert.h>
#endif
#include <stdlib.h>
#include <stdio.h>
namespace juce
{
namespace FlacNamespace
{
#if JUCE_INCLUDE_FLAC_CODE || ! defined (JUCE_INCLUDE_FLAC_CODE)
#undef VERSION
#define VERSION "1.2.1"
#define VERSION "1.3.1"
#define FLAC__NO_DLL 1
#if JUCE_MSVC
#pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111)
#else
#define HAVE_LROUND 1
#endif
#if JUCE_MAC
@@ -66,6 +128,7 @@ namespace FlacNamespace
#define __STDC_LIMIT_MACROS 1
#define flac_max jmax
#define flac_min jmin
#undef DEBUG // (some flac code dumps debug trace if the app defines this macro)
#include "flac/all.h"
#include "flac/libFLAC/bitmath.c"
#include "flac/libFLAC/bitreader.c"
@@ -324,7 +387,8 @@ class FlacWriter : public AudioFormatWriter
{
public:
FlacWriter (OutputStream* const out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex)
: AudioFormatWriter (out, flacFormatName, rate, numChans, bits)
: AudioFormatWriter (out, flacFormatName, rate, numChans, bits),
streamStartPos (output != nullptr ? jmax (output->getPosition(), 0ll) : 0ll)
{
using namespace FlacNamespace;
encoder = FLAC__stream_encoder_new();
@@ -432,7 +496,7 @@ public:
packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4);
memcpy (buffer + 18, info.md5sum, 16);
const bool seekOk = output->setPosition (4);
const bool seekOk = output->setPosition (streamStartPos + 4);
ignoreUnused (seekOk);
// if this fails, you've given it an output stream that can't seek! It needs
@@ -482,6 +546,7 @@ public:
private:
FlacNamespace::FLAC__StreamEncoder* encoder;
int64 streamStartPos;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacWriter)
};


+ 1
- 1
source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp View File

@@ -222,7 +222,7 @@ public:
DisposeMovie (movie);
#if JUCE_MAC
ExitMoviesOnThread ();
ExitMoviesOnThread();
#endif
}
}


+ 7
- 3
source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp View File

@@ -656,7 +656,10 @@ namespace WavFileHelpers
if (infoLength > 0)
{
infoLength = jlimit ((int64) 0, infoLength, (int64) input.readInt());
infoLength = jmin (infoLength, (int64) input.readInt());
if (infoLength <= 0)
return;
for (int i = 0; i < numElementsInArray (types); ++i)
{
@@ -664,7 +667,8 @@ namespace WavFileHelpers
{
MemoryBlock mb;
input.readIntoMemoryBlock (mb, (ssize_t) infoLength);
values.set (types[i], mb.toString());
values.set (types[i], String::createStringFromData ((const char*) mb.getData(),
(int) mb.getSize()));
break;
}
}
@@ -1258,7 +1262,7 @@ public:
if (writeFailed)
return false;
const size_t bytes = numChannels * (unsigned int) numSamples * bitsPerSample / 8;
const size_t bytes = numChannels * (size_t) numSamples * bitsPerSample / 8;
tempBlock.ensureSize (bytes, false);
switch (bitsPerSample)


+ 4
- 1
source/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp View File

@@ -166,6 +166,9 @@ public:
checkCoInitialiseCalled();
clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
startSampleInFile, numSamples, lengthInSamples);
const int stride = numChannels * sizeof (int16);
while (numSamples > 0)
@@ -297,7 +300,7 @@ private:
sampleRate = inputFormat->nSamplesPerSec;
numChannels = inputFormat->nChannels;
bitsPerSample = inputFormat->wBitsPerSample;
bitsPerSample = inputFormat->wBitsPerSample != 0 ? inputFormat->wBitsPerSample : 16;
lengthInSamples = (lengthInNanoseconds * (int) sampleRate) / 10000000;
}
}


+ 2
- 0
source/modules/juce_audio_formats/juce_audio_formats.cpp View File

@@ -31,6 +31,8 @@
#error "Incorrect use of JUCE cpp file"
#endif
#include "AppConfig.h"
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1


+ 27
- 1
source/modules/juce_audio_formats/juce_audio_formats.h View File

@@ -22,10 +22,36 @@
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.txt file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_audio_formats
vendor: juce
version: 4.3.0
name: JUCE audio file format codecs
description: Classes for reading and writing various audio file formats.
website: http://www.juce.com/juce
license: GPL/Commercial
dependencies: juce_audio_basics
OSXFrameworks: CoreAudio CoreMIDI QuartzCore AudioToolbox
iOSFrameworks: AudioToolbox QuartzCore
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#ifndef JUCE_AUDIO_FORMATS_H_INCLUDED
#define JUCE_AUDIO_FORMATS_H_INCLUDED
#include "../juce_audio_basics/juce_audio_basics.h"
#include <juce_audio_basics/juce_audio_basics.h>
//==============================================================================
/** Config: JUCE_USE_FLAC


+ 183
- 0
source/modules/juce_audio_processors/format/juce_AudioPluginFormat.cpp View File

@@ -22,5 +22,188 @@
==============================================================================
*/
namespace AudioPluginFormatHelpers
{
struct CallbackInvoker
{
struct InvokeOnMessageThread : public CallbackMessage
{
InvokeOnMessageThread (AudioPluginInstance* inInstance, const String& inError,
AudioPluginFormat::InstantiationCompletionCallback* inCompletion,
CallbackInvoker* invoker)
: instance (inInstance), error (inError), compCallback (inCompletion), owner (invoker)
{}
void messageCallback() override { compCallback->completionCallback (instance, error); }
//==============================================================================
AudioPluginInstance* instance;
String error;
ScopedPointer<AudioPluginFormat::InstantiationCompletionCallback> compCallback;
ScopedPointer<CallbackInvoker> owner;
};
//==============================================================================
CallbackInvoker (AudioPluginFormat::InstantiationCompletionCallback* cc) : completion (cc)
{}
void completionCallback (AudioPluginInstance* instance, const String& error)
{
(new InvokeOnMessageThread (instance, error, completion, this))->post();
}
static void staticCompletionCallback (void* userData, AudioPluginInstance* instance, const String& error)
{
reinterpret_cast<CallbackInvoker*> (userData)->completionCallback (instance, error);
}
//==============================================================================
AudioPluginFormat::InstantiationCompletionCallback* completion;
};
}
AudioPluginFormat::AudioPluginFormat() noexcept {}
AudioPluginFormat::~AudioPluginFormat() {}
AudioPluginInstance* AudioPluginFormat::createInstanceFromDescription (const PluginDescription& desc,
double initialSampleRate,
int initialBufferSize)
{
String errorMessage;
return createInstanceFromDescription (desc, initialSampleRate, initialBufferSize, errorMessage);
}
//==============================================================================
struct EventSignaler : public AudioPluginFormat::InstantiationCompletionCallback
{
EventSignaler (WaitableEvent& inEvent, AudioPluginInstance*& inInstance, String& inErrorMessage)
: event (inEvent), outInstance (inInstance), outErrorMessage (inErrorMessage)
{}
void completionCallback (AudioPluginInstance* newInstance, const String& result) override
{
outInstance = newInstance;
outErrorMessage = result;
event.signal();
}
static void staticCompletionCallback (void* userData, AudioPluginInstance* pluginInstance, const String& error)
{
reinterpret_cast<EventSignaler*> (userData)->completionCallback (pluginInstance, error);
}
WaitableEvent& event;
AudioPluginInstance*& outInstance;
String& outErrorMessage;
JUCE_DECLARE_NON_COPYABLE (EventSignaler)
};
AudioPluginInstance* AudioPluginFormat::createInstanceFromDescription (const PluginDescription& desc,
double initialSampleRate,
int initialBufferSize,
String& errorMessage)
{
if (MessageManager::getInstance()->isThisTheMessageThread()
&& requiresUnblockedMessageThreadDuringCreation(desc))
{
errorMessage = NEEDS_TRANS ("This plug-in cannot be instantiated synchronously");
return nullptr;
}
WaitableEvent waitForCreation;
AudioPluginInstance* instance = nullptr;
ScopedPointer<EventSignaler> eventSignaler (new EventSignaler (waitForCreation, instance, errorMessage));
if (! MessageManager::getInstance()->isThisTheMessageThread())
createPluginInstanceAsync (desc, initialSampleRate, initialBufferSize, eventSignaler.release());
else
createPluginInstance (desc, initialSampleRate, initialBufferSize,
eventSignaler, EventSignaler::staticCompletionCallback);
waitForCreation.wait();
return instance;
}
void AudioPluginFormat::createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
AudioPluginFormat::InstantiationCompletionCallback* callback)
{
if (MessageManager::getInstance()->isThisTheMessageThread())
{
createPluginInstanceOnMessageThread (description, initialSampleRate, initialBufferSize, callback);
return;
}
//==============================================================================
struct InvokeOnMessageThread : public CallbackMessage
{
InvokeOnMessageThread (AudioPluginFormat* myself,
const PluginDescription& descriptionParam,
double initialSampleRateParam,
int initialBufferSizeParam,
AudioPluginFormat::InstantiationCompletionCallback* callbackParam)
: owner (myself), descr (descriptionParam), sampleRate (initialSampleRateParam),
bufferSize (initialBufferSizeParam), call (callbackParam)
{}
void messageCallback() override
{
owner->createPluginInstanceOnMessageThread (descr, sampleRate, bufferSize, call);
}
AudioPluginFormat* owner;
PluginDescription descr;
double sampleRate;
int bufferSize;
AudioPluginFormat::InstantiationCompletionCallback* call;
};
(new InvokeOnMessageThread (this, description, initialSampleRate, initialBufferSize, callback))->post();
}
#if JUCE_COMPILER_SUPPORTS_LAMBDAS
void AudioPluginFormat::createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
std::function<void (AudioPluginInstance*, const String&)> f)
{
struct CallbackInvoker : public AudioPluginFormat::InstantiationCompletionCallback
{
CallbackInvoker (std::function<void (AudioPluginInstance*, const String&)> inCompletion)
: completion (inCompletion)
{}
void completionCallback (AudioPluginInstance* instance, const String& error) override
{
completion (instance, error);
}
std::function<void (AudioPluginInstance*, const String&)> completion;
};
createPluginInstanceAsync (description, initialSampleRate, initialBufferSize, new CallbackInvoker (f));
}
#endif
void AudioPluginFormat::createPluginInstanceOnMessageThread (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
AudioPluginFormat::InstantiationCompletionCallback* callback)
{
jassert (callback != nullptr);
jassert (MessageManager::getInstance()->isThisTheMessageThread());
//==============================================================================
//==============================================================================
AudioPluginFormatHelpers::CallbackInvoker* completion = new AudioPluginFormatHelpers::CallbackInvoker (callback);
createPluginInstance (description, initialSampleRate, initialBufferSize, completion,
AudioPluginFormatHelpers::CallbackInvoker::staticCompletionCallback);
}

+ 65
- 7
source/modules/juce_audio_processors/format/juce_AudioPluginFormat.h View File

@@ -30,11 +30,20 @@
/**
The base class for a type of plugin format, such as VST, AudioUnit, LADSPA, etc.
@see AudioFormatManager
@see AudioPluginFormatManager
*/
class JUCE_API AudioPluginFormat
{
public:
//==============================================================================
struct JUCE_API InstantiationCompletionCallback
{
virtual ~InstantiationCompletionCallback() {}
virtual void completionCallback (AudioPluginInstance* instance, const String& error) = 0;
JUCE_LEAK_DETECTOR (InstantiationCompletionCallback)
};
//==============================================================================
/** Destructor. */
virtual ~AudioPluginFormat();
@@ -58,11 +67,36 @@ public:
const String& fileOrIdentifier) = 0;
/** Tries to recreate a type from a previously generated PluginDescription.
@see PluginDescription::createInstance
@see AudioPluginFormatManager::createInstance
*/
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&,
double initialSampleRate,
int initialBufferSize);
/** Same as above but with the possibility of returning an error message.
@see AudioPluginFormatManager::createInstance
*/
virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc,
double initialSampleRate,
int initialBufferSize) = 0;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&,
double initialSampleRate,
int initialBufferSize,
String& errorMessage);
/** Tries to recreate a type from a previously generated PluginDescription.
@see AudioPluginFormatManager::createInstanceAsync
*/
void createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
InstantiationCompletionCallback* completionCallback);
#if JUCE_COMPILER_SUPPORTS_LAMBDAS
void createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
std::function<void (AudioPluginInstance*, const String&)> completionCallback);
#endif
/** Should do a quick check to see if this file or directory might be a plugin of
this format.
@@ -82,7 +116,7 @@ public:
It doesn't actually need to load it, just to check whether the file or component
still exists.
*/
virtual bool doesPluginStillExist (const PluginDescription& desc) = 0;
virtual bool doesPluginStillExist (const PluginDescription&) = 0;
/** Returns true if this format needs to run a scan to find its list of plugins. */
virtual bool canScanForPlugins() const = 0;
@@ -90,9 +124,17 @@ public:
/** Searches a suggested set of directories for any plugins in this format.
The path might be ignored, e.g. by AUs, which are found by the OS rather
than manually.
@param directoriesToSearch This specifies which directories shall be
searched for plug-ins.
@param recursive Should the search recursively traverse folders.
@param allowPluginsWhichRequireAsynchronousInstantiation
If this is false then plug-ins which require
asynchronous creation will be excluded.
*/
virtual StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch,
bool recursive) = 0;
bool recursive,
bool allowPluginsWhichRequireAsynchronousInstantiation = false) = 0;
/** Returns the typical places to look for this kind of plugin.
@@ -103,8 +145,24 @@ public:
protected:
//==============================================================================
friend class AudioPluginFormatManager;
AudioPluginFormat() noexcept;
/** Implementors must override this function. This is guaranteed to be called on
the message thread. You may call the callback on any thread.
*/
virtual void createPluginInstance (const PluginDescription&, double initialSampleRate,
int initialBufferSize, void* userData,
void (*callback) (void*, AudioPluginInstance*, const String&)) = 0;
virtual bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept = 0;
private:
/** @internal */
void createPluginInstanceOnMessageThread (const PluginDescription&, double rate, int size,
AudioPluginFormat::InstantiationCompletionCallback*);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormat)
};


+ 86
- 10
source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp View File

@@ -22,6 +22,39 @@
==============================================================================
*/
namespace PluginFormatManagerHelpers
{
struct ErrorCallbackOnMessageThread : public CallbackMessage
{
ErrorCallbackOnMessageThread (const String& inError,
AudioPluginFormat::InstantiationCompletionCallback* inCallback)
: error (inError), callback (inCallback)
{
}
void messageCallback() override { callback->completionCallback (nullptr, error); }
String error;
ScopedPointer<AudioPluginFormat::InstantiationCompletionCallback> callback;
};
#if JUCE_COMPILER_SUPPORTS_LAMBDAS
struct ErrorLambdaOnMessageThread : public CallbackMessage
{
ErrorLambdaOnMessageThread (const String& inError,
std::function<void (AudioPluginInstance*, const String&)> f)
: error (inError), lambda (f)
{
}
void messageCallback() override { lambda (nullptr, error); }
String error;
std::function<void (AudioPluginInstance*, const String&)> lambda;
};
#endif
}
AudioPluginFormatManager::AudioPluginFormatManager() {}
AudioPluginFormatManager::~AudioPluginFormatManager() {}
@@ -32,15 +65,15 @@ void AudioPluginFormatManager::addDefaultFormats()
// you should only call this method once!
for (int i = formats.size(); --i >= 0;)
{
#if JUCE_PLUGINHOST_VST
#if JUCE_PLUGINHOST_VST && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_IOS)
jassert (dynamic_cast<VSTPluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_VST3
#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS)
jassert (dynamic_cast<VST3PluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_AU && JUCE_MAC
#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)
jassert (dynamic_cast<AudioUnitPluginFormat*> (formats[i]) == nullptr);
#endif
@@ -50,15 +83,15 @@ void AudioPluginFormatManager::addDefaultFormats()
}
#endif
#if JUCE_PLUGINHOST_AU && JUCE_MAC
#if JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)
formats.add (new AudioUnitPluginFormat());
#endif
#if JUCE_PLUGINHOST_VST
#if JUCE_PLUGINHOST_VST && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_IOS)
formats.add (new VSTPluginFormat());
#endif
#if JUCE_PLUGINHOST_VST3
#if JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS)
formats.add (new VST3PluginFormat());
#endif
@@ -85,12 +118,55 @@ void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format)
AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, double rate,
int blockSize, String& errorMessage) const
{
if (AudioPluginFormat* format = findFormatForDescription (description, errorMessage))
return format->createInstanceFromDescription (description, rate, blockSize, errorMessage);
return nullptr;
}
void AudioPluginFormatManager::createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
AudioPluginFormat::InstantiationCompletionCallback* callback)
{
String error;
if (AudioPluginFormat* format = findFormatForDescription (description, error))
return format->createPluginInstanceAsync (description, initialSampleRate, initialBufferSize, callback);
(new PluginFormatManagerHelpers::ErrorCallbackOnMessageThread (error, callback))->post();
}
#if JUCE_COMPILER_SUPPORTS_LAMBDAS
void AudioPluginFormatManager::createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
std::function<void (AudioPluginInstance*, const String&)> f)
{
String error;
if (AudioPluginFormat* format = findFormatForDescription (description, error))
return format->createPluginInstanceAsync (description, initialSampleRate, initialBufferSize, f);
(new PluginFormatManagerHelpers::ErrorLambdaOnMessageThread (error, f))->post();
}
#endif
AudioPluginFormat* AudioPluginFormatManager::findFormatForDescription (const PluginDescription& description, String& errorMessage) const
{
errorMessage = String();
for (int i = 0; i < formats.size(); ++i)
if (AudioPluginInstance* result = formats.getUnchecked(i)->createInstanceFromDescription (description, rate, blockSize))
return result;
{
AudioPluginFormat* format;
if ((format = formats.getUnchecked (i))->getName() == description.pluginFormatName
&& format->fileMightContainThisPluginType (description.fileOrIdentifier))
return format;
}
errorMessage = NEEDS_TRANS ("No compatible plug-in format exists for this plug-in");
errorMessage = doesPluginStillExist (description) ? TRANS ("This plug-in failed to load correctly")
: TRANS ("This plug-in file no longer exists");
return nullptr;
}


+ 42
- 0
source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.h View File

@@ -75,12 +75,51 @@ public:
If it can't load the plugin, it returns nullptr and leaves a message in the
errorMessage string.
If you intend to instantiate a AudioUnit v3 plug-in then you must either
use the non-blocking asynchrous version below - or call this method from a
thread other than the message thread and without blocking the message
thread.
*/
AudioPluginInstance* createPluginInstance (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
String& errorMessage) const;
/** Tries to asynchronously load the type for this description, by trying
all the formats that this manager knows about.
The caller must supply a callback object which will be called when
the instantantiation has completed.
If it can't load the plugin then the callback function will be called
passing a nullptr as the instance argument along with an error message.
The callback function will be called on the message thread so the caller
must not block the message thread.
The callback object will be deleted automatically after it has been
invoked.
The caller is responsible for deleting the instance that is passed to
the callback function.
If you intend to instantiate a AudioUnit v3 plug-in then you must use
this non-blocking asynchrous version - or call the synchrous method
from an auxiliary thread.
*/
void createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
AudioPluginFormat::InstantiationCompletionCallback* callback);
#if JUCE_COMPILER_SUPPORTS_LAMBDAS
void createPluginInstanceAsync (const PluginDescription& description,
double initialSampleRate,
int initialBufferSize,
std::function<void (AudioPluginInstance*, const String&)> completionCallback);
#endif
/** Checks that the file or component for this plugin actually still exists.
(This won't try to load the plugin)
@@ -89,6 +128,9 @@ public:
private:
//==============================================================================
//@internal
AudioPluginFormat* findFormatForDescription (const PluginDescription&, String& errorMessage) const;
OwnedArray<AudioPluginFormat> formats;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager)


+ 778
- 0
source/modules/juce_audio_processors/format_types/juce_AU_Shared.h View File

@@ -0,0 +1,778 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
// This macro can be set if you need to override this internal name for some reason..
#ifndef JUCE_STATE_DICTIONARY_KEY
#define JUCE_STATE_DICTIONARY_KEY "jucePluginState"
#endif
struct AudioUnitHelpers
{
// maps a channel index into an AU format to an index of a juce format
struct AUChannelStreamOrder
{
AudioChannelLayoutTag auLayoutTag;
AudioChannelSet::ChannelType speakerOrder[8];
};
struct StreamOrder : public AudioChannelSet
{
static AUChannelStreamOrder auChannelStreamOrder[];
};
static AudioChannelSet::ChannelType CoreAudioChannelLabelToJuceType (AudioChannelLabel label) noexcept
{
if (label >= kAudioChannelLabel_Discrete_0 && label <= kAudioChannelLabel_Discrete_65535)
{
const unsigned int discreteChannelNum = label - kAudioChannelLabel_Discrete_0;
return static_cast<AudioChannelSet::ChannelType> (AudioChannelSet::discreteChannel0 + discreteChannelNum);
}
switch (label)
{
case kAudioChannelLabel_Center:
case kAudioChannelLabel_Mono: return AudioChannelSet::centre;
case kAudioChannelLabel_Left:
case kAudioChannelLabel_HeadphonesLeft: return AudioChannelSet::left;
case kAudioChannelLabel_Right:
case kAudioChannelLabel_HeadphonesRight: return AudioChannelSet::right;
case kAudioChannelLabel_LFEScreen: return AudioChannelSet::LFE;
case kAudioChannelLabel_LeftSurround: return AudioChannelSet::leftSurround;
case kAudioChannelLabel_RightSurround: return AudioChannelSet::rightSurround;
case kAudioChannelLabel_LeftCenter: return AudioChannelSet::leftCentre;
case kAudioChannelLabel_RightCenter: return AudioChannelSet::rightCentre;
case kAudioChannelLabel_CenterSurround: return AudioChannelSet::surround;
case kAudioChannelLabel_LeftSurroundDirect: return AudioChannelSet::leftSurroundSide;
case kAudioChannelLabel_RightSurroundDirect: return AudioChannelSet::rightSurroundSide;
case kAudioChannelLabel_TopCenterSurround: return AudioChannelSet::topMiddle;
case kAudioChannelLabel_VerticalHeightLeft: return AudioChannelSet::topFrontLeft;
case kAudioChannelLabel_VerticalHeightRight: return AudioChannelSet::topFrontRight;
case kAudioChannelLabel_VerticalHeightCenter: return AudioChannelSet::topFrontCentre;
case kAudioChannelLabel_TopBackLeft: return AudioChannelSet::topRearLeft;
case kAudioChannelLabel_RearSurroundLeft: return AudioChannelSet::leftSurroundRear;
case kAudioChannelLabel_TopBackRight: return AudioChannelSet::topRearRight;
case kAudioChannelLabel_RearSurroundRight: return AudioChannelSet::rightSurroundRear;
case kAudioChannelLabel_TopBackCenter: return AudioChannelSet::topRearCentre;
case kAudioChannelLabel_LFE2: return AudioChannelSet::LFE2;
case kAudioChannelLabel_LeftWide: return AudioChannelSet::wideLeft;
case kAudioChannelLabel_RightWide: return AudioChannelSet::wideRight;
case kAudioChannelLabel_Ambisonic_W: return AudioChannelSet::ambisonicW;
case kAudioChannelLabel_Ambisonic_X: return AudioChannelSet::ambisonicX;
case kAudioChannelLabel_Ambisonic_Y: return AudioChannelSet::ambisonicY;
case kAudioChannelLabel_Ambisonic_Z: return AudioChannelSet::ambisonicZ;
default: return AudioChannelSet::unknown;
}
}
static AudioChannelLabel JuceChannelTypeToCoreAudioLabel (const AudioChannelSet::ChannelType& label) noexcept
{
if (label >= AudioChannelSet::discreteChannel0)
{
const unsigned int discreteChannelNum = label - AudioChannelSet::discreteChannel0;;
return static_cast<AudioChannelLabel> (kAudioChannelLabel_Discrete_0 + discreteChannelNum);
}
switch (label)
{
case AudioChannelSet::centre: return kAudioChannelLabel_Center;
case AudioChannelSet::left: return kAudioChannelLabel_Left;
case AudioChannelSet::right: return kAudioChannelLabel_Right;
case AudioChannelSet::LFE: return kAudioChannelLabel_LFEScreen;
case AudioChannelSet::leftSurroundRear: return kAudioChannelLabel_RearSurroundLeft;
case AudioChannelSet::rightSurroundRear: return kAudioChannelLabel_RearSurroundRight;
case AudioChannelSet::leftCentre: return kAudioChannelLabel_LeftCenter;
case AudioChannelSet::rightCentre: return kAudioChannelLabel_RightCenter;
case AudioChannelSet::surround: return kAudioChannelLabel_CenterSurround;
case AudioChannelSet::leftSurround: return kAudioChannelLabel_LeftSurround;
case AudioChannelSet::rightSurround: return kAudioChannelLabel_RightSurround;
case AudioChannelSet::topMiddle: return kAudioChannelLabel_TopCenterSurround;
case AudioChannelSet::topFrontLeft: return kAudioChannelLabel_VerticalHeightLeft;
case AudioChannelSet::topFrontRight: return kAudioChannelLabel_VerticalHeightRight;
case AudioChannelSet::topFrontCentre: return kAudioChannelLabel_VerticalHeightCenter;
case AudioChannelSet::topRearLeft: return kAudioChannelLabel_TopBackLeft;
case AudioChannelSet::topRearRight: return kAudioChannelLabel_TopBackRight;
case AudioChannelSet::topRearCentre: return kAudioChannelLabel_TopBackCenter;
case AudioChannelSet::LFE2: return kAudioChannelLabel_LFE2;
case AudioChannelSet::wideLeft: return kAudioChannelLabel_LeftWide;
case AudioChannelSet::wideRight: return kAudioChannelLabel_RightWide;
case AudioChannelSet::ambisonicW: return kAudioChannelLabel_Ambisonic_W;
case AudioChannelSet::ambisonicX: return kAudioChannelLabel_Ambisonic_X;
case AudioChannelSet::ambisonicY: return kAudioChannelLabel_Ambisonic_Y;
case AudioChannelSet::ambisonicZ: return kAudioChannelLabel_Ambisonic_Z;
case AudioChannelSet::leftSurroundSide: return kAudioChannelLabel_LeftSurroundDirect;
case AudioChannelSet::rightSurroundSide: return kAudioChannelLabel_RightSurroundDirect;
case AudioChannelSet::unknown: return kAudioChannelLabel_Unknown;
case AudioChannelSet::discreteChannel0: return kAudioChannelLabel_Discrete_0;
}
return kAudioChannelLabel_Unknown;
}
static AudioChannelSet CoreAudioChannelBitmapToJuceType (UInt32 bitmap) noexcept
{
AudioChannelSet set;
if ((bitmap & kAudioChannelBit_Left) != 0) set.addChannel (AudioChannelSet::left);
if ((bitmap & kAudioChannelBit_Right) != 0) set.addChannel (AudioChannelSet::right);
if ((bitmap & kAudioChannelBit_Center) != 0) set.addChannel (AudioChannelSet::centre);
if ((bitmap & kAudioChannelBit_LFEScreen) != 0) set.addChannel (AudioChannelSet::LFE);
if ((bitmap & kAudioChannelBit_LeftSurroundDirect) != 0) set.addChannel (AudioChannelSet::leftSurroundSide);
if ((bitmap & kAudioChannelBit_RightSurroundDirect) != 0) set.addChannel (AudioChannelSet::rightSurroundSide);
if ((bitmap & kAudioChannelBit_LeftCenter) != 0) set.addChannel (AudioChannelSet::leftCentre);
if ((bitmap & kAudioChannelBit_RightCenter) != 0) set.addChannel (AudioChannelSet::rightCentre);
if ((bitmap & kAudioChannelBit_CenterSurround) != 0) set.addChannel (AudioChannelSet::surround);
if ((bitmap & kAudioChannelBit_LeftSurround) != 0) set.addChannel (AudioChannelSet::leftSurround);
if ((bitmap & kAudioChannelBit_RightSurround) != 0) set.addChannel (AudioChannelSet::rightSurround);
if ((bitmap & kAudioChannelBit_TopCenterSurround) != 0) set.addChannel (AudioChannelSet::topMiddle);
if ((bitmap & kAudioChannelBit_VerticalHeightLeft) != 0) set.addChannel (AudioChannelSet::topFrontLeft);
if ((bitmap & kAudioChannelBit_VerticalHeightCenter) != 0) set.addChannel (AudioChannelSet::topFrontCentre);
if ((bitmap & kAudioChannelBit_VerticalHeightRight) != 0) set.addChannel (AudioChannelSet::topFrontRight);
if ((bitmap & kAudioChannelBit_TopBackLeft) != 0) set.addChannel (AudioChannelSet::topRearLeft);
if ((bitmap & kAudioChannelBit_TopBackCenter) != 0) set.addChannel (AudioChannelSet::topRearCentre);
if ((bitmap & kAudioChannelBit_TopBackRight) != 0) set.addChannel (AudioChannelSet::topRearRight);
return set;
}
static AudioChannelSet CoreAudioChannelLayoutToJuceType (const AudioChannelLayout& layout) noexcept
{
const AudioChannelLayoutTag tag = layout.mChannelLayoutTag;
if (tag == kAudioChannelLayoutTag_UseChannelBitmap) return CoreAudioChannelBitmapToJuceType (layout.mChannelBitmap);
if (tag == kAudioChannelLayoutTag_UseChannelDescriptions)
{
if (layout.mNumberChannelDescriptions <= 8)
{
// first try to convert the layout via the auChannelStreamOrder array
int layoutIndex;
for (layoutIndex = 0; StreamOrder::auChannelStreamOrder[layoutIndex].auLayoutTag != 0; ++layoutIndex)
{
const AUChannelStreamOrder& streamOrder = StreamOrder::auChannelStreamOrder[layoutIndex];
int numChannels;
for (numChannels = 0; numChannels < 8 && streamOrder.speakerOrder[numChannels] != 0;)
++numChannels;
if (numChannels != (int) layout.mNumberChannelDescriptions)
continue;
int ch;
for (ch = 0; ch < numChannels; ++ch)
if (JuceChannelTypeToCoreAudioLabel (streamOrder.speakerOrder[ch]) != layout.mChannelDescriptions[ch].mChannelLabel)
break;
// match!
if (ch == numChannels)
break;
}
if (StreamOrder::auChannelStreamOrder[layoutIndex].auLayoutTag != 0)
return CALayoutTagToChannelSet (StreamOrder::auChannelStreamOrder[layoutIndex].auLayoutTag);
}
AudioChannelSet set;
for (unsigned int i = 0; i < layout.mNumberChannelDescriptions; ++i)
set.addChannel (CoreAudioChannelLabelToJuceType (layout.mChannelDescriptions[i].mChannelLabel));
return set;
}
return CALayoutTagToChannelSet (tag);
}
static AudioChannelSet CALayoutTagToChannelSet (AudioChannelLayoutTag tag) noexcept
{
switch (tag)
{
case kAudioChannelLayoutTag_Unknown: return AudioChannelSet::disabled();
case kAudioChannelLayoutTag_Mono: return AudioChannelSet::mono();
case kAudioChannelLayoutTag_Stereo:
case kAudioChannelLayoutTag_StereoHeadphones:
case kAudioChannelLayoutTag_Binaural: return AudioChannelSet::stereo();
case kAudioChannelLayoutTag_Quadraphonic: return AudioChannelSet::quadraphonic();
case kAudioChannelLayoutTag_Pentagonal: return AudioChannelSet::pentagonal();
case kAudioChannelLayoutTag_Hexagonal: return AudioChannelSet::hexagonal();
case kAudioChannelLayoutTag_Octagonal: return AudioChannelSet::octagonal();
case kAudioChannelLayoutTag_Ambisonic_B_Format: return AudioChannelSet::ambisonic();
case kAudioChannelLayoutTag_AudioUnit_6_0: return AudioChannelSet::create6point0();
case kAudioChannelLayoutTag_DTS_6_0_A: return AudioChannelSet::create6point0Music();
case kAudioChannelLayoutTag_MPEG_6_1_A: return AudioChannelSet::create6point1();
case kAudioChannelLayoutTag_DTS_6_1_A: return AudioChannelSet::create6point1Music();
case kAudioChannelLayoutTag_MPEG_5_0_B:
case kAudioChannelLayoutTag_MPEG_5_0_A:
return AudioChannelSet::create5point0();
case kAudioChannelLayoutTag_MPEG_5_1_A: return AudioChannelSet::create5point1();
case kAudioChannelLayoutTag_DTS_7_1:
case kAudioChannelLayoutTag_AudioUnit_7_0: return AudioChannelSet::create7point0();
case kAudioChannelLayoutTag_AudioUnit_7_0_Front: return AudioChannelSet::create7point0SDDS();
case kAudioChannelLayoutTag_MPEG_7_1_A: return AudioChannelSet::create7point1SDDS();
case kAudioChannelLayoutTag_MPEG_3_0_A:
case kAudioChannelLayoutTag_MPEG_3_0_B: return AudioChannelSet::createLCR();
case kAudioChannelLayoutTag_MPEG_4_0_A:
case kAudioChannelLayoutTag_MPEG_4_0_B: return AudioChannelSet::createLCRS();
case kAudioChannelLayoutTag_ITU_2_1: return AudioChannelSet::createLRS();
case kAudioChannelLayoutTag_MPEG_7_1_C: return AudioChannelSet::create7point1();
}
if (int numChannels = static_cast<int> (tag) & 0xffff)
return AudioChannelSet::discreteChannels (numChannels);
// Bitmap and channel description array layout tags are currently unsupported :-(
jassertfalse;
return AudioChannelSet();
}
static AudioChannelLayoutTag ChannelSetToCALayoutTag (const AudioChannelSet& set) noexcept
{
if (set == AudioChannelSet::mono()) return kAudioChannelLayoutTag_Mono;
if (set == AudioChannelSet::stereo()) return kAudioChannelLayoutTag_Stereo;
if (set == AudioChannelSet::createLCR()) return kAudioChannelLayoutTag_MPEG_3_0_A;
if (set == AudioChannelSet::createLRS()) return kAudioChannelLayoutTag_ITU_2_1;
if (set == AudioChannelSet::createLCRS()) return kAudioChannelLayoutTag_MPEG_4_0_A;
if (set == AudioChannelSet::quadraphonic()) return kAudioChannelLayoutTag_Quadraphonic;
if (set == AudioChannelSet::pentagonal()) return kAudioChannelLayoutTag_Pentagonal;
if (set == AudioChannelSet::hexagonal()) return kAudioChannelLayoutTag_Hexagonal;
if (set == AudioChannelSet::octagonal()) return kAudioChannelLayoutTag_Octagonal;
if (set == AudioChannelSet::ambisonic()) return kAudioChannelLayoutTag_Ambisonic_B_Format;
if (set == AudioChannelSet::create5point0()) return kAudioChannelLayoutTag_MPEG_5_0_A;
if (set == AudioChannelSet::create5point1()) return kAudioChannelLayoutTag_MPEG_5_1_A;
if (set == AudioChannelSet::create6point0()) return kAudioChannelLayoutTag_AudioUnit_6_0;
if (set == AudioChannelSet::create6point0Music()) return kAudioChannelLayoutTag_DTS_6_0_A;
if (set == AudioChannelSet::create6point1Music()) return kAudioChannelLayoutTag_DTS_6_1_A;
if (set == AudioChannelSet::create6point1()) return kAudioChannelLayoutTag_MPEG_6_1_A;
if (set == AudioChannelSet::create7point0()) return kAudioChannelLayoutTag_AudioUnit_7_0;
if (set == AudioChannelSet::create7point1()) return kAudioChannelLayoutTag_MPEG_7_1_C;
if (set == AudioChannelSet::create7point0SDDS()) return kAudioChannelLayoutTag_AudioUnit_7_0_Front;
if (set == AudioChannelSet::create7point1SDDS()) return kAudioChannelLayoutTag_MPEG_7_1_A;
if (set == AudioChannelSet::disabled()) return kAudioChannelLayoutTag_Unknown;
return static_cast<AudioChannelLayoutTag> ((int) kAudioChannelLayoutTag_DiscreteInOrder | set.size());
}
static int auChannelIndexToJuce (int auIndex, const AudioChannelSet& channelSet)
{
if (auIndex >= 8) return auIndex;
AudioChannelLayoutTag currentLayout = ChannelSetToCALayoutTag (channelSet);
int layoutIndex;
for (layoutIndex = 0; StreamOrder::auChannelStreamOrder[layoutIndex].auLayoutTag != currentLayout; ++layoutIndex)
if (StreamOrder::auChannelStreamOrder[layoutIndex].auLayoutTag == 0) return auIndex;
AudioChannelSet::ChannelType channelType
= StreamOrder::auChannelStreamOrder[layoutIndex].speakerOrder[auIndex];
const int juceIndex = channelSet.getChannelTypes().indexOf (channelType);
jassert (juceIndex >= 0);
return juceIndex >= 0 ? juceIndex : auIndex;
}
static int juceChannelIndexToAu (int juceIndex, const AudioChannelSet& channelSet)
{
if (channelSet.isDiscreteLayout())
return juceIndex;
AudioChannelLayoutTag currentLayout = ChannelSetToCALayoutTag (channelSet);
int layoutIndex;
for (layoutIndex = 0; StreamOrder::auChannelStreamOrder[layoutIndex].auLayoutTag != currentLayout; ++layoutIndex)
{
if (StreamOrder::auChannelStreamOrder[layoutIndex].auLayoutTag == 0)
{
jassertfalse;
return juceIndex;
}
}
const AUChannelStreamOrder& channelOrder = StreamOrder::auChannelStreamOrder[layoutIndex];
AudioChannelSet::ChannelType channelType = channelSet.getTypeOfChannel (juceIndex);
for (int i = 0; i < 8 && channelOrder.speakerOrder[i] != 0; ++i)
if (channelOrder.speakerOrder[i] == channelType)
return i;
jassertfalse;
return juceIndex;
}
class ChannelRemapper
{
public:
ChannelRemapper (AudioProcessor& p) : processor (p), inputLayoutMap (nullptr), outputLayoutMap (nullptr) {}
~ChannelRemapper() {}
void alloc()
{
const int numInputBuses = processor.getBusCount (true);
const int numOutputBuses = processor.getBusCount (false);
initializeChannelMapArray (true, numInputBuses);
initializeChannelMapArray (false, numOutputBuses);
for (int busIdx = 0; busIdx < numInputBuses; ++busIdx)
fillLayoutChannelMaps (true, busIdx);
for (int busIdx = 0; busIdx < numOutputBuses; ++busIdx)
fillLayoutChannelMaps (false, busIdx);
}
void release()
{
inputLayoutMap = outputLayoutMap = nullptr;
inputLayoutMapPtrStorage.free();
outputLayoutMapPtrStorage.free();
inputLayoutMapStorage.free();
outputLayoutMapStorage.free();
}
inline const int* get (bool input, int bus) const noexcept { return (input ? inputLayoutMap : outputLayoutMap) [bus]; }
private:
//==============================================================================
AudioProcessor& processor;
HeapBlock<int*> inputLayoutMapPtrStorage, outputLayoutMapPtrStorage;
HeapBlock<int> inputLayoutMapStorage, outputLayoutMapStorage;
int** inputLayoutMap;
int** outputLayoutMap;
//==============================================================================
void initializeChannelMapArray (bool isInput, const int numBuses)
{
HeapBlock<int*>& layoutMapPtrStorage = isInput ? inputLayoutMapPtrStorage : outputLayoutMapPtrStorage;
HeapBlock<int>& layoutMapStorage = isInput ? inputLayoutMapStorage : outputLayoutMapStorage;
int**& layoutMap = isInput ? inputLayoutMap : outputLayoutMap;
const int totalInChannels = processor.getTotalNumInputChannels();
const int totalOutChannels = processor.getTotalNumOutputChannels();
layoutMapPtrStorage.calloc (static_cast<size_t> (numBuses));
layoutMapStorage.calloc (static_cast<size_t> (isInput ? totalInChannels : totalOutChannels));
layoutMap = layoutMapPtrStorage. getData();
int ch = 0;
for (int busIdx = 0; busIdx < numBuses; ++busIdx)
{
layoutMap[busIdx] = layoutMapStorage.getData() + ch;
ch += processor.getChannelCountOfBus (isInput, busIdx);
}
}
void fillLayoutChannelMaps (bool isInput, int busNr)
{
int* layoutMap = (isInput ? inputLayoutMap : outputLayoutMap)[busNr];
const AudioChannelSet& channelFormat = processor.getChannelLayoutOfBus (isInput, busNr);
const int numChannels = channelFormat.size();
for (int i = 0; i < numChannels; ++i)
layoutMap[i] = AudioUnitHelpers::juceChannelIndexToAu (i, channelFormat);
}
};
//==============================================================================
class CoreAudioBufferList
{
public:
CoreAudioBufferList() { reset(); }
//==============================================================================
void prepare (int inChannels, int outChannels, int maxFrames)
{
const int numChannels = jmax (inChannels, outChannels);
scratch.setSize (numChannels, maxFrames);
channels.calloc (static_cast<size_t> (numChannels));
reset();
}
void release()
{
scratch.setSize (0, 0);
channels.free();
}
void reset() noexcept
{
pushIdx = 0;
popIdx = 0;
zeromem (channels.getData(), sizeof(float*) * static_cast<size_t> (scratch.getNumChannels()));
}
//==============================================================================
float* setBuffer (const int idx, float* ptr = nullptr) noexcept
{
jassert (idx < scratch.getNumChannels());
return (channels [idx] = uniqueBuffer (idx, ptr));
}
//==============================================================================
float* push() noexcept
{
jassert (pushIdx < scratch.getNumChannels());
return channels [pushIdx++];
}
void push (AudioBufferList& bufferList, const int* channelMap) noexcept
{
jassert (pushIdx < scratch.getNumChannels());
if (bufferList.mNumberBuffers > 0)
{
const UInt32 n = bufferList.mBuffers [0].mDataByteSize /
(bufferList.mBuffers [0].mNumberChannels * sizeof (float));
const bool isInterleaved = isAudioBufferInterleaved (bufferList);
const int numChannels = static_cast<int> (isInterleaved ? bufferList.mBuffers [0].mNumberChannels
: bufferList.mNumberBuffers);
for (int ch = 0; ch < numChannels; ++ch)
{
float* data = push();
int mappedChannel = channelMap [ch];
if (isInterleaved || static_cast<float*> (bufferList.mBuffers [mappedChannel].mData) != data)
copyAudioBuffer (bufferList, mappedChannel, n, data);
}
}
}
//==============================================================================
float* pop() noexcept
{
jassert (popIdx < scratch.getNumChannels());
return channels[popIdx++];
}
void pop (AudioBufferList& buffer, const int* channelMap) noexcept
{
if (buffer.mNumberBuffers > 0)
{
const UInt32 n = buffer.mBuffers [0].mDataByteSize / (buffer.mBuffers [0].mNumberChannels * sizeof (float));
const bool isInterleaved = isAudioBufferInterleaved (buffer);
const int numChannels = static_cast<int> (isInterleaved ? buffer.mBuffers [0].mNumberChannels : buffer.mNumberBuffers);
for (int ch = 0; ch < numChannels; ++ch)
{
int mappedChannel = channelMap [ch];
float* nextBuffer = pop();
if (nextBuffer == buffer.mBuffers [mappedChannel].mData && ! isInterleaved)
continue; // no copying necessary
if (buffer.mBuffers [mappedChannel].mData == nullptr && ! isInterleaved)
buffer.mBuffers [mappedChannel].mData = nextBuffer;
else
copyAudioBuffer (nextBuffer, mappedChannel, n, buffer);
}
}
}
//==============================================================================
AudioSampleBuffer& getBuffer (UInt32 frames) noexcept
{
jassert (pushIdx == scratch.getNumChannels());
#if JUCE_DEBUG
for (int i = 0; i < pushIdx; ++i)
jassert (channels [i] != nullptr);
#endif
mutableBuffer.setDataToReferTo (channels, pushIdx, static_cast<int> (frames));
return mutableBuffer;
}
private:
float* uniqueBuffer (int idx, float* buffer) noexcept
{
if (buffer == nullptr)
return scratch.getWritePointer (idx);
for (int ch = 0; ch < idx; ++ch)
if (buffer == channels[ch])
return scratch.getWritePointer (idx);
return buffer;
}
//==============================================================================
AudioSampleBuffer scratch;
AudioSampleBuffer mutableBuffer;
HeapBlock<float*> channels;
int pushIdx, popIdx;
};
static bool isAudioBufferInterleaved (const AudioBufferList& audioBuffer) noexcept
{
return (audioBuffer.mNumberBuffers == 1 && audioBuffer.mBuffers[0].mNumberChannels > 1);
}
static void clearAudioBuffer (const AudioBufferList& audioBuffer) noexcept
{
for (unsigned int ch = 0; ch < audioBuffer.mNumberBuffers; ++ch)
zeromem (audioBuffer.mBuffers[ch].mData, audioBuffer.mBuffers[ch].mDataByteSize);
}
static void copyAudioBuffer (const AudioBufferList& audioBuffer, const int channel, const UInt32 size, float* dst) noexcept
{
if (! isAudioBufferInterleaved (audioBuffer))
{
jassert (channel < static_cast<int> (audioBuffer.mNumberBuffers));
jassert (audioBuffer.mBuffers[channel].mDataByteSize == (size * sizeof (float)));
memcpy (dst, audioBuffer.mBuffers[channel].mData, size * sizeof (float));
}
else
{
const int numChannels = static_cast<int> (audioBuffer.mBuffers[0].mNumberChannels);
const UInt32 n = static_cast<UInt32> (numChannels) * size;
const float* src = static_cast<const float*> (audioBuffer.mBuffers[0].mData);
jassert (channel < numChannels);
jassert (audioBuffer.mBuffers[0].mDataByteSize == (n * sizeof (float)));
for (const float* inData = src; inData < (src + n); inData += numChannels)
*dst++ = inData[channel];
}
}
static void copyAudioBuffer (const float *src, const int channel, const UInt32 size, AudioBufferList& audioBuffer) noexcept
{
if (! isAudioBufferInterleaved (audioBuffer))
{
jassert (channel < static_cast<int> (audioBuffer.mNumberBuffers));
jassert (audioBuffer.mBuffers[channel].mDataByteSize == (size * sizeof (float)));
memcpy (audioBuffer.mBuffers[channel].mData, src, size * sizeof (float));
}
else
{
const int numChannels = static_cast<int> (audioBuffer.mBuffers[0].mNumberChannels);
const UInt32 n = static_cast<UInt32> (numChannels) * size;
float* dst = static_cast<float*> (audioBuffer.mBuffers[0].mData);
jassert (channel < numChannels);
jassert (audioBuffer.mBuffers[0].mDataByteSize == (n * sizeof (float)));
for (float* outData = dst; outData < (dst + n); outData += numChannels)
outData[channel] = *src++;
}
}
template <int numLayouts>
static bool isLayoutSupported (const AudioProcessor& processor,
bool isInput, int busIdx,
int numChannels,
const short (&channelLayoutList) [numLayouts][2],
bool hasLayoutMap = true)
{
if (const AudioProcessor::Bus* bus = processor.getBus (isInput, busIdx))
{
if (! bus->isNumberOfChannelsSupported (numChannels))
return false;
if (! hasLayoutMap)
return true;
const int numConfigs = sizeof (channelLayoutList) / sizeof (short[2]);
for (int i = 0; i < numConfigs; ++i)
{
if (channelLayoutList[i][isInput ? 0 : 1] == numChannels)
return true;
}
}
return false;
}
static Array<AUChannelInfo> getAUChannelInfo (const AudioProcessor& processor)
{
Array<AUChannelInfo> channelInfo;
const bool hasMainInputBus = (processor.getBusCount (true) > 0);
const bool hasMainOutputBus = (processor.getBusCount (false) > 0);
if ((! hasMainInputBus) && (! hasMainOutputBus))
{
// midi effect plug-in: no audio
AUChannelInfo info;
info.inChannels = 0;
info.outChannels = 0;
channelInfo.add (info);
return channelInfo;
}
else
{
const uint32_t maxNumChanToCheckFor = 9;
uint32_t defaultInputs = static_cast<uint32_t> (processor.getChannelCountOfBus (true, 0));
uint32_t defaultOutputs = static_cast<uint32_t> (processor.getChannelCountOfBus (false, 0));
uint32_t lastInputs = defaultInputs;
uint32_t lastOutputs = defaultOutputs;
SortedSet<uint32_t> supportedChannels;
// add the current configuration
if (lastInputs != 0 || lastOutputs != 0)
supportedChannels.add ((lastInputs << 16) | lastOutputs);
for (uint32_t inChanNum = hasMainInputBus ? 1 : 0; inChanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++inChanNum)
{
const AudioProcessor::Bus* inBus = processor.getBus (true, 0);
if (inBus != nullptr && (! inBus->isNumberOfChannelsSupported ((int) inChanNum)))
continue;
for (uint32_t outChanNum = hasMainOutputBus ? 1 : 0; outChanNum <= (hasMainOutputBus ? maxNumChanToCheckFor : 0); ++outChanNum)
{
const AudioProcessor::Bus* outBus = processor.getBus (false, 0);
if (outBus != nullptr && (! outBus->isNumberOfChannelsSupported ((int) outChanNum)))
continue;
uint32_t channelConfiguration = (inChanNum << 16) | outChanNum;
// did we already try this configuration?
if (supportedChannels.contains (channelConfiguration)) continue;
if (lastInputs != inChanNum && (inChanNum > 0 && inBus != nullptr))
{
AudioChannelSet set = inBus->supportedLayoutWithChannels ((int) inChanNum);
AudioProcessor::BusesLayout layouts = inBus->getBusesLayoutForLayoutChangeOfBus (set);
lastInputs = inChanNum;
lastOutputs = hasMainOutputBus ? static_cast<uint32_t> (layouts.outputBuses.getReference (0).size()) : 0;
supportedChannels.add ((lastInputs << 16) | lastOutputs);
}
if (lastOutputs != outChanNum && (outChanNum > 0 && outBus != nullptr))
{
AudioChannelSet set = outBus->supportedLayoutWithChannels ((int) outChanNum);
AudioProcessor::BusesLayout layouts = outBus->getBusesLayoutForLayoutChangeOfBus (set);
lastOutputs = outChanNum;
lastInputs = hasMainInputBus ? static_cast<uint32_t> (layouts.inputBuses.getReference (0).size()) : 0;
supportedChannels.add ((lastInputs << 16) | lastOutputs);
}
}
}
bool hasInOutMismatch = false;
for (int i = 0; i < supportedChannels.size(); ++i)
{
const uint32_t numInputs = (supportedChannels[i] >> 16) & 0xffff;
const uint32_t numOutputs = (supportedChannels[i] >> 0) & 0xffff;
if (numInputs != numOutputs)
{
hasInOutMismatch = true;
break;
}
}
bool hasUnsupportedInput = ! hasMainOutputBus, hasUnsupportedOutput = ! hasMainInputBus;
for (uint32_t inChanNum = hasMainInputBus ? 1 : 0; inChanNum <= (hasMainInputBus ? maxNumChanToCheckFor : 0); ++inChanNum)
{
uint32_t channelConfiguration = (inChanNum << 16) | (hasInOutMismatch ? defaultOutputs : inChanNum);
if (! supportedChannels.contains (channelConfiguration))
{
hasUnsupportedInput = true;
break;
}
}
for (uint32_t outChanNum = hasMainOutputBus ? 1 : 0; outChanNum <= (hasMainOutputBus ? maxNumChanToCheckFor : 0); ++outChanNum)
{
uint32_t channelConfiguration = ((hasInOutMismatch ? defaultInputs : outChanNum) << 16) | outChanNum;
if (! supportedChannels.contains (channelConfiguration))
{
hasUnsupportedOutput = true;
break;
}
}
for (int i = 0; i < supportedChannels.size(); ++i)
{
const int numInputs = (supportedChannels[i] >> 16) & 0xffff;
const int numOutputs = (supportedChannels[i] >> 0) & 0xffff;
AUChannelInfo info;
// see here: https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide/TheAudioUnit/TheAudioUnit.html
info.inChannels = static_cast<SInt16> (hasMainInputBus ? (hasUnsupportedInput ? numInputs : (hasInOutMismatch && (! hasUnsupportedOutput) ? -2 : -1)) : 0);
info.outChannels = static_cast<SInt16> (hasMainOutputBus ? (hasUnsupportedOutput ? numOutputs : (hasInOutMismatch && (! hasUnsupportedInput) ? -2 : -1)) : 0);
if (info.inChannels == -2 && info.outChannels == -2)
info.inChannels = -1;
int j;
for (j = 0; j < channelInfo.size(); ++j)
if (channelInfo[j].inChannels == info.inChannels && channelInfo[j].outChannels == info.outChannels)
break;
if (j >= channelInfo.size())
channelInfo.add (info);
}
}
return channelInfo;
}
};
AudioUnitHelpers::AUChannelStreamOrder AudioUnitHelpers::StreamOrder::auChannelStreamOrder[] =
{
{kAudioChannelLayoutTag_Mono, {centre, unknown, unknown, unknown, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_Stereo, {left, right, unknown, unknown, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_StereoHeadphones, {left, right, unknown, unknown, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_Binaural, {left, right, unknown, unknown, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_Quadraphonic, {left, right, leftSurround, rightSurround, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_Pentagonal, {left, right, leftSurroundRear, rightSurroundRear, centre, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_Hexagonal, {left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround, unknown, unknown}},
{kAudioChannelLayoutTag_Octagonal, {left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight}},
{kAudioChannelLayoutTag_Ambisonic_B_Format, {ambisonicW, ambisonicX, ambisonicY, ambisonicZ, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_MPEG_5_0_A, {left, right, centre, leftSurround, rightSurround, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_MPEG_5_0_B, {left, right, leftSurround, rightSurround, centre, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_MPEG_5_1_A, {left, right, centre, LFE, leftSurround, rightSurround, unknown, unknown}},
{kAudioChannelLayoutTag_AudioUnit_6_0, {left, right, leftSurround, rightSurround, centre, centreSurround, unknown, unknown}},
{kAudioChannelLayoutTag_DTS_6_0_A, {left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide, unknown, unknown}}, // TODO check this one
{kAudioChannelLayoutTag_MPEG_6_1_A, {left, right, centre, LFE, leftSurround, rightSurround, centre, unknown}},
{kAudioChannelLayoutTag_DTS_6_1_A, {leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE, unknown}},
{kAudioChannelLayoutTag_AudioUnit_7_0, {left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear, unknown}},
{kAudioChannelLayoutTag_MPEG_7_1_C, {left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear}},
{kAudioChannelLayoutTag_AudioUnit_7_0_Front,{left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre, unknown}},
{kAudioChannelLayoutTag_MPEG_7_1_A, {left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre}},
{kAudioChannelLayoutTag_DTS_7_1, {leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE}},
{kAudioChannelLayoutTag_MPEG_3_0_A, {left, right, centre, unknown, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_MPEG_3_0_B, {centre, left, right, unknown, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_MPEG_4_0_A, {left, right, centre, centreSurround, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_MPEG_4_0_B, {centre, left, right, centreSurround, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_ITU_2_1, {left, right, centreSurround, unknown, unknown, unknown, unknown, unknown}},
{kAudioChannelLayoutTag_EAC3_7_1_C, {left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide}},
{unknown, {unknown,unknown,unknown,unknown,unknown,unknown,unknown,unknown}}
};

+ 14
- 4
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h View File

@@ -22,7 +22,7 @@
==============================================================================
*/
#if (JUCE_PLUGINHOST_AU && JUCE_MAC) || DOXYGEN
#if (JUCE_PLUGINHOST_AU && (JUCE_MAC || JUCE_IOS)) || DOXYGEN
//==============================================================================
/**
@@ -38,16 +38,24 @@ public:
//==============================================================================
String getName() const override { return "AudioUnit"; }
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
bool pluginNeedsRescanning (const PluginDescription&) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive, bool) override;
bool doesPluginStillExist (const PluginDescription&) override;
FileSearchPath getDefaultLocationsToSearch() override;
bool canScanForPlugins() const override { return true; }
private:
//==============================================================================
void createPluginInstance (const PluginDescription&,
double initialSampleRate,
int initialBufferSize,
void* userData,
void (*callback) (void*, AudioPluginInstance*, const String&)) override;
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginFormat)
};
@@ -55,8 +63,10 @@ private:
#endif
//==============================================================================
#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200
enum
{
/** Custom AudioUnit property used to indicate MPE support */
kAudioUnitProperty_SupportsMPE = 75001
kAudioUnitProperty_SupportsMPE = 58
};
#endif

+ 882
- 221
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm
File diff suppressed because it is too large
View File


+ 17
- 4
source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp View File

@@ -610,11 +610,14 @@ void LADSPAPluginFormat::findAllTypesForFile (OwnedArray <PluginDescription>& re
}
}
AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const PluginDescription& desc,
double sampleRate, int blockSize)
void LADSPAPluginFormat::createPluginInstance (const PluginDescription& desc,
double sampleRate, int blockSize,
void* userData,
void (*callback) (void*, AudioPluginInstance*, const String&))
{
ScopedPointer<LADSPAPluginInstance> result;
if (fileMightContainThisPluginType (desc.fileOrIdentifier))
{
File file (desc.fileOrIdentifier);
@@ -639,7 +642,17 @@ AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const Pl
previousWorkingDirectory.setAsCurrentWorkingDirectory();
}
return result.release();
String errorMsg;
if (result == nullptr)
errorMsg = String (NEEDS_TRANS ("Unable to load XXX plug-in file")).replace ("XXX", "LADSPA");
callback (userData, result.release(), errorMsg);
}
bool LADSPAPluginFormat::requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept
{
return false;
}
bool LADSPAPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier)
@@ -663,7 +676,7 @@ bool LADSPAPluginFormat::doesPluginStillExist (const PluginDescription& desc)
return File::createFileWithoutCheckingPath (desc.fileOrIdentifier).exists();
}
StringArray LADSPAPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive)
StringArray LADSPAPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive, bool)
{
StringArray results;


+ 9
- 3
source/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.h View File

@@ -31,22 +31,28 @@
class JUCE_API LADSPAPluginFormat : public AudioPluginFormat
{
public:
//==============================================================================
LADSPAPluginFormat();
~LADSPAPluginFormat();
//==============================================================================
String getName() const override { return "LADSPA"; }
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
bool pluginNeedsRescanning (const PluginDescription&) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive, bool) override;
bool doesPluginStillExist (const PluginDescription&) override;
FileSearchPath getDefaultLocationsToSearch() override;
bool canScanForPlugins() const override { return true; }
private:
//==============================================================================
void createPluginInstance (const PluginDescription&, double initialSampleRate,
int initialBufferSize, void* userData,
void (*callback) (void*, AudioPluginInstance*, const String&)) override;
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override;
private:
void recursiveFileSearch (StringArray&, const File&, bool recursive);


+ 72
- 34
source/modules/juce_audio_processors/format_types/juce_VST3Common.h View File

@@ -67,17 +67,17 @@ inline juce::String toString (const Steinberg::char16* string) noexcept { re
inline juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
inline juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast<const Steinberg::char16*> (string)); }
static void toString128 (Steinberg::Vst::String128 result, const char* source)
inline void toString128 (Steinberg::Vst::String128 result, const char* source)
{
Steinberg::UString (result, 128).fromAscii (source);
}
static void toString128 (Steinberg::Vst::String128 result, const juce::String& source)
inline void toString128 (Steinberg::Vst::String128 result, const juce::String& source)
{
Steinberg::UString (result, 128).fromAscii (source.toUTF8());
}
static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept
inline Steinberg::Vst::TChar* toString (const juce::String& source) noexcept
{
return reinterpret_cast<Steinberg::Vst::TChar*> (source.toUTF16().getAddress());
}
@@ -146,22 +146,22 @@ static inline Steinberg::Vst::Speaker getSpeakerType (AudioChannelSet::ChannelTy
case AudioChannelSet::left: return kSpeakerL;
case AudioChannelSet::right: return kSpeakerR;
case AudioChannelSet::centre: return kSpeakerC;
case AudioChannelSet::subbass: return kSpeakerLfe;
case AudioChannelSet::surroundLeft: return kSpeakerLs;
case AudioChannelSet::surroundRight: return kSpeakerRs;
case AudioChannelSet::centreLeft: return kSpeakerLc;
case AudioChannelSet::centreRight: return kSpeakerRc;
case AudioChannelSet::surround: return kSpeakerS;
case AudioChannelSet::sideLeft: return kSpeakerSl;
case AudioChannelSet::sideRight: return kSpeakerSr;
case AudioChannelSet::topMiddle: return kSpeakerTm;
case AudioChannelSet::LFE: return kSpeakerLfe;
case AudioChannelSet::leftSurround: return kSpeakerLs;
case AudioChannelSet::rightSurround: return kSpeakerRs;
case AudioChannelSet::leftCentre: return kSpeakerLc;
case AudioChannelSet::rightCentre: return kSpeakerRc;
case AudioChannelSet::centreSurround: return kSpeakerCs;
case AudioChannelSet::leftSurroundRear: return kSpeakerSl;
case AudioChannelSet::rightSurroundRear: return kSpeakerSr;
case AudioChannelSet::topMiddle: return (1 << 11); /* kSpeakerTm */
case AudioChannelSet::topFrontLeft: return kSpeakerTfl;
case AudioChannelSet::topFrontCentre: return kSpeakerTfc;
case AudioChannelSet::topFrontRight: return kSpeakerTfr;
case AudioChannelSet::topRearLeft: return kSpeakerTrl;
case AudioChannelSet::topRearCentre: return kSpeakerTrc;
case AudioChannelSet::topRearRight: return kSpeakerTrr;
case AudioChannelSet::subbass2: return kSpeakerLfe2;
case AudioChannelSet::LFE2: return kSpeakerLfe2;
default: break;
}
@@ -177,30 +177,49 @@ static inline AudioChannelSet::ChannelType getChannelType (Steinberg::Vst::Speak
case kSpeakerL: return AudioChannelSet::left;
case kSpeakerR: return AudioChannelSet::right;
case kSpeakerC: return AudioChannelSet::centre;
case kSpeakerLfe: return AudioChannelSet::subbass;
case kSpeakerLs: return AudioChannelSet::surroundLeft;
case kSpeakerRs: return AudioChannelSet::surroundRight;
case kSpeakerLc: return AudioChannelSet::centreLeft;
case kSpeakerRc: return AudioChannelSet::centreRight;
case kSpeakerS: return AudioChannelSet::surround;
case kSpeakerSl: return AudioChannelSet::sideLeft;
case kSpeakerSr: return AudioChannelSet::sideRight;
case kSpeakerTm: return AudioChannelSet::topMiddle;
case kSpeakerLfe: return AudioChannelSet::LFE;
case kSpeakerLs: return AudioChannelSet::leftSurround;
case kSpeakerRs: return AudioChannelSet::rightSurround;
case kSpeakerLc: return AudioChannelSet::leftCentre;
case kSpeakerRc: return AudioChannelSet::rightCentre;
case kSpeakerCs: return AudioChannelSet::centreSurround;
case kSpeakerSl: return AudioChannelSet::leftSurroundRear;
case kSpeakerSr: return AudioChannelSet::rightSurroundRear;
case (1 << 11): return AudioChannelSet::topMiddle; /* kSpeakerTm */
case kSpeakerTfl: return AudioChannelSet::topFrontLeft;
case kSpeakerTfc: return AudioChannelSet::topFrontCentre;
case kSpeakerTfr: return AudioChannelSet::topFrontRight;
case kSpeakerTrl: return AudioChannelSet::topRearLeft;
case kSpeakerTrc: return AudioChannelSet::topRearCentre;
case kSpeakerTrr: return AudioChannelSet::topRearRight;
case kSpeakerLfe2: return AudioChannelSet::subbass2;
case kSpeakerLfe2: return AudioChannelSet::LFE2;
default: break;
}
return AudioChannelSet::unknown;
}
static inline Steinberg::Vst::SpeakerArrangement getSpeakerArrangement (const AudioChannelSet& channels) noexcept
static inline Steinberg::Vst::SpeakerArrangement getVst3SpeakerArrangement (const AudioChannelSet& channels) noexcept
{
if (channels == AudioChannelSet::disabled()) return Steinberg::Vst::SpeakerArr::kEmpty;
else if (channels == AudioChannelSet::mono()) return Steinberg::Vst::SpeakerArr::kMono;
else if (channels == AudioChannelSet::stereo()) return Steinberg::Vst::SpeakerArr::kStereo;
else if (channels == AudioChannelSet::createLCR()) return Steinberg::Vst::SpeakerArr::k30Cine;
else if (channels == AudioChannelSet::createLRS()) return Steinberg::Vst::SpeakerArr::k30Music;
else if (channels == AudioChannelSet::createLCRS()) return Steinberg::Vst::SpeakerArr::k40Cine;
else if (channels == AudioChannelSet::create5point0()) return Steinberg::Vst::SpeakerArr::k50;
else if (channels == AudioChannelSet::create5point1()) return Steinberg::Vst::SpeakerArr::k51;
else if (channels == AudioChannelSet::create6point0()) return Steinberg::Vst::SpeakerArr::k60Cine;
else if (channels == AudioChannelSet::create6point1()) return Steinberg::Vst::SpeakerArr::k61Cine;
else if (channels == AudioChannelSet::create6point0Music()) return Steinberg::Vst::SpeakerArr::k60Music;
else if (channels == AudioChannelSet::create6point1Music()) return Steinberg::Vst::SpeakerArr::k61Music;
else if (channels == AudioChannelSet::create7point0()) return Steinberg::Vst::SpeakerArr::k70Music;
else if (channels == AudioChannelSet::create7point0SDDS()) return Steinberg::Vst::SpeakerArr::k70Cine;
else if (channels == AudioChannelSet::create7point1()) return Steinberg::Vst::SpeakerArr::k71CineSideFill;
else if (channels == AudioChannelSet::create7point1SDDS()) return Steinberg::Vst::SpeakerArr::k71Cine;
else if (channels == AudioChannelSet::ambisonic()) return Steinberg::Vst::SpeakerArr::kBFormat;
else if (channels == AudioChannelSet::quadraphonic()) return Steinberg::Vst::SpeakerArr::k40Music;
Steinberg::Vst::SpeakerArrangement result = 0;
Array<AudioChannelSet::ChannelType> types (channels.getChannelTypes());
@@ -213,6 +232,25 @@ static inline Steinberg::Vst::SpeakerArrangement getSpeakerArrangement (const Au
static inline AudioChannelSet getChannelSetForSpeakerArrangement (Steinberg::Vst::SpeakerArrangement arr) noexcept
{
if (arr == Steinberg::Vst::SpeakerArr::kEmpty) return AudioChannelSet::disabled();
else if (arr == Steinberg::Vst::SpeakerArr::kMono) return AudioChannelSet::mono();
else if (arr == Steinberg::Vst::SpeakerArr::kStereo) return AudioChannelSet::stereo();
else if (arr == Steinberg::Vst::SpeakerArr::k30Cine) return AudioChannelSet::createLCR();
else if (arr == Steinberg::Vst::SpeakerArr::k30Music) return AudioChannelSet::createLRS();
else if (arr == Steinberg::Vst::SpeakerArr::k40Cine) return AudioChannelSet::createLCRS();
else if (arr == Steinberg::Vst::SpeakerArr::k50) return AudioChannelSet::create5point0();
else if (arr == Steinberg::Vst::SpeakerArr::k51) return AudioChannelSet::create5point1();
else if (arr == Steinberg::Vst::SpeakerArr::k60Cine) return AudioChannelSet::create6point0();
else if (arr == Steinberg::Vst::SpeakerArr::k61Cine) return AudioChannelSet::create6point1();
else if (arr == Steinberg::Vst::SpeakerArr::k60Music) return AudioChannelSet::create6point0Music();
else if (arr == Steinberg::Vst::SpeakerArr::k61Music) return AudioChannelSet::create6point1Music();
else if (arr == Steinberg::Vst::SpeakerArr::k70Music) return AudioChannelSet::create7point0();
else if (arr == Steinberg::Vst::SpeakerArr::k70Cine) return AudioChannelSet::create7point0SDDS();
else if (arr == Steinberg::Vst::SpeakerArr::k71CineSideFill) return AudioChannelSet::create7point1();
else if (arr == Steinberg::Vst::SpeakerArr::k71Cine) return AudioChannelSet::create7point1SDDS();
else if (arr == Steinberg::Vst::SpeakerArr::kBFormat) return AudioChannelSet::ambisonic();
else if (arr == Steinberg::Vst::SpeakerArr::k40Music) return AudioChannelSet::quadraphonic();
AudioChannelSet result;
for (Steinberg::Vst::Speaker speaker = 1; speaker <= Steinberg::Vst::kSpeakerRcs; speaker <<= 1)
@@ -462,12 +500,12 @@ struct VST3BufferExchange
vstBuffers.silenceFlags = 0;
}
static void mapArrangementToBusses (int& channelIndexOffset, int index,
static void mapArrangementToBuses (int& channelIndexOffset, int index,
Array<Steinberg::Vst::AudioBusBuffers>& result,
BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement,
BusMap& busMapToUse, const AudioChannelSet& arrangement,
AudioBuffer<FloatType>& source)
{
const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits();
const int numChansForBus = arrangement.size();
if (index >= result.size())
result.add (Steinberg::Vst::AudioBusBuffers());
@@ -483,26 +521,26 @@ struct VST3BufferExchange
channelIndexOffset += numChansForBus;
}
static inline void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<Steinberg::Vst::SpeakerArrangement>& arrangements,
static inline void mapBufferToBuses (Array<Steinberg::Vst::AudioBusBuffers>& result, BusMap& busMapToUse,
const Array<AudioChannelSet>& arrangements,
AudioBuffer<FloatType>& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < arrangements.size(); ++i)
mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse,
mapArrangementToBuses (channelIndexOffset, i, result, busMapToUse,
arrangements.getUnchecked (i), source);
}
static inline void mapBufferToBusses (Array<Steinberg::Vst::AudioBusBuffers>& result,
static inline void mapBufferToBuses (Array<Steinberg::Vst::AudioBusBuffers>& result,
Steinberg::Vst::IAudioProcessor& processor,
BusMap& busMapToUse, bool isInput, int numBusses,
BusMap& busMapToUse, bool isInput, int numBuses,
AudioBuffer<FloatType>& source)
{
int channelIndexOffset = 0;
for (int i = 0; i < numBusses; ++i)
mapArrangementToBusses (channelIndexOffset, i,
for (int i = 0; i < numBuses; ++i)
mapArrangementToBuses (channelIndexOffset, i,
result, busMapToUse,
getArrangementForBus (&processor, isInput, i),
source);


+ 9
- 1
source/modules/juce_audio_processors/format_types/juce_VST3Headers.h View File

@@ -47,12 +47,15 @@
#pragma clang diagnostic ignored "-Wdelete-non-virtual-dtor"
#endif
#undef DEVELOPMENT
#define DEVELOPMENT 0 // This avoids a Clang warning in Steinberg code about unused values
/* These files come with the Steinberg VST3 SDK - to get them, you'll need to
visit the Steinberg website and agree to whatever is currently required to
get them.
Then, you'll need to make sure your include path contains your "VST3 SDK"
directory (or whatever you've named it on your machine). The Introjucer has
directory (or whatever you've named it on your machine). The Projucer has
a special box for setting this path.
*/
#if JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY
@@ -78,6 +81,7 @@
#include <pluginterfaces/vst/ivstunits.h>
#include <pluginterfaces/vst/ivstmidicontrollers.h>
#include <public.sdk/source/common/memorystream.h>
#include <public.sdk/source/vst/vsteditcontroller.h>
#else
#if JUCE_MINGW
#define _set_abort_behavior(...)
@@ -128,6 +132,10 @@ namespace Steinberg
#pragma clang diagnostic pop
#endif
#if JUCE_WINDOWS
#include <windows.h>
#endif
//==============================================================================
#undef ASSERT
#undef WARNING


+ 625
- 284
source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp
File diff suppressed because it is too large
View File


+ 16
- 20
source/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h View File

@@ -25,11 +25,12 @@
#ifndef JUCE_VST3PLUGINFORMAT_H_INCLUDED
#define JUCE_VST3PLUGINFORMAT_H_INCLUDED
#if JUCE_PLUGINHOST_VST3
#if (JUCE_PLUGINHOST_VST3 && (JUCE_MAC || JUCE_WINDOWS)) || DOXYGEN
/**
Implements a plugin format for VST3s.
*/
class JUCE_API VST3PluginFormat : public AudioPluginFormat
class JUCE_API VST3PluginFormat : public AudioPluginFormat
{
public:
/** Constructor */
@@ -39,32 +40,27 @@ public:
~VST3PluginFormat();
//==============================================================================
/** @internal */
String getName() const override { return "VST3"; }
/** @internal */
void findAllTypesForFile (OwnedArray<PluginDescription>& results, const String& fileOrIdentifier) override;
/** @internal */
AudioPluginInstance* createInstanceFromDescription (const PluginDescription& description, double, int) override;
/** @internal */
String getName() const override { return "VST3"; }
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
/** @internal */
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
/** @internal */
bool pluginNeedsRescanning (const PluginDescription& description) override;
/** @internal */
StringArray searchPathsForPlugins (const FileSearchPath& searchPath, bool recursive) override;
/** @internal */
bool doesPluginStillExist (const PluginDescription& description) override;
/** @internal */
bool pluginNeedsRescanning (const PluginDescription&) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive, bool) override;
bool doesPluginStillExist (const PluginDescription&) override;
FileSearchPath getDefaultLocationsToSearch() override;
/** @internal */
bool canScanForPlugins() const override { return true; }
bool canScanForPlugins() const override { return true; }
private:
void createPluginInstance (const PluginDescription&, double initialSampleRate,
int initialBufferSize, void* userData,
void (*callback) (void*, AudioPluginInstance*, const String&)) override;
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override;
private:
//==============================================================================
void recursiveFileSearch (StringArray&, const File&, bool recursive);
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat)
};


+ 238
- 0
source/modules/juce_audio_processors/format_types/juce_VSTCommon.h View File

@@ -0,0 +1,238 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2015 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_VSTCOMMON_H_INCLUDED
#define JUCE_VSTCOMMON_H_INCLUDED
//==============================================================================
struct SpeakerMappings : private AudioChannelSet // (inheritance only to give easier access to items in the namespace)
{
struct Mapping
{
int32 vst2;
ChannelType channels[13];
bool matches (const Array<ChannelType>& chans) const noexcept
{
const int n = sizeof (channels) / sizeof (ChannelType);
for (int i = 0; i < n; ++i)
{
if (channels[i] == unknown) return (i == chans.size());
if (i == chans.size()) return (channels[i] == unknown);
if (channels[i] != chans.getUnchecked(i))
return false;
}
return true;
}
};
static AudioChannelSet vstArrangementTypeToChannelSet (int32 arr, int fallbackNumChannels)
{
if (arr == vstSpeakerConfigTypeEmpty) return AudioChannelSet::disabled();
else if (arr == vstSpeakerConfigTypeMono) return AudioChannelSet::mono();
else if (arr == vstSpeakerConfigTypeLR) return AudioChannelSet::stereo();
else if (arr == vstSpeakerConfigTypeLRC) return AudioChannelSet::createLCR();
else if (arr == vstSpeakerConfigTypeLRS) return AudioChannelSet::createLRS();
else if (arr == vstSpeakerConfigTypeLRCS) return AudioChannelSet::createLCRS();
else if (arr == vstSpeakerConfigTypeLRCLsRs) return AudioChannelSet::create5point0();
else if (arr == vstSpeakerConfigTypeLRCLfeLsRs) return AudioChannelSet::create5point1();
else if (arr == vstSpeakerConfigTypeLRCLsRsCs) return AudioChannelSet::create6point0();
else if (arr == vstSpeakerConfigTypeLRCLfeLsRsCs) return AudioChannelSet::create6point1();
else if (arr == vstSpeakerConfigTypeLRLsRsSlSr) return AudioChannelSet::create6point0Music();
else if (arr == vstSpeakerConfigTypeLRLfeLsRsSlSr) return AudioChannelSet::create6point1Music();
else if (arr == vstSpeakerConfigTypeLRCLsRsSlSr) return AudioChannelSet::create7point0();
else if (arr == vstSpeakerConfigTypeLRCLsRsLcRc) return AudioChannelSet::create7point0SDDS();
else if (arr == vstSpeakerConfigTypeLRCLfeLsRsSlSr) return AudioChannelSet::create7point1();
else if (arr == vstSpeakerConfigTypeLRCLfeLsRsLcRc) return AudioChannelSet::create7point1SDDS();
else if (arr == vstSpeakerConfigTypeLRLsRs) return AudioChannelSet::quadraphonic();
for (const Mapping* m = getMappings(); m->vst2 != vstSpeakerConfigTypeEmpty; ++m)
{
if (m->vst2 == arr)
{
AudioChannelSet s;
for (int i = 0; m->channels[i] != 0; ++i)
s.addChannel (m->channels[i]);
return s;
}
}
return AudioChannelSet::discreteChannels (fallbackNumChannels);
}
static AudioChannelSet vstArrangementTypeToChannelSet (const VstSpeakerConfiguration& arr)
{
return vstArrangementTypeToChannelSet (arr.type, arr.numberOfChannels);
}
static int32 channelSetToVstArrangementType (AudioChannelSet channels)
{
if (channels == AudioChannelSet::disabled()) return vstSpeakerConfigTypeEmpty;
else if (channels == AudioChannelSet::mono()) return vstSpeakerConfigTypeMono;
else if (channels == AudioChannelSet::stereo()) return vstSpeakerConfigTypeLR;
else if (channels == AudioChannelSet::createLCR()) return vstSpeakerConfigTypeLRC;
else if (channels == AudioChannelSet::createLRS()) return vstSpeakerConfigTypeLRS;
else if (channels == AudioChannelSet::createLCRS()) return vstSpeakerConfigTypeLRCS;
else if (channels == AudioChannelSet::create5point0()) return vstSpeakerConfigTypeLRCLsRs;
else if (channels == AudioChannelSet::create5point1()) return vstSpeakerConfigTypeLRCLfeLsRs;
else if (channels == AudioChannelSet::create6point0()) return vstSpeakerConfigTypeLRCLsRsCs;
else if (channels == AudioChannelSet::create6point1()) return vstSpeakerConfigTypeLRCLfeLsRsCs;
else if (channels == AudioChannelSet::create6point0Music()) return vstSpeakerConfigTypeLRLsRsSlSr;
else if (channels == AudioChannelSet::create6point1Music()) return vstSpeakerConfigTypeLRLfeLsRsSlSr;
else if (channels == AudioChannelSet::create7point0()) return vstSpeakerConfigTypeLRCLsRsSlSr;
else if (channels == AudioChannelSet::create7point0SDDS()) return vstSpeakerConfigTypeLRCLsRsLcRc;
else if (channels == AudioChannelSet::create7point1()) return vstSpeakerConfigTypeLRCLfeLsRsSlSr;
else if (channels == AudioChannelSet::create7point1SDDS()) return vstSpeakerConfigTypeLRCLfeLsRsLcRc;
else if (channels == AudioChannelSet::quadraphonic()) return vstSpeakerConfigTypeLRLsRs;
Array<AudioChannelSet::ChannelType> chans (channels.getChannelTypes());
if (channels == AudioChannelSet::disabled())
return vstSpeakerConfigTypeEmpty;
for (const Mapping* m = getMappings(); m->vst2 != vstSpeakerConfigTypeEmpty; ++m)
if (m->matches (chans))
return m->vst2;
return vstSpeakerConfigTypeUser;
}
static void channelSetToVstArrangement (const AudioChannelSet& channels, VstSpeakerConfiguration& result)
{
result.type = channelSetToVstArrangementType (channels);
result.numberOfChannels = channels.size();
for (int i = 0; i < result.numberOfChannels; ++i)
{
VstIndividualSpeakerInfo& speaker = result.speakers[i];
zeromem (&speaker, sizeof (VstIndividualSpeakerInfo));
speaker.type = getSpeakerType (channels.getTypeOfChannel (i));
}
}
static const Mapping* getMappings() noexcept
{
static const Mapping mappings[] =
{
{ vstSpeakerConfigTypeMono, { centre, unknown } },
{ vstSpeakerConfigTypeLR, { left, right, unknown } },
{ vstSpeakerConfigTypeLsRs, { leftSurround, rightSurround, unknown } },
{ vstSpeakerConfigTypeLcRc, { leftCentre, rightCentre, unknown } },
{ vstSpeakerConfigTypeSlSr, { leftSurroundRear, rightSurroundRear, unknown } },
{ vstSpeakerConfigTypeCLfe, { centre, LFE, unknown } },
{ vstSpeakerConfigTypeLRC, { left, right, centre, unknown } },
{ vstSpeakerConfigTypeLRS, { left, right, surround, unknown } },
{ vstSpeakerConfigTypeLRCLfe, { left, right, centre, LFE, unknown } },
{ vstSpeakerConfigTypeLRLfeS, { left, right, LFE, surround, unknown } },
{ vstSpeakerConfigTypeLRCS, { left, right, centre, surround, unknown } },
{ vstSpeakerConfigTypeLRLsRs, { left, right, leftSurround, rightSurround, unknown } },
{ vstSpeakerConfigTypeLRCLfeS, { left, right, centre, LFE, surround, unknown } },
{ vstSpeakerConfigTypeLRLfeLsRs, { left, right, LFE, leftSurround, rightSurround, unknown } },
{ vstSpeakerConfigTypeLRCLsRs, { left, right, centre, leftSurround, rightSurround, unknown } },
{ vstSpeakerConfigTypeLRCLfeLsRs, { left, right, centre, LFE, leftSurround, rightSurround, unknown } },
{ vstSpeakerConfigTypeLRCLsRsCs, { left, right, centre, leftSurround, rightSurround, surround, unknown } },
{ vstSpeakerConfigTypeLRLsRsSlSr, { left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
{ vstSpeakerConfigTypeLRCLfeLsRsCs, { left, right, centre, LFE, leftSurround, rightSurround, surround, unknown } },
{ vstSpeakerConfigTypeLRLfeLsRsSlSr, { left, right, LFE, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
{ vstSpeakerConfigTypeLRCLsRsLcRc, { left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, unknown } },
{ vstSpeakerConfigTypeLRCLsRsSlSr, { left, right, centre, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
{ vstSpeakerConfigTypeLRCLfeLsRsLcRc, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, unknown } },
{ vstSpeakerConfigTypeLRCLfeLsRsSlSr, { left, right, centre, LFE, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, unknown } },
{ vstSpeakerConfigTypeLRCLsRsLcRcCs, { left, right, centre, leftSurround, rightSurround, topFrontLeft, topFrontRight, surround, unknown } },
{ vstSpeakerConfigTypeLRCLsRsCsSlSr, { left, right, centre, leftSurround, rightSurround, surround, leftSurroundRear, rightSurroundRear, unknown } },
{ vstSpeakerConfigTypeLRCLfeLsRsLcRcCs, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontRight, surround, unknown } },
{ vstSpeakerConfigTypeLRCLfeLsRsCsSlSr, { left, right, centre, LFE, leftSurround, rightSurround, surround, leftSurroundRear, rightSurroundRear, unknown } },
{ vstSpeakerConfigTypeLRCLfeLsRsTflTfcTfrTrlTrrLfe2, { left, right, centre, LFE, leftSurround, rightSurround, topFrontLeft, topFrontCentre, topFrontRight, topRearLeft, topRearRight, LFE2, unknown } },
{ vstSpeakerConfigTypeEmpty, { unknown } }
};
return mappings;
}
static inline int32 getSpeakerType (AudioChannelSet::ChannelType type) noexcept
{
switch (type)
{
case AudioChannelSet::left: return vstIndividualSpeakerTypeLeft;
case AudioChannelSet::right: return vstIndividualSpeakerTypeRight;
case AudioChannelSet::centre: return vstIndividualSpeakerTypeCentre;
case AudioChannelSet::LFE: return vstIndividualSpeakerTypeLFE;
case AudioChannelSet::leftSurround: return vstIndividualSpeakerTypeLeftSurround;
case AudioChannelSet::rightSurround: return vstIndividualSpeakerTypeRightSurround;
case AudioChannelSet::leftCentre: return vstIndividualSpeakerTypeLeftCentre;
case AudioChannelSet::rightCentre: return vstIndividualSpeakerTypeRightCentre;
case AudioChannelSet::surround: return vstIndividualSpeakerTypeSurround;
case AudioChannelSet::leftSurroundRear: return vstIndividualSpeakerTypeLeftRearSurround;
case AudioChannelSet::rightSurroundRear: return vstIndividualSpeakerTypeRightRearSurround;
case AudioChannelSet::topMiddle: return vstIndividualSpeakerTypeTopMiddle;
case AudioChannelSet::topFrontLeft: return vstIndividualSpeakerTypeTopFrontLeft;
case AudioChannelSet::topFrontCentre: return vstIndividualSpeakerTypeTopFrontCentre;
case AudioChannelSet::topFrontRight: return vstIndividualSpeakerTypeTopFrontRight;
case AudioChannelSet::topRearLeft: return vstIndividualSpeakerTypeTopRearLeft;
case AudioChannelSet::topRearCentre: return vstIndividualSpeakerTypeTopRearCentre;
case AudioChannelSet::topRearRight: return vstIndividualSpeakerTypeTopRearRight;
case AudioChannelSet::LFE2: return vstIndividualSpeakerTypeLFE2;
default: break;
}
return 0;
}
static inline AudioChannelSet::ChannelType getChannelType (int32 type) noexcept
{
switch (type)
{
case vstIndividualSpeakerTypeLeft: return AudioChannelSet::left;
case vstIndividualSpeakerTypeRight: return AudioChannelSet::right;
case vstIndividualSpeakerTypeCentre: return AudioChannelSet::centre;
case vstIndividualSpeakerTypeLFE: return AudioChannelSet::LFE;
case vstIndividualSpeakerTypeLeftSurround: return AudioChannelSet::leftSurround;
case vstIndividualSpeakerTypeRightSurround: return AudioChannelSet::rightSurround;
case vstIndividualSpeakerTypeLeftCentre: return AudioChannelSet::leftCentre;
case vstIndividualSpeakerTypeRightCentre: return AudioChannelSet::rightCentre;
case vstIndividualSpeakerTypeSurround: return AudioChannelSet::surround;
case vstIndividualSpeakerTypeLeftRearSurround: return AudioChannelSet::leftSurroundRear;
case vstIndividualSpeakerTypeRightRearSurround: return AudioChannelSet::rightSurroundRear;
case vstIndividualSpeakerTypeTopMiddle: return AudioChannelSet::topMiddle;
case vstIndividualSpeakerTypeTopFrontLeft: return AudioChannelSet::topFrontLeft;
case vstIndividualSpeakerTypeTopFrontCentre: return AudioChannelSet::topFrontCentre;
case vstIndividualSpeakerTypeTopFrontRight: return AudioChannelSet::topFrontRight;
case vstIndividualSpeakerTypeTopRearLeft: return AudioChannelSet::topRearLeft;
case vstIndividualSpeakerTypeTopRearCentre: return AudioChannelSet::topRearCentre;
case vstIndividualSpeakerTypeTopRearRight: return AudioChannelSet::topRearRight;
case vstIndividualSpeakerTypeLFE2: return AudioChannelSet::LFE2;
default: break;
}
return AudioChannelSet::unknown;
}
};
#endif // JUCE_VSTCOMMON_H_INCLUDED

+ 458
- 0
source/modules/juce_audio_processors/format_types/juce_VSTInterface.h View File

@@ -0,0 +1,458 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2016 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-----------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#ifndef JUCE_VSTINTERFACE_H_INCLUDED
#define JUCE_VSTINTERFACE_H_INCLUDED
#include "../../juce_core/juce_core.h"
using namespace juce;
#if JUCE_MSVC
#define VSTINTERFACECALL __cdecl
#pragma pack(push)
#pragma pack(8)
#elif JUCE_MAC || JUCE_IOS
#define VSTINTERFACECALL
#if JUCE_64BIT
#pragma options align=power
#else
#pragma options align=mac68k
#endif
#else
#define VSTINTERFACECALL
#pragma pack(push, 8)
#endif
const int32 juceVstInterfaceVersion = 2400;
const int32 juceVstInterfaceIdentifier = 0x56737450; // The "magic" identifier in the SDK is 'VstP'.
//==============================================================================
struct VstEffectInterface
{
int32 interfaceIdentifier;
pointer_sized_int (VSTINTERFACECALL* dispatchFunction) (VstEffectInterface*, int32 op, int32 index, pointer_sized_int value, void* ptr, float opt);
void (VSTINTERFACECALL* processAudioFunction) (VstEffectInterface*, float** inputs, float** outputs, int32 numSamples);
void (VSTINTERFACECALL* setParameterValueFunction) (VstEffectInterface*, int32 parameterIndex, float value);
float (VSTINTERFACECALL* getParameterValueFunction) (VstEffectInterface*, int32 parameterIndex);
int32 numPrograms;
int32 numParameters;
int32 numInputChannels;
int32 numOutputChannels;
int32 flags;
pointer_sized_int hostSpace1;
pointer_sized_int hostSpace2;
int32 latency;
int32 deprecated1;
int32 deprecated2;
float deprecated3;
void* effectPointer;
void* userPointer;
int32 plugInIdentifier;
int32 plugInVersion;
void (VSTINTERFACECALL* processAudioInplaceFunction) (VstEffectInterface*, float** inputs, float** outputs, int32 numSamples);
void (VSTINTERFACECALL* processDoubleAudioInplaceFunction) (VstEffectInterface*, double** inputs, double** outputs, int32 numSamples);
char emptySpace[56];
};
typedef pointer_sized_int (VSTINTERFACECALL* VstHostCallback) (VstEffectInterface*, int32 op, int32 index, pointer_sized_int value, void* ptr, float opt);
enum VstEffectInterfaceFlags
{
vstEffectFlagHasEditor = 1,
vstEffectFlagInplaceAudio = 16,
vstEffectFlagDataInChunks = 32,
vstEffectFlagIsSynth = 256,
vstEffectFlagInplaceDoubleAudio = 4096
};
//==============================================================================
enum VstHostToPlugInOpcodes
{
plugInOpcodeOpen,
plugInOpcodeClose,
plugInOpcodeSetCurrentProgram,
plugInOpcodeGetCurrentProgram,
plugInOpcodeSetCurrentProgramName,
plugInOpcodeGetCurrentProgramName,
plugInOpcodeGetParameterLabel,
plugInOpcodeGetParameterText,
plugInOpcodeGetParameterName,
plugInOpcodeSetSampleRate = plugInOpcodeGetParameterName + 2,
plugInOpcodeSetBlockSize,
plugInOpcodeResumeSuspend,
plugInOpcodeGetEditorBounds,
plugInOpcodeOpenEditor,
plugInOpcodeCloseEditor,
plugInOpcodeDrawEditor,
plugInOpcodeGetMouse,
plugInOpcodeEditorIdle = plugInOpcodeGetMouse + 2,
plugInOpcodeeffEditorTop,
plugInOpcodeSleepEditor,
plugInOpcodeIdentify,
plugInOpcodeGetData,
plugInOpcodeSetData,
plugInOpcodePreAudioProcessingEvents,
plugInOpcodeIsParameterAutomatable,
plugInOpcodeParameterValueForText,
plugInOpcodeGetProgramName = plugInOpcodeParameterValueForText + 2,
plugInOpcodeConnectInput = plugInOpcodeGetProgramName + 2,
plugInOpcodeConnectOutput,
plugInOpcodeGetInputPinProperties,
plugInOpcodeGetOutputPinProperties,
plugInOpcodeGetPlugInCategory,
plugInOpcodeSetSpeakerConfiguration = plugInOpcodeGetPlugInCategory + 7,
plugInOpcodeSetBypass = plugInOpcodeSetSpeakerConfiguration + 2,
plugInOpcodeGetPlugInName,
plugInOpcodeGetManufacturerName = plugInOpcodeGetPlugInName + 2,
plugInOpcodeGetManufacturerProductName,
plugInOpcodeGetManufacturerVersion,
plugInOpcodeManufacturerSpecific,
plugInOpcodeCanPlugInDo,
plugInOpcodeGetTailSize,
plugInOpcodeIdle,
plugInOpcodeKeyboardFocusRequired = plugInOpcodeIdle + 4,
plugInOpcodeGetVstInterfaceVersion,
plugInOpcodeGetCurrentMidiProgram = plugInOpcodeGetVstInterfaceVersion + 5,
plugInOpcodeGetSpeakerArrangement = plugInOpcodeGetCurrentMidiProgram + 6,
plugInOpcodeNextPlugInUniqueID,
plugInOpcodeStartProcess,
plugInOpcodeStopProcess,
plugInOpcodeSetNumberOfSamplesToProcess,
plugInOpcodeSetSampleFloatType = plugInOpcodeSetNumberOfSamplesToProcess + 4,
plugInOpcodeMaximum = plugInOpcodeSetSampleFloatType
};
enum VstPlugInToHostOpcodes
{
hostOpcodeParameterChanged,
hostOpcodeVstVersion,
hostOpcodeCurrentId,
hostOpcodeIdle,
hostOpcodePinConnected,
hostOpcodePlugInWantsMidi = hostOpcodePinConnected + 2,
hostOpcodeGetTimingInfo,
hostOpcodePreAudioProcessingEvents,
hostOpcodeSetTime,
hostOpcodeTempoAt,
hostOpcodeGetNumberOfAutomatableParameters,
hostOpcodeGetParameterInterval,
hostOpcodeIOModified,
hostOpcodeNeedsIdle,
hostOpcodeWindowSize,
hostOpcodeGetSampleRate,
hostOpcodeGetBlockSize,
hostOpcodeGetInputLatency,
hostOpcodeGetOutputLatency,
hostOpcodeGetPreviousPlugIn,
hostOpcodeGetNextPlugIn,
hostOpcodeWillReplace,
hostOpcodeGetCurrentAudioProcessingLevel,
hostOpcodeGetAutomationState,
hostOpcodeOfflineStart,
hostOpcodeOfflineReadSource,
hostOpcodeOfflineWrite,
hostOpcodeOfflineGetCurrentPass,
hostOpcodeOfflineGetCurrentMetaPass,
hostOpcodeSetOutputSampleRate,
hostOpcodeGetOutputSpeakerConfiguration,
hostOpcodeGetManufacturerName,
hostOpcodeGetProductName,
hostOpcodeGetManufacturerVersion,
hostOpcodeManufacturerSpecific,
hostOpcodeSetIcon,
hostOpcodeCanHostDo,
hostOpcodeGetLanguage,
hostOpcodeOpenEditorWindow,
hostOpcodeCloseEditorWindow,
hostOpcodeGetDirectory,
hostOpcodeUpdateView,
hostOpcodeParameterChangeGestureBegin,
hostOpcodeParameterChangeGestureEnd,
};
//==============================================================================
enum VstProcessingSampleType
{
vstProcessingSampleTypeFloat,
vstProcessingSampleTypeDouble
};
//==============================================================================
// These names must be identical to the Steinberg SDK so JUCE users can set
// exactly what they want.
enum VstPlugInCategory
{
kPlugCategUnknown,
kPlugCategEffect,
kPlugCategSynth,
kPlugCategAnalysis,
kPlugCategMastering,
kPlugCategSpacializer,
kPlugCategRoomFx,
kPlugSurroundFx,
kPlugCategRestoration,
kPlugCategOfflineProcess,
kPlugCategShell,
kPlugCategGenerator
};
//==============================================================================
struct VstEditorBounds
{
int16 upper;
int16 leftmost;
int16 lower;
int16 rightmost;
};
//==============================================================================
enum VstMaxStringLengths
{
vstMaxNameLength = 64,
vstMaxParameterOrPinLabelLength = 64,
vstMaxParameterOrPinShortLabelLength = 8,
vstMaxCategoryLength = 24,
vstMaxManufacturerStringLength = 64,
vstMaxPlugInNameStringLength = 64
};
//==============================================================================
struct VstPinInfo
{
char text[vstMaxParameterOrPinLabelLength];
int32 flags;
int32 configurationType;
char shortText[vstMaxParameterOrPinShortLabelLength];
char unused[48];
};
enum VstPinInfoFlags
{
vstPinInfoFlagIsActive = 1,
vstPinInfoFlagIsStereo = 2,
vstPinInfoFlagValid = 4
};
//==============================================================================
struct VstEvent
{
int32 type;
int32 size;
int32 sampleOffset;
int32 flags;
char content[16];
};
enum VstEventTypes
{
vstMidiEventType = 1,
vstSysExEventType = 6
};
struct VstEventBlock
{
int32 numberOfEvents;
pointer_sized_int future;
VstEvent* events[2];
};
struct VstMidiEvent
{
int32 type;
int32 size;
int32 sampleOffset;
int32 flags;
int32 noteSampleLength;
int32 noteSampleOffset;
char midiData[4];
char tuning;
char noteVelocityOff;
char future1;
char future2;
};
enum VstMidiEventFlags
{
vstMidiEventIsRealtime = 1
};
struct VstSysExEvent
{
int32 type;
int32 size;
int32 offsetSamples;
int32 flags;
int32 sysExDumpSize;
pointer_sized_int future1;
char* sysExDump;
pointer_sized_int future2;
};
//==============================================================================
struct VstTimingInformation
{
double samplePosition;
double sampleRate;
double systemTimeNanoseconds;
double musicalPosition;
double tempoBPM;
double lastBarPosition;
double loopStartPosition;
double loopEndPosition;
int32 timeSignatureNumerator;
int32 timeSignatureDenominator;
int32 smpteOffset;
int32 smpteRate;
int32 samplesToNearestClock;
int32 flags;
};
enum VstTimingInformationFlags
{
vstTimingInfoFlagTransportChanged = 1,
vstTimingInfoFlagCurrentlyPlaying = 2,
vstTimingInfoFlagLoopActive = 4,
vstTimingInfoFlagCurrentlyRecording = 8,
vstTimingInfoFlagAutomationWriteModeActive = 64,
vstTimingInfoFlagAutomationReadModeActive = 128,
vstTimingInfoFlagNanosecondsValid = 256,
vstTimingInfoFlagMusicalPositionValid = 512,
vstTimingInfoFlagTempoValid = 1024,
vstTimingInfoFlagLastBarPositionValid = 2056,
vstTimingInfoFlagLoopPositionValid = 4096,
vstTimingInfoFlagTimeSignatureValid = 8192,
vstTimingInfoFlagSmpteValid = 16384,
vstTimingInfoFlagNearestClockValid = 32768
};
//==============================================================================
enum VstSmpteRates
{
vstSmpteRateFps24,
vstSmpteRateFps25,
vstSmpteRateFps2997,
vstSmpteRateFps30,
vstSmpteRateFps2997drop,
vstSmpteRateFps30drop,
vstSmpteRate16mmFilm,
vstSmpteRate35mmFilm,
vstSmpteRateFps239 = vstSmpteRate35mmFilm + 3,
vstSmpteRateFps249 ,
vstSmpteRateFps599,
vstSmpteRateFps60
};
//==============================================================================
struct VstIndividualSpeakerInfo
{
float azimuthalAngle;
float elevationAngle;
float radius;
float reserved;
char label[vstMaxNameLength];
int32 type;
char unused[28];
};
enum VstIndividualSpeakerType
{
vstIndividualSpeakerTypeUndefined = 0x7fffffff,
vstIndividualSpeakerTypeMono = 0,
vstIndividualSpeakerTypeLeft,
vstIndividualSpeakerTypeRight,
vstIndividualSpeakerTypeCentre,
vstIndividualSpeakerTypeLFE,
vstIndividualSpeakerTypeLeftSurround,
vstIndividualSpeakerTypeRightSurround,
vstIndividualSpeakerTypeLeftCentre,
vstIndividualSpeakerTypeRightCentre,
vstIndividualSpeakerTypeSurround,
vstIndividualSpeakerTypeCentreSurround = vstIndividualSpeakerTypeSurround,
vstIndividualSpeakerTypeLeftRearSurround,
vstIndividualSpeakerTypeRightRearSurround,
vstIndividualSpeakerTypeTopMiddle,
vstIndividualSpeakerTypeTopFrontLeft,
vstIndividualSpeakerTypeTopFrontCentre,
vstIndividualSpeakerTypeTopFrontRight,
vstIndividualSpeakerTypeTopRearLeft,
vstIndividualSpeakerTypeTopRearCentre,
vstIndividualSpeakerTypeTopRearRight,
vstIndividualSpeakerTypeLFE2
};
struct VstSpeakerConfiguration
{
int32 type;
int32 numberOfChannels;
VstIndividualSpeakerInfo speakers[8];
};
enum VstSpeakerConfigurationType
{
vstSpeakerConfigTypeUser = -2,
vstSpeakerConfigTypeEmpty = -1,
vstSpeakerConfigTypeMono = 0,
vstSpeakerConfigTypeLR,
vstSpeakerConfigTypeLsRs,
vstSpeakerConfigTypeLcRc,
vstSpeakerConfigTypeSlSr,
vstSpeakerConfigTypeCLfe,
vstSpeakerConfigTypeLRC,
vstSpeakerConfigTypeLRS,
vstSpeakerConfigTypeLRCLfe,
vstSpeakerConfigTypeLRLfeS,
vstSpeakerConfigTypeLRCS,
vstSpeakerConfigTypeLRLsRs,
vstSpeakerConfigTypeLRCLfeS,
vstSpeakerConfigTypeLRLfeLsRs,
vstSpeakerConfigTypeLRCLsRs,
vstSpeakerConfigTypeLRCLfeLsRs,
vstSpeakerConfigTypeLRCLsRsCs,
vstSpeakerConfigTypeLRLsRsSlSr,
vstSpeakerConfigTypeLRCLfeLsRsCs,
vstSpeakerConfigTypeLRLfeLsRsSlSr,
vstSpeakerConfigTypeLRCLsRsLcRc,
vstSpeakerConfigTypeLRCLsRsSlSr,
vstSpeakerConfigTypeLRCLfeLsRsLcRc,
vstSpeakerConfigTypeLRCLfeLsRsSlSr,
vstSpeakerConfigTypeLRCLsRsLcRcCs,
vstSpeakerConfigTypeLRCLsRsCsSlSr,
vstSpeakerConfigTypeLRCLfeLsRsLcRcCs,
vstSpeakerConfigTypeLRCLfeLsRsCsSlSr,
vstSpeakerConfigTypeLRCLfeLsRsTflTfcTfrTrlTrrLfe2
};
#if JUCE_MSVC
#pragma pack(pop)
#elif JUCE_MAC || JUCE_IOS
#pragma options align=reset
#else
#pragma pack(pop)
#endif
#endif // JUCE_VSTINTERFACE_H_INCLUDED

+ 39
- 38
source/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h View File

@@ -22,7 +22,8 @@
==============================================================================
*/
#ifdef __aeffect__ // NB: this must come first, *before* the header-guard.
// NB: this must come first, *before* the header-guard.
#ifdef JUCE_VSTINTERFACE_H_INCLUDED
#ifndef JUCE_VSTMIDIEVENTLIST_H_INCLUDED
#define JUCE_VSTMIDIEVENTLIST_H_INCLUDED
@@ -53,7 +54,7 @@ public:
numEventsUsed = 0;
if (events != nullptr)
events->numEvents = 0;
events->numberOfEvents = 0;
}
void addEvent (const void* const midiData, const int numBytes, const int frameOffset)
@@ -61,65 +62,65 @@ public:
ensureSize (numEventsUsed + 1);
VstMidiEvent* const e = (VstMidiEvent*) (events->events [numEventsUsed]);
events->numEvents = ++numEventsUsed;
events->numberOfEvents = ++numEventsUsed;
if (numBytes <= 4)
{
if (e->type == kVstSysExType)
if (e->type == vstSysExEventType)
{
delete[] (((VstMidiSysexEvent*) e)->sysexDump);
e->type = kVstMidiType;
e->byteSize = sizeof (VstMidiEvent);
e->noteLength = 0;
e->noteOffset = 0;
e->detune = 0;
e->noteOffVelocity = 0;
delete[] (((VstSysExEvent*) e)->sysExDump);
e->type = vstMidiEventType;
e->size = sizeof (VstMidiEvent);
e->noteSampleLength = 0;
e->noteSampleOffset = 0;
e->tuning = 0;
e->noteVelocityOff = 0;
}
e->deltaFrames = frameOffset;
e->sampleOffset = frameOffset;
memcpy (e->midiData, midiData, (size_t) numBytes);
}
else
{
VstMidiSysexEvent* const se = (VstMidiSysexEvent*) e;
VstSysExEvent* const se = (VstSysExEvent*) e;
if (se->type == kVstSysExType)
delete[] se->sysexDump;
if (se->type == vstSysExEventType)
delete[] se->sysExDump;
se->sysexDump = new char [numBytes];
memcpy (se->sysexDump, midiData, (size_t) numBytes);
se->sysExDump = new char [(size_t) numBytes];
memcpy (se->sysExDump, midiData, (size_t) numBytes);
se->type = kVstSysExType;
se->byteSize = sizeof (VstMidiSysexEvent);
se->deltaFrames = frameOffset;
se->type = vstSysExEventType;
se->size = sizeof (VstSysExEvent);
se->offsetSamples = frameOffset;
se->flags = 0;
se->dumpBytes = numBytes;
se->resvd1 = 0;
se->resvd2 = 0;
se->sysExDumpSize = numBytes;
se->future1 = 0;
se->future2 = 0;
}
}
//==============================================================================
// Handy method to pull the events out of an event buffer supplied by the host
// or plugin.
static void addEventsToMidiBuffer (const VstEvents* events, MidiBuffer& dest)
static void addEventsToMidiBuffer (const VstEventBlock* events, MidiBuffer& dest)
{
for (int i = 0; i < events->numEvents; ++i)
for (int i = 0; i < events->numberOfEvents; ++i)
{
const VstEvent* const e = events->events[i];
if (e != nullptr)
{
if (e->type == kVstMidiType)
if (e->type == vstMidiEventType)
{
dest.addEvent ((const juce::uint8*) ((const VstMidiEvent*) e)->midiData,
4, e->deltaFrames);
4, e->sampleOffset);
}
else if (e->type == kVstSysExType)
else if (e->type == vstSysExEventType)
{
dest.addEvent ((const juce::uint8*) ((const VstMidiSysexEvent*) e)->sysexDump,
(int) ((const VstMidiSysexEvent*) e)->dumpBytes,
e->deltaFrames);
dest.addEvent ((const juce::uint8*) ((const VstSysExEvent*) e)->sysExDump,
(int) ((const VstSysExEvent*) e)->sysExDumpSize,
e->sampleOffset);
}
}
}
@@ -160,24 +161,24 @@ public:
}
//==============================================================================
HeapBlock<VstEvents> events;
HeapBlock<VstEventBlock> events;
private:
int numEventsUsed, numEventsAllocated;
static VstEvent* allocateVSTEvent()
{
VstEvent* const e = (VstEvent*) std::calloc (1, sizeof (VstMidiEvent) > sizeof (VstMidiSysexEvent) ? sizeof (VstMidiEvent)
: sizeof (VstMidiSysexEvent));
e->type = kVstMidiType;
e->byteSize = sizeof (VstMidiEvent);
VstEvent* const e = (VstEvent*) std::calloc (1, sizeof (VstMidiEvent) > sizeof (VstSysExEvent) ? sizeof (VstMidiEvent)
: sizeof (VstSysExEvent));
e->type = vstMidiEventType;
e->size = sizeof (VstMidiEvent);
return e;
}
static void freeVSTEvent (VstEvent* e)
{
if (e->type == kVstSysExType)
delete[] (((VstMidiSysexEvent*) e)->sysexDump);
if (e->type == vstSysExEventType)
delete[] (((VstSysExEvent*) e)->sysExDump);
std::free (e);
}


+ 619
- 542
source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp
File diff suppressed because it is too large
View File


+ 24
- 11
source/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.h View File

@@ -22,7 +22,7 @@
==============================================================================
*/
#if JUCE_PLUGINHOST_VST || DOXYGEN
#if (JUCE_PLUGINHOST_VST && (JUCE_MAC || JUCE_WINDOWS || JUCE_LINUX || JUCE_IOS)) || DOXYGEN
//==============================================================================
/**
@@ -36,7 +36,7 @@ public:
~VSTPluginFormat();
//==============================================================================
/** Attempts to retreive the VSTXML data from a plugin.
/** Attempts to retrieve the VSTXML data from a plugin.
Will return nullptr if the plugin isn't a VST, or if it doesn't have any VSTXML.
*/
static const XmlElement* getVSTXML (AudioPluginInstance* plugin);
@@ -53,6 +53,13 @@ public:
/** Attempts to set a VST's state from a chunk of memory. */
static bool setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset);
/** Given a suitable function pointer to a VSTPluginMain function, this will attempt to
instantiate and return a plugin for it.
*/
static AudioPluginInstance* createCustomVSTFromMainCall (void* entryPointFunction,
double initialSampleRate,
int initialBufferSize);
//==============================================================================
/** Base class for some extra functions that can be attached to a VST plugin instance. */
class ExtraFunctions
@@ -75,23 +82,21 @@ public:
static void setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions);
//==============================================================================
#if JUCE_64BIT
typedef int64 VstIntPtr;
#else
typedef int32 VstIntPtr;
#endif
/** This simply calls directly to the VST's AEffect::dispatcher() function. */
static VstIntPtr JUCE_CALLTYPE dispatcher (AudioPluginInstance*, int32, int32, VstIntPtr, void*, float);
static pointer_sized_int JUCE_CALLTYPE dispatcher (AudioPluginInstance*, int32, int32, pointer_sized_int, void*, float);
/** Given a VstEffectInterface* (aka vst::AEffect*), this will return the juce AudioPluginInstance
that is being used to wrap it
*/
static AudioPluginInstance* getPluginInstanceFromVstEffectInterface (void* aEffect);
//==============================================================================
String getName() const override { return "VST"; }
void findAllTypesForFile (OwnedArray<PluginDescription>&, const String& fileOrIdentifier) override;
AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override;
bool fileMightContainThisPluginType (const String& fileOrIdentifier) override;
String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override;
bool pluginNeedsRescanning (const PluginDescription&) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override;
StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive, bool) override;
bool doesPluginStillExist (const PluginDescription&) override;
FileSearchPath getDefaultLocationsToSearch() override;
bool canScanForPlugins() const override { return true; }
@@ -103,6 +108,14 @@ public:
*/
virtual void aboutToScanVSTShellPlugin (const PluginDescription&);
private:
//==============================================================================
void createPluginInstance (const PluginDescription&, double initialSampleRate,
int initialBufferSize, void* userData,
void (*callback) (void*, AudioPluginInstance*, const String&)) override;
bool requiresUnblockedMessageThreadDuringCreation (const PluginDescription&) const noexcept override;
private:
void recursiveFileSearch (StringArray&, const File&, bool recursive);


+ 27
- 13
source/modules/juce_audio_processors/juce_audio_processors.cpp View File

@@ -31,10 +31,15 @@
#error "Incorrect use of JUCE cpp file"
#endif
#include "AppConfig.h"
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
#include "juce_audio_processors.h"
#include "../juce_gui_extra/juce_gui_extra.h"
#if ! JUCE_AUDIO_PROCESSOR_NO_GUI
#include <juce_gui_extra/juce_gui_extra.h>
#endif
//==============================================================================
#if JUCE_MAC
@@ -70,9 +75,18 @@ static inline bool arrayContainsPlugin (const OwnedArray<PluginDescription>& lis
return false;
}
#if JUCE_MAC
#if JUCE_MAC || JUCE_IOS
#if JUCE_IOS
#define JUCE_IOS_MAC_VIEW UIView
typedef UIViewComponent ViewComponentBaseClass;
#else
#define JUCE_IOS_MAC_VIEW NSView
typedef NSViewComponent ViewComponentBaseClass;
#endif
//==============================================================================
struct AutoResizingNSViewComponent : public NSViewComponent,
private AsyncUpdater {
AutoResizingNSViewComponent();
@@ -84,7 +98,7 @@ struct AutoResizingNSViewComponent : public NSViewComponent,
struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent,
private Timer {
AutoResizingNSViewComponentWithParent();
NSView* getChildView() const;
JUCE_IOS_MAC_VIEW* getChildView() const;
void timerCallback() override;
};
@@ -112,27 +126,29 @@ void AutoResizingNSViewComponent::handleAsyncUpdate()
resizeToFitView();
}
//==============================================================================
AutoResizingNSViewComponentWithParent::AutoResizingNSViewComponentWithParent()
{
NSView* v = [[NSView alloc] init];
JUCE_IOS_MAC_VIEW* v = [[JUCE_IOS_MAC_VIEW alloc] init];
setView (v);
[v release];
startTimer(500);
startTimer(30);
}
NSView* AutoResizingNSViewComponentWithParent::getChildView() const
JUCE_IOS_MAC_VIEW* AutoResizingNSViewComponentWithParent::getChildView() const
{
if (NSView* parent = (NSView*)getView())
if (JUCE_IOS_MAC_VIEW* parent = (JUCE_IOS_MAC_VIEW*)getView())
if ([[parent subviews] count] > 0)
return [[parent subviews] objectAtIndex: 0];
return nil;
}
void AutoResizingNSViewComponentWithParent::timerCallback()
{
if (NSView* child = getChildView())
if (JUCE_IOS_MAC_VIEW* child = getChildView())
{
stopTimer();
setView(child);
@@ -148,12 +164,10 @@ void AutoResizingNSViewComponentWithParent::timerCallback()
#include "format/juce_AudioPluginFormat.cpp"
#include "format/juce_AudioPluginFormatManager.cpp"
#include "processors/juce_AudioProcessor.cpp"
#include "processors/juce_AudioChannelSet.cpp"
#include "processors/juce_AudioProcessorEditor.cpp"
#include "processors/juce_AudioProcessorGraph.cpp"
#include "processors/juce_GenericAudioProcessorEditor.cpp"
#include "processors/juce_PluginDescription.cpp"
#include "processors/AudioProcessorGraphMultiThreaded.cpp"
#include "format_types/juce_LADSPAPluginFormat.cpp"
#include "format_types/juce_VSTPluginFormat.cpp"
#include "format_types/juce_VST3PluginFormat.cpp"


+ 34
- 7
source/modules/juce_audio_processors/juce_audio_processors.h View File

@@ -22,17 +22,42 @@
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.txt file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_audio_processors
vendor: juce
version: 4.3.0
name: JUCE audio processor classes
description: Classes for loading and playing VST, AU, or internally-generated audio processors.
website: http://www.juce.com/juce
license: GPL/Commercial
dependencies: juce_gui_extra, juce_audio_basics
OSXFrameworks: CoreAudio CoreMIDI AudioToolbox
iOSFrameworks: AudioToolbox
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#ifndef JUCE_AUDIO_PROCESSORS_H_INCLUDED
#define JUCE_AUDIO_PROCESSORS_H_INCLUDED
#include "../juce_gui_basics/juce_gui_basics.h"
#include "../juce_audio_basics/juce_audio_basics.h"
#include <juce_gui_basics/juce_gui_basics.h>
#include <juce_audio_basics/juce_audio_basics.h>
//==============================================================================
/** Config: JUCE_PLUGINHOST_VST
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be
installed on your machine.
Enables the VST audio plugin hosting classes.
@see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3
*/
@@ -63,10 +88,14 @@
// #error "You need to set either the JUCE_PLUGINHOST_AU and/or JUCE_PLUGINHOST_VST and/or JUCE_PLUGINHOST_VST3 flags if you're using this module!"
#endif
#if ! (defined (JUCE_SUPPORT_CARBON) || JUCE_64BIT)
#if ! (defined (JUCE_SUPPORT_CARBON) || JUCE_64BIT || JUCE_IOS)
#define JUCE_SUPPORT_CARBON 1
#endif
#ifndef JUCE_SUPPORT_LEGACY_AUDIOPROCESSOR
#define JUCE_SUPPORT_LEGACY_AUDIOPROCESSOR 1
#endif
//==============================================================================
//==============================================================================
namespace juce
@@ -77,13 +106,11 @@ class AudioProcessor;
#include "processors/juce_AudioProcessorEditor.h"
#include "processors/juce_AudioProcessorListener.h"
#include "processors/juce_AudioProcessorParameter.h"
#include "processors/juce_AudioChannelSet.h"
#include "processors/juce_AudioProcessor.h"
#include "processors/juce_PluginDescription.h"
#include "processors/juce_AudioPluginInstance.h"
#include "processors/juce_AudioProcessorGraph.h"
#include "processors/juce_GenericAudioProcessorEditor.h"
#include "processors/AudioProcessorGraphMultiThreaded.h"
#include "format/juce_AudioPluginFormat.h"
#include "format/juce_AudioPluginFormatManager.h"
#include "scanning/juce_KnownPluginList.h"


+ 3
- 0
source/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h View File

@@ -78,6 +78,9 @@ public:
protected:
//==============================================================================
AudioPluginInstance() {}
AudioPluginInstance (const BusesProperties& ioLayouts) : AudioProcessor (ioLayouts) {}
template <int numLayouts>
AudioPluginInstance (const short channelLayoutList[numLayouts][2]) : AudioProcessor (channelLayoutList) {}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance)
};


+ 773
- 157
source/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp
File diff suppressed because it is too large
View File


+ 628
- 83
source/modules/juce_audio_processors/processors/juce_AudioProcessor.h View File

@@ -25,6 +25,7 @@
#ifndef JUCE_AUDIOPROCESSOR_H_INCLUDED
#define JUCE_AUDIOPROCESSOR_H_INCLUDED
struct PluginBusUtilities;
//==============================================================================
/**
@@ -43,10 +44,41 @@
class JUCE_API AudioProcessor
{
protected:
struct BusesProperties;
//==============================================================================
/** Constructor. */
/** Constructor.
This constructor will create a main input and output bus which are diabled
by default. If you need more fine grain control then use the other
constructors.
*/
AudioProcessor();
/** Constructor for multibus AudioProcessors
If your AudioProcessor supports multiple buses than use this constructor
to initialise the bus layouts and bus names of your plug-in.
*/
AudioProcessor (const BusesProperties& ioLayouts);
/** Constructor for AudioProcessors which use layout maps
If your AudioProcessor uses layout maps then use this constructor.
*/
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS
AudioProcessor (const std::initializer_list<const short[2]>& channelLayoutList)
{
initialise (busesPropertiesFromLayoutArray (layoutListToArray (channelLayoutList)));
}
#else
template <int numLayouts>
AudioProcessor (const short channelLayoutList[numLayouts][2])
{
initialise (busesPropertiesFromLayoutArray (layoutListToArray (channelLayoutList)));
}
#endif
public:
//==============================================================================
enum ProcessingPrecision
@@ -70,18 +102,20 @@ public:
playback stops.
You can call getTotalNumInputChannels and getTotalNumOutputChannels
or query the busArrangement member variable to find out the number of
or query the busLayout member variable to find out the number of
channels your processBlock callback must process.
The estimatedSamplesPerBlock value is a HINT about the typical number of
samples that will be processed for each callback, but isn't any kind
of guarantee. The actual block sizes that the host uses may be different
each time the callback happens, and may be more or less than this value.
The maximumExpectedSamplesPerBlock value is a strong hint about the maximum
number of samples that will be provided in each block. You may want to use
this value to resize internal buffers. You should program defensively in
case a buggy host exceeds this value. The actual block sizes that the host
uses may be different each time the callback happens: completely variable
block sizes can be expected from some hosts.
@see busArrangement, getTotalNumInputChannels, getTotalNumOutputChannels
@see busLayout, getTotalNumInputChannels, getTotalNumOutputChannels
*/
virtual void prepareToPlay (double sampleRate,
int estimatedSamplesPerBlock) = 0;
int maximumExpectedSamplesPerBlock) = 0;
/** Called after playback has stopped, to let the filter free up any resources it
no longer needs.
@@ -107,7 +141,7 @@ public:
If your plug-in has more than one input or output buses then the buffer passed
to the processBlock methods will contain a bundle of all channels of each bus.
Use AudioBusArrangement::getBusBuffer to obtain an audio buffer for a
Use AudiobusLayout::getBusBuffer to obtain an audio buffer for a
particular bus.
Note that if you have more outputs than inputs, then only those channels that
@@ -144,7 +178,7 @@ public:
processBlock() method to send out an asynchronous message. You could also use
the AsyncUpdater class in a similar way.
@see AudioBusArrangement::getBusBuffer
@see AudiobusLayout::getBusBuffer
*/
virtual void processBlock (AudioBuffer<float>& buffer,
@@ -169,7 +203,7 @@ public:
If your plug-in has more than one input or output buses then the buffer passed
to the processBlock methods will contain a bundle of all channels of
each bus. Use AudioBusArrangement::getBusBuffer to obtain a audio buffer
each bus. Use AudiobusLayout::getBusBuffer to obtain a audio buffer
for a particular bus.
Note that if you have more outputs than inputs, then only those channels that
@@ -182,8 +216,8 @@ public:
but you should only read/write from the ones that your filter is supposed to
be using.
If your plugin uses buses, then you should use AudioBusArrangement::getBusBuffer()
or AudioBusArrangement::getChannelIndexInProcessBlockBuffer() to find out which
If your plugin uses buses, then you should use AudiobusLayout::getBusBuffer()
or AudiobusLayout::getChannelIndexInProcessBlockBuffer() to find out which
of the input and output channels correspond to which of the buses.
The number of samples in these buffers is NOT guaranteed to be the same for every
@@ -210,12 +244,13 @@ public:
processBlock() method to send out an asynchronous message. You could also use
the AsyncUpdater class in a similar way.
@see AudioBusArrangement::getBusBuffer
@see AudiobusLayout::getBusBuffer
*/
virtual void processBlock (AudioBuffer<double>& buffer,
MidiBuffer& midiMessages);
/** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but
you may override this method e.g. to add latency compensation to the data to match
the processor's latency characteristics. This will avoid situations where bypassing
@@ -227,6 +262,7 @@ public:
MidiBuffer& midiMessages);
/** Renders the next block when the processor is being bypassed.
The default implementation of this method will pass-through any incoming audio, but
you may override this method e.g. to add latency compensation to the data to match
the processor's latency characteristics. This will avoid situations where bypassing
@@ -237,103 +273,372 @@ public:
virtual void processBlockBypassed (AudioBuffer<double>& buffer,
MidiBuffer& midiMessages);
//==============================================================================
/** Describes the layout and properties of an audio bus.
Effectively a bus description is a named set of channel types.
@see AudioChannelSet
/**
Represents the bus layout state of a plug-in
*/
struct AudioProcessorBus
struct BusesLayout
{
/** Creates a bus from a name and set of channel types. */
AudioProcessorBus (const String& busName, const AudioChannelSet& channelTypes);
/** An array containing the list of input buses that this processor supports. */
Array<AudioChannelSet> inputBuses;
/** The bus's name. */
String name;
/** An array containing the list of output buses that this processor supports. */
Array<AudioChannelSet> outputBuses;
/** Get the number of channels of a particular bus */
int getNumChannels (bool isInput, int busIndex) const noexcept
{
const Array<AudioChannelSet>& bus = (isInput ? inputBuses : outputBuses);
return isPositiveAndBelow(busIndex, bus.size()) ? bus.getReference (busIndex).size() : 0;
}
/** Get the channel set of a particular bus */
AudioChannelSet& getChannelSet (bool isInput, int busIndex)
{
Array<AudioChannelSet>& sets = isInput ? inputBuses : outputBuses;
jassert (isPositiveAndBelow (busIndex, sets.size()));
return sets.getReference (busIndex);
}
/** The set of channel types that the bus contains. */
AudioChannelSet channels;
/** Get the channel set of a particular bus */
AudioChannelSet getChannelSet (bool isInput, int busIndex) const noexcept
{
const Array<AudioChannelSet>& sets = isInput ? inputBuses : outputBuses;
if (isPositiveAndBelow (busIndex, sets.size()))
return sets.getReference (busIndex);
else
return AudioChannelSet();
}
/** Get the input channel layout on the main bus. */
AudioChannelSet getMainInputChannelSet() const noexcept { return getChannelSet (true, 0); }
/** Get the output channel layout on the main bus. */
AudioChannelSet getMainOutputChannelSet() const noexcept { return getChannelSet (false, 0); }
/** Get the number of input channels on the main bus. */
int getMainInputChannels() const noexcept { return getNumChannels (true, 0); }
/** Get the number of output channels on the main bus. */
int getMainOutputChannels() const noexcept { return getNumChannels (false, 0); }
bool operator== (const BusesLayout& other) const noexcept { return inputBuses == other.inputBuses && outputBuses == other.outputBuses; }
bool operator!= (const BusesLayout& other) const noexcept { return inputBuses != other.inputBuses || outputBuses != other.outputBuses; }
};
//==============================================================================
/**
Represents a set of input and output buses for an AudioProcessor.
*/
struct AudioBusArrangement
Describes the layout and properties of an audio bus.
Effectively a bus description is a named set of channel types.
@see AudioChannelSet, AudioProcessor::addBus
*/
class Bus
{
/** An array containing the list of input buses that this processor supports. */
Array<AudioProcessorBus> inputBuses;
public:
/** Returns true if this bus is an input bus. */
bool isInput() const;
/** An array containing the list of output buses that this processor supports. */
Array<AudioProcessorBus> outputBuses;
/** Returns the index of this bus. */
int getBusIndex() const;
/** Returns true if the current bus is the main input or output bus. */
bool isMain() const { return getBusIndex() == 0; }
//==============================================================================
/** The bus's name. */
const String &getName() const noexcept { return name; }
/** Get the default layout of this bus.
@see AudioChannelSet
*/
const AudioChannelSet& getDefaultLayout() const noexcept { return dfltLayout; }
//==============================================================================
/** The bus's current layout. This will be AudioChannelSet::disabled() if the current
layout is dfisabled.
@see AudioChannelSet
*/
const AudioChannelSet& getCurrentLayout() const noexcept { return layout; }
/** Return the bus's last active channel layout.
If the bus is currently enabled then the result will be identical to getCurrentLayout
otherwise it will return the last enabled layout.
@see AudioChannelSet
*/
const AudioChannelSet& getLastEnabledLayout() const noexcept { return lastLayout; }
/** Sets the bus's current layout.
If the AudioProcessor does not support this layout then this will return false.
@see AudioChannelSet
*/
bool setCurrentLayout (const AudioChannelSet& layout);
/** Sets the bus's current layout without changing the enabled state.
If the AudioProcessor does not support this layout then this will return false.
@see AudioChannelSet
*/
bool setCurrentLayoutWithoutEnabling (const AudioChannelSet& layout);
/** Return the number of channels of the current bus. */
inline int getNumberOfChannels() const noexcept { return cachedChannelCount; }
/** Set the number of channles of this bus. This will return false if the AudioProcessor
does not support this layout. */
bool setNumberOfChannels (int channels);
//==============================================================================
/** Checks if a particular layout is supported.
@param set The AudioChannelSet which is to be probed.
@see AudioChannelSet
*/
bool isLayoutSupported (const AudioChannelSet& set) const;
/** Checks if this bus can support a given number of channels. */
bool isNumberOfChannelsSupported (int channels) const;
/** Returns a ChannelSet that the bus supports with a given number of channels. */
AudioChannelSet supportedLayoutWithChannels (int channels) const;
/** Returns the maximum number of channels that this bus can support.
@param limit The maximum value to return.
*/
int getMaxSupportedChannels (int limit = AudioChannelSet::maxChannelsOfNamedLayout) const;
/** Returns the resulting layouts of all buses after changing the layout of this bus.
Changing an individual layout of a bus may also change the layout of all the other
buses. This method returns what the layouts of all the buses of the audio processor
would be, if you were to change the layout of this bus to the given layout. If there
is no way to support the given layout then this method will return the next best
layout.
*/
BusesLayout getBusesLayoutForLayoutChangeOfBus (const AudioChannelSet& set) const;
//==============================================================================
/** Returns true if the current bus is enabled. */
bool isEnabled() const noexcept { return ! layout.isDisabled(); }
/** Enable or disable this bus. This will return false if the AudioProcessor
does not support disabling this bus. */
bool enable (bool shouldEnable = true);
/** Returns if this bus is enabled by default. */
bool isEnabledByDefault() const noexcept { return enabledByDefault; }
//==============================================================================
/** Returns the position of a bus's channels within the processBlock buffer.
This can be called in processBlock to figure out which channel of the master AudioSampleBuffer
maps onto a specific bus's channel.
*/
int getChannelIndexInProcessBlockBuffer (bool isInput, int busIndex, int channelIndex) const noexcept;
*/
int getChannelIndexInProcessBlockBuffer (int channelIndex) const noexcept;
/** Returns an AudioBuffer containing a set of channel pointers for a specific bus.
This can be called in processBlock to get a buffer containing a sub-group of the master
AudioSampleBuffer which contains all the plugin channels.
*/
*/
template <typename FloatType>
AudioBuffer<FloatType> getBusBuffer (AudioBuffer<FloatType>& processBlockBuffer, bool isInput, int busIndex) const
AudioBuffer<FloatType> getBusBuffer (AudioBuffer<FloatType>& processBlockBuffer) const
{
const int busNumChannels = (isInput ? inputBuses : outputBuses).getReference (busIndex).channels.size();
const int channelOffset = getChannelIndexInProcessBlockBuffer (isInput, busIndex, 0);
return AudioBuffer<FloatType> (processBlockBuffer.getArrayOfWritePointers() + channelOffset,
busNumChannels, processBlockBuffer.getNumSamples());
bool isIn;
int busIdx;
busDirAndIndex (isIn, busIdx);
return owner.getBusBuffer (processBlockBuffer, isIn, busIdx);
}
private:
friend class AudioProcessor;
Bus (AudioProcessor&, const String&, const AudioChannelSet&, bool);
void busDirAndIndex (bool&, int&) const noexcept;
void updateChannelCount() noexcept;
/** Returns the total number of channels in all the input buses. */
int getTotalNumInputChannels() const noexcept;
AudioProcessor& owner;
String name;
AudioChannelSet layout, dfltLayout, lastLayout;
bool enabledByDefault;
int cachedChannelCount;
/** Returns the total number of channels in all the output buses. */
int getTotalNumOutputChannels() const noexcept;
JUCE_DECLARE_NON_COPYABLE (Bus);
};
/** The processor's bus arrangement.
//==============================================================================
/** Returns the number of buses on the input or output side */
int getBusCount (bool isInput) const noexcept { return (isInput ? inputBuses : outputBuses).size(); }
/** Returns the audio bus with a given index and direction.
If busIdx is invalid then this method will return a nullptr.
*/
Bus* getBus (bool isInput, int busIdx) noexcept { return (isInput ? inputBuses : outputBuses)[busIdx]; }
/** Returns the audio bus with a given index and direction.
If busIdx is invalid then this method will return a nullptr.
*/
const Bus* getBus (bool isInput, int busIdx) const noexcept { return const_cast<AudioProcessor*> (this)->getBus (isInput, busIdx); }
//==============================================================================
/** Callback to query if a bus can currently be added.
This callback probes if a bus can currently be added. You should override
this callback if you want to support dynamically adding/removing buses by
the host. This is useful for mixer audio processors.
The default implementation will always return false.
@see addBus
*/
virtual bool canAddBus (bool /*inputBus*/) const { return false; }
/** Callback to query if the last bus can currently be removed.
This callback probes if the last bus can currently be removed. You should
override this callback if you want to support dynamically adding/removing
buses by the host. This is useful for mixer audio processors.
If you return true in this callback then the AudioProcessor will go ahead
and delete the bus.
The default implementation will always return false.
*/
virtual bool canRemoveBus (bool /*inputBus*/) const { return false; }
/** Dynamically request an additional bus.
Request an additional bus from the audio processor. If the audio processor
does not support adding additional buses then this method will return false.
Most audio processors will not allow you to dynamically add/remove
audio buses and will return false.
This method will invoke the canApplyBusCountChange callback to probe
if a bus can be added and, if yes, will use the supplied bus properties
of the canApplyBusCountChange callback to create a new bus.
@see canApplyBusCountChange, removeBus
*/
bool addBus (bool isInput);
/** Dynamically remove the latest added bus.
Request the removal of the last bus from the audio processor. If the
audio processor does not support removing buses then this method will
return false.
Most audio processors will not allow you to dynamically add/remove
audio buses and will return false.
The default implementation will return false.
Your plugin can modify this either
- in the plugin's constructor
- in the setPreferredBusArrangement() callback
Changing it at other times can result in undefined behaviour.
This method will invoke the canApplyBusCountChange callback to probe if
a bus can currently be removed and, if yes, will go ahead and remove it.
The host will negotiate with the plugin over its bus configuration by making calls
to setPreferredBusArrangement().
@see addBus, canRemoveBus
*/
bool removeBus (bool isInput);
//==============================================================================
/** Set the channel layouts of this audio processor.
@see setPreferredBusArrangement
If the layout is not supported by this audio processor then
this method will return false. You can use the checkBusesLayoutSupported
and getNextBestLayout methods to probe which layouts this audio
processor supports.
*/
bool setBusesLayout (const BusesLayout& arr);
/** Set the channel layouts of this audio processor without changing the
enablement state of the buses.
If the layout is not supported by this audio processor then
this method will return false. You can use the checkBusesLayoutSupported
and getNextBestLayout methods to probe which layouts this audio
processor supports.
*/
AudioBusArrangement busArrangement;
bool setBusesLayoutWithoutEnabling (const BusesLayout& arr);
/** Provides the current channel layouts of this audio processor. */
BusesLayout getBusesLayout() const;
/** Provides the channel layout of the bus with a given index and direction.
If the index, direction combination is invalid then this will return an
AudioChannelSet with no channels.
*/
AudioChannelSet getChannelLayoutOfBus (bool isInput, int busIdx) const noexcept;
/** Set the channel layout of the bus with a given index and direction.
If the index, direction combination is invalid or the layout is not
supported by the audio processor then this method will return false.
*/
bool setChannelLayoutOfBus (bool isInput, int busIdx, const AudioChannelSet& layout);
/** Provides the number of channels of the bus with a given index and direction.
If the index, direction combination is invalid then this will return zero.
*/
inline int getChannelCountOfBus (bool isInput, int busIdx) const noexcept
{
if (const Bus* bus = getBus (isInput, busIdx))
return bus->getNumberOfChannels();
return 0;
}
/** Enables all buses */
bool enableAllBuses();
/** Disables all non-main buses (aux and sidechains). */
bool disableNonMainBuses();
//==============================================================================
/** Called by the host, this attempts to change the plugin's channel layout on a particular bus.
The base class implementation will perform some basic sanity-checking and then apply the
changes to the processor's busArrangement value.
You may override it and return false if you want to make your plugin smarter about refusing
certain layouts that you don't want to support. Your plug-in may also respond to this call by
changing the channel layout of other buses, for example, if your plug-in requires the same
number of input and output channels.
/** Returns the position of a bus's channels within the processBlock buffer.
This can be called in processBlock to figure out which channel of the master AudioSampleBuffer
maps onto a specific bus's channel.
*/
int getChannelIndexInProcessBlockBuffer (bool isInput, int busIndex, int channelIndex) const noexcept;
For most basic plug-ins, which do not require side-chains, aux buses or detailed audio
channel layout information, it is easier to specify the acceptable channel configurations
via the "PlugIn Channel Configurations" field in the Introjucer. In this case, you should
not override this method.
/** Returns the offset in a bus's buffer from an absolute channel indes.
If, on the other hand, you decide to override this method then you need to make sure that
"PlugIn Channel Configurations" field in the Introjucer is empty.
This method returns the offset in a bus's buffer given an absolute channel index.
It also provides the bus index. For example, this method would return one
for a processor with two stereo buses when given the absolute channel index.
*/
int getOffsetInBusBufferForAbsoluteChannelIndex (bool isInput, int absoluteChannelIndex, /*out*/ int& busIdx) const noexcept;
Note, that you must not do any heavy allocations or calculations in this callback as it may
be called several hundred times during initialization. If you require any layout specific
allocations then defer these to prepareToPlay callback.
/** Returns an AudioBuffer containing a set of channel pointers for a specific bus.
This can be called in processBlock to get a buffer containing a sub-group of the master
AudioSampleBuffer which contains all the plugin channels.
*/
template <typename FloatType>
AudioBuffer<FloatType> getBusBuffer (AudioBuffer<FloatType>& processBlockBuffer, bool isInput, int busIndex) const
{
const int busNumChannels = getChannelCountOfBus (isInput, busIndex);
const int channelOffset = getChannelIndexInProcessBlockBuffer (isInput, busIndex, 0);
@returns false if there is no way for the processor to support the given format on the specified bus.
return AudioBuffer<FloatType> (processBlockBuffer.getArrayOfWritePointers() + channelOffset,
busNumChannels, processBlockBuffer.getNumSamples());
}
@see prepareToPlay, busArrangement, AudioBusArrangement::getBusBuffer, getTotalNumInputChannels, getTotalNumOutputChannels
//==============================================================================
/** Returns true if the Audio processor is likely to support a given layout.
This can be called regardless if the processor is currently running.
*/
virtual bool setPreferredBusArrangement (bool isInputBus, int busIndex, const AudioChannelSet& preferredSet);
bool checkBusesLayoutSupported (const BusesLayout& layouts) const;
//==============================================================================
/** Returns true if the Audio processor supports double precision floating point processing.
@@ -403,7 +708,7 @@ public:
getMainBusNumInputChannels if your processor does not have any sidechains
or aux buses.
*/
int getTotalNumInputChannels() const noexcept { return busArrangement.getTotalNumInputChannels(); }
int getTotalNumInputChannels() const noexcept { return cachedTotalIns; }
/** Returns the total number of output channels.
@@ -417,13 +722,56 @@ public:
getMainBusNumOutputChannels if your processor does not have any sidechains
or aux buses.
*/
int getTotalNumOutputChannels() const noexcept { return busArrangement.getTotalNumOutputChannels(); }
int getTotalNumOutputChannels() const noexcept { return cachedTotalOuts; }
/** Returns the number of input channels on the main bus. */
int getMainBusNumInputChannels() const noexcept;
inline int getMainBusNumInputChannels() const noexcept { return getChannelCountOfBus (true, 0); }
/** Returns the number of output channels on the main bus. */
int getMainBusNumOutputChannels() const noexcept;
inline int getMainBusNumOutputChannels() const noexcept { return getChannelCountOfBus (false, 0); }
//==============================================================================
/** Returns true if the channel layout map contains a certain layout.
You can use this method to help you implement the checkBusesLayoutSupported
method. For example
@code
bool checkBusesLayoutSupported (const BusesLayout& layouts) override
{
return containsLayout (layouts, {{1,1},{2,2}});
}
@endcode
*/
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS
static bool containsLayout (const BusesLayout& layouts, const std::initializer_list<const short[2]>& channelLayoutList)
{
return containsLayout (layouts, layoutListToArray (channelLayoutList));
}
#endif
template <int numLayouts>
static bool containsLayout (const BusesLayout& layouts, const short (&channelLayoutList) [numLayouts][2])
{
return containsLayout (layouts, layoutListToArray (channelLayoutList));
}
/** Returns the next best layout which is contained in a channel layout map.
You can use this mehtod to help you implement getNextBestLayout. For example:
@code
BusesLayout getNextBestLayout (const BusesLayout& layouts) override
{
return getNextBestLayoutInLayoutList (layouts, {{1,1},{2,2}});
}
@endcode
*/
template <int numLayouts>
BusesLayout getNextBestLayoutInLayoutList (const BusesLayout& layouts,
const short (&channelLayoutList) [numLayouts][2])
{
return getNextBestLayoutInList (layouts, layoutListToArray (channelLayoutList));
}
//==============================================================================
/** Returns the current sample rate.
@@ -473,6 +821,9 @@ public:
/** Returns true if the processor supports MPE. */
virtual bool supportsMPE() const { return false; }
/** Returns true if this is a midi effect plug-in and does no audio processing. */
virtual bool isMidiEffect() const { return false; }
//==============================================================================
/** This returns a critical section that will automatically be locked while the host
is calling the processBlock() method.
@@ -611,6 +962,17 @@ public:
*/
virtual const String getParameterName (int parameterIndex);
/** Returns the ID of a particular parameter.
The ID is used to communicate the value or mapping of a particular parameter with
the host. By default this method will simply return a string representation of
index.
NOTE! This method will eventually be deprecated! It's recommended that you use the
AudioProcessorParameterWithID class instead to manage your parameters.
*/
virtual String getParameterID (int index);
/** Called by the host to find out the value of one of the filter's parameters.
The host will expect the value returned to be between 0 and 1.0.
@@ -628,7 +990,7 @@ public:
If you want to provide customised short versions of your parameter names that
will look better in constrained spaces (e.g. the displays on hardware controller
devices or mixing desks) then you should implement this method.
If you don't override it, the default implementation will call getParameterText(int),
If you don't override it, the default implementation will call getParameterName(int),
and truncate the result.
NOTE! This method will eventually be deprecated! It's recommended that you use
@@ -864,9 +1226,15 @@ public:
*/
virtual void setCurrentProgramStateInformation (const void* data, int sizeInBytes);
/** This method is called when the number of input or output channels is changed. */
/** This method is called when the total number of input or output channels is changed. */
virtual void numChannelsChanged();
/** This method is called when the number of buses is changed. */
virtual void numBusesChanged();
/** This method is called when the layout of the audio processor changes. */
virtual void processorLayoutsChanged();
//==============================================================================
/** LV2 specific calls, saving/restore as string. */
virtual String getStateInformationString () { return String::empty; }
@@ -894,9 +1262,9 @@ public:
/** This is called by the processor to specify its details before being played. You
should call this function after having informed the processor about the channel
and bus layouts via setPreferredBusArrangement.
and bus layouts via setBusesLayout.
@see setPreferredBusArrangement
@see setBusesLayout
*/
void setRateAndBufferSizeDetails (double sampleRate, int blockSize) noexcept;
@@ -913,6 +1281,7 @@ public:
wrapperType_VST,
wrapperType_VST3,
wrapperType_AudioUnit,
wrapperType_AudioUnitv3,
wrapperType_RTAS,
wrapperType_AAX,
wrapperType_Standalone
@@ -939,7 +1308,7 @@ public:
/** Returns the name of one of the processor's input channels.
These functions are deprecated: your audio processor can inform the host
on channel layouts and names via the methods in the AudioBusArrangement class.
on channel layouts and names via the methods in the AudiobusLayout class.
*/
JUCE_DEPRECATED (virtual const String getInputChannelName (int channelIndex) const);
JUCE_DEPRECATED (virtual const String getOutputChannelName (int channelIndex) const);
@@ -947,7 +1316,7 @@ public:
/** Returns true if the specified channel is part of a stereo pair with its neighbour.
These functions are deprecated: your audio processor should specify the audio
channel pairing information by modifying the busArrangement member variable in
channel pairing information by modifying the busLayout member variable in
the constructor. */
JUCE_DEPRECATED (virtual bool isInputChannelStereoPair (int index) const);
JUCE_DEPRECATED (virtual bool isOutputChannelStereoPair (int index) const);
@@ -976,6 +1345,110 @@ public:
static void JUCE_CALLTYPE setTypeOfNextNewPlugin (WrapperType);
protected:
/** Callback to query if the AudioProcessor supports a specific layout.
This callback is called when the host probes the supported bus layouts via
the checkBusesLayoutSupported method. You should override this callback if you
would like to limit the layouts that your AudioProcessor supports. The default
implementation will accept any layout. JUCE does basic sanity checks so that
the provided layouts parameter will have the same number of buses as your
AudioProcessor.
@see checkBusesLayoutSupported
*/
virtual bool isBusesLayoutSupported (const BusesLayout& /*layouts*/) const { return true; }
/** Callback to check if a certain bus layout can now be applied
Most subclasses will not need to override this method and should instead
override the isBusesLayoutSupported callback to reject certain layout changes.
This callback is called when the user requests a layout change. It will only be
called if processing of the AudioProcessor has been stopped by a previous call to
releaseResources or after the construction of the AudioProcessor. It will be called
just before the actual layout change. By returning false you will abort the layout
change and setBusesLayout will return false indicating that the layout change
was not successful.
The default implementation will simply call isBusesLayoutSupported.
You only need to override this method if there is a chance that your AudioProcessor
may not accept a layout although you have previously claimed to support it via the
isBusesLayoutSupported callback. This can occur if your AudioProcessor's supported
layouts depend on other plug-in parameters which may have changed since the last
call to isBusesLayoutSupported, such as the format of an audio file which can be
selected by the user in the AudioProcessor's editor. This callback gives the
AudioProcessor a last chance to reject a layout if conditions have changed as it
is always called just before the actual layout change.
As it is never called while the AudioProcessor is processing audio, it can also
be used for AudioProcessors which wrap other plug-in formats to apply the current
layout to the underlying plug-in. This callback gives such AudioProcessors a
chance to reject the layout change should an error occur with the underlying plug-in
during the layout change.
@see isBusesLayoutSupported, setBusesLayout
*/
virtual bool canApplyBusesLayout (const BusesLayout& layouts) const { return isBusesLayoutSupported (layouts); }
//==============================================================================
/** Structure used for AudioProcessor Callbacks */
struct BusProperties
{
/** The name of the bus */
String busName;
/** The default layout of the bus */
AudioChannelSet defaultLayout;
/** Is this bus activated by default? */
bool isActivatedByDefault;
};
struct BusesProperties
{
/** The layouts of the input buses */
Array<BusProperties> inputLayouts;
/** The layouts of the output buses */
Array<BusProperties> outputLayouts;
void addBus (bool isInput, const String& name, const AudioChannelSet& dfltLayout, bool isActivatedByDefault = true);
BusesProperties withInput (const String& name, const AudioChannelSet& dfltLayout, bool isActivatedByDefault = true) const;
BusesProperties withOutput (const String& name, const AudioChannelSet& dfltLayout, bool isActivatedByDefault = true) const;
};
/** Callback to query if adding/removing buses currently possible.
This callback is called when the host calls addBus or removeBus.
Similar to canApplyBusesLayout, this callback is only called while
the AudioProcessor is stopped and gives the processor a last
chance to reject a requested bus change. It can also be used to apply
the bus count change to an underlying wrapped plug-in.
When adding a bus, isAddingBuses will be true and the plug-in is
expected to fill out outNewBusProperties with the properties of the
bus which will be created just after the succesful return of this callback.
Implementations of AudioProcessor will rarely need to override this
method. Only override this method if your processor supports adding
and removing buses and if it needs more fine grain control over the
naming of new buses or may reject bus number changes although canAddBus
or canRemoveBus returned true.
The default implementation will return false if canAddBus/canRemoveBus
returns false (the default behavior). Otherwise, this method returns
"Input #busIdx" for input buses and "Output #busIdx" for output buses
where busIdx is the index for newly created buses. The default layout
in this case will be the layout of the previous bus of the same direction.
*/
virtual bool canApplyBusCountChange (bool isInput, bool isAddingBuses,
BusProperties& outNewBusProperties);
//==============================================================================
friend struct PluginBusUtilities;
/** @internal */
AudioPlayHead* playHead;
@@ -983,6 +1456,67 @@ protected:
void sendParamChangeMessageToListeners (int parameterIndex, float newValue);
private:
//==============================================================================
struct InOutChannelPair
{
int16 inChannels, outChannels;
InOutChannelPair() noexcept : inChannels (0), outChannels (0) {}
InOutChannelPair (short inCh, short outCh) noexcept : inChannels (inCh), outChannels (outCh) {}
InOutChannelPair (const InOutChannelPair& o) noexcept : inChannels (o.inChannels), outChannels (o.outChannels) {}
InOutChannelPair (const short (&config)[2]) noexcept : inChannels (config[0]), outChannels (config[1]) {}
InOutChannelPair& operator= (const InOutChannelPair& o) { inChannels = o.inChannels; outChannels = o.outChannels; return *this; }
bool operator== (const InOutChannelPair& other) const noexcept
{
return (other.inChannels == inChannels && other.outChannels == outChannels);
}
};
template <int numLayouts>
static Array<InOutChannelPair> layoutListToArray (const short (&configuration) [numLayouts][2])
{
Array<InOutChannelPair> layouts;
for (int i = 0; i < numLayouts; ++i)
{
InOutChannelPair pair (configuration [i]);
layouts.add (pair);
}
return layouts;
}
#if JUCE_COMPILER_SUPPORTS_INITIALIZER_LISTS
static Array<InOutChannelPair> layoutListToArray (const std::initializer_list<const short[2]>& configuration)
{
Array<InOutChannelPair> layouts;
for (std::initializer_list<const short[2]>::const_iterator it = configuration.begin();
it != configuration.end(); ++it)
{
InOutChannelPair pair (*it);
layouts.add (pair);
}
return layouts;
}
#endif
//==============================================================================
static BusesProperties busesPropertiesFromLayoutArray (const Array<InOutChannelPair>&);
//==============================================================================
BusesLayout getNextBestLayoutInList (const BusesLayout& layouts,
const Array<InOutChannelPair>& channelLayouts) const;
static bool containsLayout (const BusesLayout& layouts, const Array<InOutChannelPair>& channelLayouts);
//==============================================================================
void initialise (const BusesProperties& ioLayouts);
void createBus (bool inputBus, const BusProperties&);
//==============================================================================
Array<AudioProcessorListener*> listeners;
#if ! JUCE_AUDIO_PROCESSOR_NO_GUI
Component::SafePointer<AudioProcessorEditor> activeEditor;
@@ -996,9 +1530,15 @@ private:
ProcessingPrecision processingPrecision;
CriticalSection callbackLock, listenerLock;
friend class Bus;
mutable OwnedArray<Bus> inputBuses;
mutable OwnedArray<Bus> outputBuses;
String cachedInputSpeakerArrString;
String cachedOutputSpeakerArrString;
int cachedTotalIns, cachedTotalOuts;
OwnedArray<AudioProcessorParameter> managedParameters;
AudioProcessorParameter* getParamChecked (int) const noexcept;
@@ -1007,8 +1547,13 @@ private:
#endif
AudioProcessorListener* getListenerLocked (int) const noexcept;
void disableNonMainBuses (bool isInput);
void updateSpeakerFormatStrings();
bool applyBusLayouts (const BusesLayout& arr);
void audioIOChanged (bool busNumberChanged, bool channelNumChanged);
BusesLayout getNextBestLayout (const BusesLayout& layouts) const;
template <typename floatType>
void processBypassed (AudioBuffer<floatType>&, MidiBuffer&);
// This method is no longer used - you can delete it from your AudioProcessor classes.
JUCE_DEPRECATED_WITH_BODY (virtual bool silenceInProducesSilenceOut() const, { return false; });


+ 129
- 2
source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp View File

@@ -22,14 +22,16 @@
==============================================================================
*/
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p)
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor& p) noexcept : processor (p), constrainer (nullptr)
{
initialise();
}
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p)
AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* p) noexcept : processor (*p), constrainer (nullptr)
{
// the filter must be valid..
jassert (p != nullptr);
initialise();
}
AudioProcessorEditor::~AudioProcessorEditor()
@@ -37,7 +39,132 @@ AudioProcessorEditor::~AudioProcessorEditor()
// if this fails, then the wrapper hasn't called editorBeingDeleted() on the
// filter for some reason..
jassert (processor.getActiveEditor() != this);
removeComponentListener (resizeListener);
}
void AudioProcessorEditor::setControlHighlight (ParameterControlHighlightInfo) {}
int AudioProcessorEditor::getControlParameterIndex (Component&) { return -1; }
void AudioProcessorEditor::initialise()
{
resizable = false;
attachConstrainer (&defaultConstrainer);
addComponentListener (resizeListener = new AudioProcessorEditorListener (this));
}
//==============================================================================
void AudioProcessorEditor::setResizable (const bool shouldBeResizable, const bool useBottomRightCornerResizer)
{
if (shouldBeResizable != resizable)
{
resizable = shouldBeResizable;
if (! resizable)
{
setConstrainer (&defaultConstrainer);
if (getWidth() > 0 && getHeight() > 0)
{
defaultConstrainer.setSizeLimits (getWidth(), getHeight(), getWidth(), getHeight());
resized();
}
}
}
const bool shouldHaveCornerResizer = (useBottomRightCornerResizer && shouldBeResizable);
if (shouldHaveCornerResizer != (resizableCorner != nullptr))
{
if (shouldHaveCornerResizer)
{
Component::addChildComponent (resizableCorner = new ResizableCornerComponent (this, constrainer));
resizableCorner->setAlwaysOnTop (true);
}
else
resizableCorner = nullptr;
}
}
void AudioProcessorEditor::setResizeLimits (const int newMinimumWidth,
const int newMinimumHeight,
const int newMaximumWidth,
const int newMaximumHeight) noexcept
{
// if you've set up a custom constrainer then these settings won't have any effect..
jassert (constrainer == &defaultConstrainer || constrainer == nullptr);
const bool shouldEnableResize = (newMinimumWidth != newMaximumWidth || newMinimumHeight != newMaximumHeight);
const bool shouldHaveCornerResizer = (shouldEnableResize != resizable || resizableCorner != nullptr);
setResizable (shouldEnableResize, shouldHaveCornerResizer);
if (constrainer == nullptr)
setConstrainer (&defaultConstrainer);
defaultConstrainer.setSizeLimits (newMinimumWidth, newMinimumHeight,
newMaximumWidth, newMaximumHeight);
setBoundsConstrained (getBounds());
}
void AudioProcessorEditor::setConstrainer (ComponentBoundsConstrainer* newConstrainer)
{
if (constrainer != newConstrainer)
{
resizable = true;
attachConstrainer (newConstrainer);
}
}
void AudioProcessorEditor::attachConstrainer (ComponentBoundsConstrainer* newConstrainer)
{
if (constrainer != newConstrainer)
{
constrainer = newConstrainer;
updatePeer();
}
}
void AudioProcessorEditor::setBoundsConstrained (Rectangle<int> newBounds)
{
if (constrainer != nullptr)
constrainer->setBoundsForComponent (this, newBounds, false, false, false, false);
else
setBounds (newBounds);
}
void AudioProcessorEditor::editorResized (bool wasResized)
{
if (wasResized)
{
bool resizerHidden = false;
if (ComponentPeer* peer = getPeer())
resizerHidden = peer->isFullScreen() || peer->isKioskMode();
if (resizableCorner != nullptr)
{
resizableCorner->setVisible (! resizerHidden);
const int resizerSize = 18;
resizableCorner->setBounds (getWidth() - resizerSize,
getHeight() - resizerSize,
resizerSize, resizerSize);
}
if (! resizable)
{
if (getWidth() > 0 && getHeight() > 0)
defaultConstrainer.setSizeLimits (getWidth(), getHeight(),
getWidth(), getHeight());
}
}
}
void AudioProcessorEditor::updatePeer()
{
if (isOnDesktop())
if (ComponentPeer* const peer = getPeer())
peer->setConstrainer (constrainer);
}

+ 85
- 1
source/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h View File

@@ -25,7 +25,7 @@
#ifndef JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED
#define JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED
class AudioProcessorEditorListener;
//==============================================================================
/**
Base class for the component that acts as the GUI for an AudioProcessor.
@@ -84,7 +84,91 @@ public:
*/
virtual int getControlParameterIndex (Component&);
//==============================================================================
/** Marks the host's editor window as resizable
@param allowHostToResize whether the editor's parent window can be resized
by the user or the host. Even if this is false, you
can still resize your window yourself by calling
setBounds (for example, when a user clicks on a button
in your editor to drop out a panel) which will bypass any
resizable/constraints checks. If you are using
your own corner resizer than this will also bypass
any checks.
@param useBottomRightCornerResizer
@see setResizeLimits, isResizable
*/
void setResizable (bool allowHostToResize, bool useBottomRightCornerResizer);
/** Returns true if the host is allowed to resize editor's parent window
@see setResizable
*/
bool isResizable() const noexcept { return resizable; }
/** This sets the maximum and minimum sizes for the window.
If the window's current size is outside these limits, it will be resized to
make sure it's within them.
A direct call to setBounds() will bypass any constraint checks, but when the
window is dragged by the user or resized by other indirect means, the constrainer
will limit the numbers involved.
@see setResizable
*/
void setResizeLimits (int newMinimumWidth,
int newMinimumHeight,
int newMaximumWidth,
int newMaximumHeight) noexcept;
/** Returns the bounds constrainer object that this window is using.
You can access this to change its properties.
*/
ComponentBoundsConstrainer* getConstrainer() noexcept { return constrainer; }
/** Sets the bounds-constrainer object to use for resizing and dragging this window.
A pointer to the object you pass in will be kept, but it won't be deleted
by this object, so it's the caller's responsibility to manage it.
If you pass a nullptr, then no contraints will be placed on the positioning of the window.
*/
void setConstrainer (ComponentBoundsConstrainer* newConstrainer);
/** Calls the window's setBounds method, after first checking these bounds
with the current constrainer.
@see setConstrainer
*/
void setBoundsConstrained (Rectangle<int> newBounds);
ScopedPointer<ResizableCornerComponent> resizableCorner;
private:
//==============================================================================
struct AudioProcessorEditorListener : ComponentListener
{
AudioProcessorEditorListener (AudioProcessorEditor* audioEditor) : e (audioEditor) {}
void componentMovedOrResized (Component&, bool, bool wasResized) override { e->editorResized (wasResized); }
void componentParentHierarchyChanged (Component&) override { e->updatePeer(); }
AudioProcessorEditor* e;
};
//==============================================================================
void initialise();
void editorResized (bool wasResized);
void updatePeer();
void attachConstrainer (ComponentBoundsConstrainer* newConstrainer);
//==============================================================================
ScopedPointer<AudioProcessorEditorListener> resizeListener;
bool resizable;
ComponentBoundsConstrainer defaultConstrainer;
ComponentBoundsConstrainer* constrainer;
JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor)
};


+ 48
- 24
source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp View File

@@ -264,7 +264,16 @@ struct ProcessBufferOp : public AudioGraphRenderingOp<ProcessBufferOp>
AudioBuffer<FloatType> buffer (channels, totalChans, numSamples);
callProcess (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse));
if (processor->isSuspended())
{
buffer.clear();
}
else
{
ScopedLock lock (processor->getCallbackLock());
callProcess (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse));
}
}
void callProcess (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
@@ -960,14 +969,7 @@ void AudioProcessorGraph::Node::prepare (const double newSampleRate, const int n
processor->setProcessingPrecision (processor->supportsDoublePrecisionProcessing() ? precision
: singlePrecision);
processor->setPlayConfigDetails (processor->getMainBusNumInputChannels(),
processor->getMainBusNumOutputChannels(),
newSampleRate, newBlockSize);
// AudioProcessorGraph currently does not support processors with multiple buses
jassert (processor->getMainBusNumInputChannels() == processor->getTotalNumInputChannels()
&& processor->getMainBusNumOutputChannels() == processor->getTotalNumOutputChannels());
processor->setRateAndBufferSizeDetails (newSampleRate, newBlockSize);
processor->prepareToPlay (newSampleRate, newBlockSize);
}
}
@@ -1035,7 +1037,7 @@ struct AudioProcessorGraph::AudioProcessorGraphBufferHelpers
//==============================================================================
AudioProcessorGraph::AudioProcessorGraph()
: lastNodeId (0), audioBuffers (new AudioProcessorGraphBufferHelpers),
currentMidiInputBuffer (nullptr)
currentMidiInputBuffer (nullptr), isPrepared (false)
{
}
@@ -1102,7 +1104,9 @@ AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const n
Node* const n = new Node (nodeId, newProcessor);
nodes.add (n);
triggerAsyncUpdate();
if (isPrepared)
triggerAsyncUpdate();
n->setParentGraph (this);
return n;
@@ -1117,7 +1121,9 @@ bool AudioProcessorGraph::removeNode (const uint32 nodeId)
if (nodes.getUnchecked(i)->nodeId == nodeId)
{
nodes.remove (i);
triggerAsyncUpdate();
if (isPrepared)
triggerAsyncUpdate();
return true;
}
@@ -1126,6 +1132,15 @@ bool AudioProcessorGraph::removeNode (const uint32 nodeId)
return false;
}
bool AudioProcessorGraph::removeNode (Node* node)
{
if (node != nullptr)
return removeNode (node->nodeId);
jassertfalse;
return false;
}
//==============================================================================
const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId,
const int sourceChannelIndex,
@@ -1168,14 +1183,14 @@ bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId,
const Node* const source = getNodeForId (sourceNodeId);
if (source == nullptr
|| (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getMainBusNumOutputChannels())
|| (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getTotalNumOutputChannels())
|| (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi()))
return false;
const Node* const dest = getNodeForId (destNodeId);
if (dest == nullptr
|| (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getMainBusNumInputChannels())
|| (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getTotalNumInputChannels())
|| (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi()))
return false;
@@ -1194,14 +1209,19 @@ bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId,
GraphRenderingOps::ConnectionSorter sorter;
connections.addSorted (sorter, new Connection (sourceNodeId, sourceChannelIndex,
destNodeId, destChannelIndex));
triggerAsyncUpdate();
if (isPrepared)
triggerAsyncUpdate();
return true;
}
void AudioProcessorGraph::removeConnection (const int index)
{
connections.remove (index);
triggerAsyncUpdate();
if (isPrepared)
triggerAsyncUpdate();
}
bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex,
@@ -1253,9 +1273,9 @@ bool AudioProcessorGraph::isConnectionLegal (const Connection* const c) const
return source != nullptr
&& dest != nullptr
&& (c->sourceChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->sourceChannelIndex, source->processor->getMainBusNumOutputChannels())
&& (c->sourceChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->sourceChannelIndex, source->processor->getTotalNumOutputChannels())
: source->processor->producesMidi())
&& (c->destChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->destChannelIndex, dest->processor->getMainBusNumInputChannels())
&& (c->destChannelIndex != midiChannelIndex ? isPositiveAndBelow (c->destChannelIndex, dest->processor->getTotalNumInputChannels())
: dest->processor->acceptsMidi());
}
@@ -1383,6 +1403,8 @@ void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int estimatedSam
clearRenderingSequence();
buildRenderingSequence();
isPrepared = true;
}
bool AudioProcessorGraph::supportsDoublePrecisionProcessing() const
@@ -1392,6 +1414,8 @@ bool AudioProcessorGraph::supportsDoublePrecisionProcessing() const
void AudioProcessorGraph::releaseResources()
{
isPrepared = false;
for (int i = 0; i < nodes.size(); ++i)
nodes.getUnchecked(i)->unprepare();
@@ -1516,13 +1540,13 @@ void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (Plugin
d.version = "1.0";
d.isInstrument = false;
d.numInputChannels = getMainBusNumInputChannels();
d.numInputChannels = getTotalNumInputChannels();
if (type == audioOutputNode && graph != nullptr)
d.numInputChannels = graph->getMainBusNumInputChannels();
d.numInputChannels = graph->getTotalNumInputChannels();
d.numOutputChannels = getMainBusNumOutputChannels();
d.numOutputChannels = getTotalNumOutputChannels();
if (type == audioInputNode && graph != nullptr)
d.numOutputChannels = graph->getMainBusNumOutputChannels();
d.numOutputChannels = graph->getTotalNumOutputChannels();
}
void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int)
@@ -1639,8 +1663,8 @@ void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorG
if (graph != nullptr)
{
setPlayConfigDetails (type == audioOutputNode ? graph->getMainBusNumOutputChannels() : 0,
type == audioInputNode ? graph->getMainBusNumInputChannels() : 0,
setPlayConfigDetails (type == audioOutputNode ? graph->getTotalNumOutputChannels() : 0,
type == audioInputNode ? graph->getTotalNumInputChannels() : 0,
getSampleRate(),
getBlockSize());


+ 8
- 0
source/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h View File

@@ -183,6 +183,12 @@ public:
*/
bool removeNode (uint32 nodeId);
/** Deletes a node within the graph which has the specified ID.
This will also delete any connections that are attached to this node.
*/
bool removeNode (Node* node);
//==============================================================================
/** Returns the number of connections in the graph. */
int getNumConnections() const { return connections.size(); }
@@ -390,6 +396,8 @@ private:
MidiBuffer* currentMidiInputBuffer;
MidiBuffer currentMidiOutputBuffer;
bool isPrepared;
void handleAsyncUpdate() override;
void clearRenderingSequence();
void buildRenderingSequence();


+ 10
- 0
source/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp View File

@@ -105,6 +105,16 @@ private:
}
}
void startedDragging() override
{
owner.beginParameterChangeGesture(index);
}
void stoppedDragging() override
{
owner.endParameterChangeGesture(index);
}
String getTextFromValue (double /*value*/) override
{
return owner.getParameterText (index) + " " + owner.getParameterLabel (index).trimEnd();


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

@@ -118,7 +118,7 @@ public:
given identifier string.
Note that this isn't quite as simple as them just calling createIdentifierString()
and comparing the strings, because the identifers can differ (thanks to shell plug-ins).
and comparing the strings, because the identifiers can differ (thanks to shell plug-ins).
*/
bool matchesIdentifierString (const String& identifierString) const;


+ 69
- 27
source/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp View File

@@ -27,6 +27,8 @@ KnownPluginList::~KnownPluginList() {}
void KnownPluginList::clear()
{
ScopedLock lock (typesArrayLock);
if (types.size() > 0)
{
types.clear();
@@ -36,6 +38,8 @@ void KnownPluginList::clear()
PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifier) const
{
ScopedLock lock (typesArrayLock);
for (int i = 0; i < types.size(); ++i)
if (types.getUnchecked(i)->fileOrIdentifier == fileOrIdentifier)
return types.getUnchecked(i);
@@ -45,6 +49,8 @@ PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifi
PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const
{
ScopedLock lock (typesArrayLock);
for (int i = 0; i < types.size(); ++i)
if (types.getUnchecked(i)->matchesIdentifierString (identifierString))
return types.getUnchecked(i);
@@ -54,27 +60,37 @@ PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& id
bool KnownPluginList::addType (const PluginDescription& type)
{
for (int i = types.size(); --i >= 0;)
{
if (types.getUnchecked(i)->isDuplicateOf (type))
ScopedLock lock (typesArrayLock);
for (int i = types.size(); --i >= 0;)
{
// strange - found a duplicate plugin with different info..
jassert (types.getUnchecked(i)->name == type.name);
jassert (types.getUnchecked(i)->isInstrument == type.isInstrument);
if (types.getUnchecked(i)->isDuplicateOf (type))
{
// strange - found a duplicate plugin with different info..
jassert (types.getUnchecked(i)->name == type.name);
jassert (types.getUnchecked(i)->isInstrument == type.isInstrument);
*types.getUnchecked(i) = type;
return false;
*types.getUnchecked(i) = type;
return false;
}
}
types.insert (0, new PluginDescription (type));
}
types.insert (0, new PluginDescription (type));
sendChangeMessage();
return true;
}
void KnownPluginList::removeType (const int index)
{
types.remove (index);
{
ScopedLock lock (typesArrayLock);
types.remove (index);
}
sendChangeMessage();
}
@@ -84,6 +100,8 @@ bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier,
if (getTypeForFile (fileOrIdentifier) == nullptr)
return false;
ScopedLock lock (typesArrayLock);
for (int i = types.size(); --i >= 0;)
{
const PluginDescription* const d = types.getUnchecked(i);
@@ -103,7 +121,7 @@ void KnownPluginList::setCustomScanner (CustomScanner* newScanner)
bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
const bool dontRescanIfAlreadyInList,
OwnedArray <PluginDescription>& typesFound,
OwnedArray<PluginDescription>& typesFound,
AudioPluginFormat& format)
{
const ScopedLock sl (scanLock);
@@ -113,6 +131,8 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
{
bool needsRescanning = false;
ScopedLock lock (typesArrayLock);
for (int i = types.size(); --i >= 0;)
{
const PluginDescription* const d = types.getUnchecked(i);
@@ -133,7 +153,7 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
if (blacklist.contains (fileOrIdentifier))
return false;
OwnedArray <PluginDescription> found;
OwnedArray<PluginDescription> found;
{
const ScopedUnlock sl2 (scanLock);
@@ -163,7 +183,7 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager,
const StringArray& files,
OwnedArray <PluginDescription>& typesFound)
OwnedArray<PluginDescription>& typesFound)
{
for (int i = 0; i < files.size(); ++i)
{
@@ -298,12 +318,17 @@ void KnownPluginList::sort (const SortMethod method, bool forwards)
if (method != defaultOrder)
{
Array<PluginDescription*> oldOrder, newOrder;
oldOrder.addArray (types);
PluginSorter sorter (method, forwards);
types.sort (sorter, true);
{
ScopedLock lock (typesArrayLock);
oldOrder.addArray (types);
newOrder.addArray (types);
PluginSorter sorter (method, forwards);
types.sort (sorter, true);
newOrder.addArray (types);
}
if (oldOrder != newOrder)
sendChangeMessage();
@@ -315,8 +340,12 @@ XmlElement* KnownPluginList::createXml() const
{
XmlElement* const e = new XmlElement ("KNOWNPLUGINS");
for (int i = types.size(); --i >= 0;)
e->prependChildElement (types.getUnchecked(i)->createXml());
{
ScopedLock lock (typesArrayLock);
for (int i = types.size(); --i >= 0;)
e->prependChildElement (types.getUnchecked(i)->createXml());
}
for (int i = 0; i < blacklist.size(); ++i)
e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", blacklist[i]);
@@ -348,7 +377,7 @@ struct PluginTreeUtils
{
enum { menuIdBase = 0x324503f4 };
static void buildTreeByFolder (KnownPluginList::PluginTree& tree, const Array <PluginDescription*>& allPlugins)
static void buildTreeByFolder (KnownPluginList::PluginTree& tree, const Array<PluginDescription*>& allPlugins)
{
for (int i = 0; i < allPlugins.size(); ++i)
{
@@ -392,7 +421,7 @@ struct PluginTreeUtils
}
static void buildTreeByCategory (KnownPluginList::PluginTree& tree,
const Array <PluginDescription*>& sorted,
const Array<PluginDescription*>& sorted,
const KnownPluginList::SortMethod sortMethod)
{
String lastType;
@@ -475,15 +504,21 @@ struct PluginTreeUtils
return false;
}
static void addToMenu (const KnownPluginList::PluginTree& tree, PopupMenu& m, const OwnedArray <PluginDescription>& allPlugins)
static bool addToMenu (const KnownPluginList::PluginTree& tree, PopupMenu& m,
const OwnedArray<PluginDescription>& allPlugins,
const String& currentlyTickedPluginID)
{
bool isTicked = false;
for (int i = 0; i < tree.subFolders.size(); ++i)
{
const KnownPluginList::PluginTree& sub = *tree.subFolders.getUnchecked(i);
PopupMenu subMenu;
addToMenu (sub, subMenu, allPlugins);
m.addSubMenu (sub.folder, subMenu);
const bool isItemTicked = addToMenu (sub, subMenu, allPlugins, currentlyTickedPluginID);
isTicked = isTicked || isItemTicked;
m.addSubMenu (sub.folder, subMenu, true, nullptr, isItemTicked, 0);
}
for (int i = 0; i < tree.plugins.size(); ++i)
@@ -495,16 +530,22 @@ struct PluginTreeUtils
if (containsDuplicateNames (tree.plugins, name))
name << " (" << plugin->pluginFormatName << ')';
m.addItem (allPlugins.indexOf (plugin) + menuIdBase, name, true, false);
const bool isItemTicked = plugin->matchesIdentifierString (currentlyTickedPluginID);
isTicked = isTicked || isItemTicked;
m.addItem (allPlugins.indexOf (plugin) + menuIdBase, name, true, isItemTicked);
}
return isTicked;
}
};
KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortMethod) const
{
Array <PluginDescription*> sorted;
Array<PluginDescription*> sorted;
{
ScopedLock lock (typesArrayLock);
PluginSorter sorter (sortMethod, true);
for (int i = 0; i < types.size(); ++i)
@@ -531,10 +572,11 @@ KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortM
}
//==============================================================================
void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const
void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod,
const String& currentlyTickedPluginID) const
{
ScopedPointer<PluginTree> tree (createTree (sortMethod));
PluginTreeUtils::addToMenu (*tree, menu, types);
PluginTreeUtils::addToMenu (*tree, menu, types, currentlyTickedPluginID);
}
int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const


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

@@ -148,7 +148,8 @@ public:
Use getIndexChosenByMenu() to find out the type that was chosen.
*/
void addToMenu (PopupMenu& menu, SortMethod sortMethod) const;
void addToMenu (PopupMenu& menu, SortMethod sortMethod,
const String& currentlyTickedPluginID = String()) const;
/** Converts a menu item index that has been chosen into its index in this list.
Returns -1 if it's not an ID that was used.
@@ -215,7 +216,7 @@ private:
OwnedArray<PluginDescription> types;
StringArray blacklist;
ScopedPointer<CustomScanner> scanner;
CriticalSection scanLock;
CriticalSection scanLock, typesArrayLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList)
};


+ 5
- 3
source/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp View File

@@ -34,15 +34,17 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo,
AudioPluginFormat& formatToLookFor,
FileSearchPath directoriesToSearch,
const bool recursive,
const File& deadMansPedal)
const File& deadMansPedal,
bool allowPluginsWhichRequireAsynchronousInstantiation)
: list (listToAddTo),
format (formatToLookFor),
deadMansPedalFile (deadMansPedal),
progress (0)
progress (0),
allowAsync (allowPluginsWhichRequireAsynchronousInstantiation)
{
directoriesToSearch.removeRedundantPaths();
filesOrIdentifiersToScan = format.searchPathsForPlugins (directoriesToSearch, recursive);
filesOrIdentifiersToScan = format.searchPathsForPlugins (directoriesToSearch, recursive, allowAsync);
// If any plugins have crashed recently when being loaded, move them to the
// end of the list to give the others a chance to load correctly..


+ 11
- 7
source/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h View File

@@ -44,12 +44,11 @@ public:
@param formatToLookFor this is the type of format that you want to look for
@param directoriesToSearch the path to search
@param searchRecursively true to search recursively
@param deadMansPedalFile if this isn't File::nonexistent, then it will
be used as a file to store the names of any plugins
that crash during initialisation. If there are
any plugins listed in it, then these will always
be scanned after all other possible files have
been tried - in this way, even if there's a few
@param deadMansPedalFile if this isn't File(), then it will be used as a file
to store the names of any plugins that crash during
initialisation. If there are any plugins listed in it,
then these will always be scanned after all other possible
files have been tried - in this way, even if there's a few
dodgy plugins in your path, then a couple of rescans
will still manage to find all the proper plugins.
It's probably best to choose a file in the user's
@@ -57,12 +56,16 @@ public:
settings file) for this. The file format it uses
is just a list of filenames of the modules that
failed.
@param allowPluginsWhichRequireAsynchronousInstantiation
If this is false then the scanner will exclude plug-ins
asynchronous creation - such as AUv3 plug-ins.
*/
PluginDirectoryScanner (KnownPluginList& listToAddResultsTo,
AudioPluginFormat& formatToLookFor,
FileSearchPath directoriesToSearch,
bool searchRecursively,
const File& deadMansPedalFile);
const File& deadMansPedalFile,
bool allowPluginsWhichRequireAsynchronousInstantiation = false);
/** Destructor. */
~PluginDirectoryScanner();
@@ -116,6 +119,7 @@ private:
StringArray failedFiles;
Atomic<int> nextIndex;
float progress;
bool allowAsync;
void updateProgress();
void setDeadMansPedalFile (const StringArray& newContents);


+ 18
- 9
source/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp View File

@@ -123,13 +123,15 @@ public:
//==============================================================================
PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit,
const File& deadMansPedal, PropertiesFile* const props)
const File& deadMansPedal, PropertiesFile* const props,
bool allowPluginsWhichRequireAsynchronousInstantiation)
: formatManager (manager),
list (listToEdit),
deadMansPedalFile (deadMansPedal),
optionsButton ("Options..."),
propertiesToUse (props),
numThreads (0)
allowAsync (allowPluginsWhichRequireAsynchronousInstantiation),
numThreads (allowAsync ? 1 : 0)
{
tableModel = new TableModel (*this, listToEdit);
@@ -331,18 +333,25 @@ class PluginListComponent::Scanner : private Timer
{
public:
Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties,
int threads, const String& title, const String& text)
bool allowPluginsWhichRequireAsynchronousInstantiation, int threads,
const String& title, const String& text)
: owner (plc), formatToScan (format), propertiesToUse (properties),
pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon),
pathChooserWindow (TRANS("Select folders to scan..."), String(), AlertWindow::NoIcon),
progressWindow (title, text, AlertWindow::NoIcon),
progress (0.0), numThreads (threads), finished (false)
progress (0.0), numThreads (threads), allowAsync (allowPluginsWhichRequireAsynchronousInstantiation),
finished (false)
{
FileSearchPath path (formatToScan.getDefaultLocationsToSearch());
// You need to use at least one thread when scanning plug-ins asynchronously
jassert (! allowAsync || (numThreads > 0));
if (path.getNumPaths() > 0) // if the path is empty, then paths aren't used for this format.
{
#if ! JUCE_IOS
if (propertiesToUse != nullptr)
path = getLastSearchPath (*propertiesToUse, formatToScan);
#endif
pathList.setSize (500, 300);
pathList.setPath (path);
@@ -381,7 +390,7 @@ private:
String pluginBeingScanned;
double progress;
int numThreads;
bool finished;
bool allowAsync, finished;
ScopedPointer<ThreadPool> pool;
static void startScanCallback (int result, AlertWindow* alert, Scanner* scanner)
@@ -413,7 +422,7 @@ private:
+ TRANS ("Are you sure you want to scan the folder \"XYZ\"?")
.replace ("XYZ", f.getFullPathName()),
TRANS ("Scan"),
String::empty,
String(),
nullptr,
ModalCallbackFunction::create (warnAboutStupidPathsCallback, this));
return;
@@ -465,7 +474,7 @@ private:
pathChooserWindow.setVisible (false);
scanner = new PluginDirectoryScanner (owner.list, formatToScan, pathList.getPath(),
true, owner.deadMansPedalFile);
true, owner.deadMansPedalFile, allowAsync);
if (propertiesToUse != nullptr)
{
@@ -545,7 +554,7 @@ private:
void PluginListComponent::scanFor (AudioPluginFormat& format)
{
currentScanner = new Scanner (*this, format, propertiesToUse, numThreads,
currentScanner = new Scanner (*this, format, propertiesToUse, allowAsync, numThreads,
dialogTitle.isNotEmpty() ? dialogTitle : TRANS("Scanning for plug-ins..."),
dialogText.isNotEmpty() ? dialogText : TRANS("Searching for all possible plug-in files..."));
}


+ 7
- 3
source/modules/juce_audio_processors/scanning/juce_PluginListComponent.h View File

@@ -47,7 +47,8 @@ public:
PluginListComponent (AudioPluginFormatManager& formatManager,
KnownPluginList& listToRepresent,
const File& deadMansPedalFile,
PropertiesFile* propertiesToUse);
PropertiesFile* propertiesToUse,
bool allowPluginsWhichRequireAsynchronousInstantiation = false);
/** Destructor. */
~PluginListComponent();
@@ -60,8 +61,10 @@ public:
const String& textForProgressWindowDescription);
/** Sets how many threads to simultaneously scan for plugins.
If this is 0, then all scanning happens on the message thread (this is the default)
*/
If this is 0, then all scanning happens on the message thread (this is the default when
allowPluginsWhichRequireAsynchronousInstantiation is false). If
allowPluginsWhichRequireAsynchronousInstantiation is true then numThreads must not
be zero (it is one by default). */
void setNumberOfThreadsForScanning (int numThreads);
/** Returns the last search path stored in a given properties file for the specified format. */
@@ -96,6 +99,7 @@ private:
TextButton optionsButton;
PropertiesFile* propertiesToUse;
String dialogTitle, dialogText;
bool allowAsync;
int numThreads;
class TableModel;


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

Loading…
Cancel
Save