Browse Source

Update juce (needs full rebuild)

tags/1.9.7
falkTX 9 years ago
parent
commit
b6a6d8799a
100 changed files with 7004 additions and 800 deletions
  1. +1
    -1
      source/backend/CarlaHostCommon.cpp
  2. +2
    -2
      source/backend/CarlaStandalone.cpp
  3. +1
    -1
      source/backend/CarlaStandaloneNSM.cpp
  4. +2
    -2
      source/backend/CarlaUtils.cpp
  5. +1
    -1
      source/backend/engine/CarlaEngine.cpp
  6. +24
    -8
      source/backend/engine/CarlaEngineGraph.cpp
  7. +2
    -1
      source/backend/engine/CarlaEngineGraph.hpp
  8. +1
    -1
      source/backend/engine/CarlaEngineJack.cpp
  9. +2
    -2
      source/backend/engine/CarlaEngineNative.cpp
  10. +1
    -1
      source/backend/engine/CarlaEngineRtAudio.cpp
  11. +1
    -1
      source/backend/plugin/CarlaPlugin.cpp
  12. +1
    -1
      source/backend/plugin/CarlaPluginFluidSynth.cpp
  13. +1
    -1
      source/backend/plugin/CarlaPluginInternal.hpp
  14. +4
    -4
      source/backend/plugin/CarlaPluginJuce.cpp
  15. +1
    -1
      source/backend/plugin/CarlaPluginLV2.cpp
  16. +1
    -1
      source/backend/plugin/CarlaPluginLinuxSampler.cpp
  17. +1
    -1
      source/backend/plugin/CarlaPluginNative.cpp
  18. +1
    -1
      source/backend/plugin/CarlaPluginVST2.cpp
  19. +2
    -2
      source/bridges-plugin/CarlaBridgePlugin.cpp
  20. +1
    -1
      source/bridges-ui/CarlaBridgeUI-LV2.cpp
  21. +5
    -4
      source/discovery/carla-discovery.cpp
  22. +391
    -0
      source/modules/AppConfig.h
  23. +0
    -26
      source/modules/juce_audio_basics.h
  24. +0
    -14
      source/modules/juce_audio_basics/AppConfig.h
  25. +1
    -1
      source/modules/juce_audio_basics/Makefile
  26. +7
    -7
      source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h
  27. +6
    -2
      source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp
  28. +1
    -1
      source/modules/juce_audio_basics/effects/juce_FFT.cpp
  29. +97
    -0
      source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h
  30. +1
    -49
      source/modules/juce_audio_basics/effects/juce_Reverb.h
  31. +12
    -5
      source/modules/juce_audio_basics/juce_audio_basics.cpp
  32. +11
    -0
      source/modules/juce_audio_basics/juce_audio_basics.h
  33. +2
    -2
      source/modules/juce_audio_basics/midi/juce_MidiFile.cpp
  34. +2
    -2
      source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp
  35. +374
    -0
      source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp
  36. +152
    -0
      source/modules/juce_audio_basics/midi/juce_MidiRPN.h
  37. +2150
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp
  38. +414
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h
  39. +198
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp
  40. +96
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEMessages.h
  41. +132
    -0
      source/modules/juce_audio_basics/mpe/juce_MPENote.cpp
  42. +180
    -0
      source/modules/juce_audio_basics/mpe/juce_MPENote.h
  43. +356
    -0
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp
  44. +313
    -0
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h
  45. +161
    -0
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp
  46. +194
    -0
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h
  47. +53
    -0
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp
  48. +191
    -0
      source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h
  49. +170
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp
  50. +96
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEValue.h
  51. +302
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp
  52. +132
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZone.h
  53. +346
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp
  54. +129
    -0
      source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h
  55. +1
    -1
      source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h
  56. +6
    -2
      source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp
  57. +0
    -30
      source/modules/juce_audio_devices.h
  58. +0
    -79
      source/modules/juce_audio_devices/AppConfig.h
  59. +1
    -1
      source/modules/juce_audio_devices/Makefile
  60. +1
    -1
      source/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h
  61. +106
    -200
      source/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
  62. +1
    -5
      source/modules/juce_audio_devices/juce_audio_devices.cpp
  63. +1
    -2
      source/modules/juce_audio_devices/midi_io/juce_MidiInput.h
  64. +10
    -0
      source/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp
  65. +4
    -3
      source/modules/juce_audio_devices/midi_io/juce_MidiOutput.h
  66. +1
    -1
      source/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h
  67. +5
    -3
      source/modules/juce_audio_devices/native/juce_android_OpenSL.cpp
  68. +2
    -2
      source/modules/juce_audio_devices/native/juce_ios_Audio.cpp
  69. +1
    -1
      source/modules/juce_audio_devices/native/juce_linux_ALSA.cpp
  70. +4
    -4
      source/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
  71. +1
    -1
      source/modules/juce_audio_devices/native/juce_linux_Midi.cpp
  72. +1
    -1
      source/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm
  73. +0
    -17
      source/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp
  74. +1
    -1
      source/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp
  75. +13
    -20
      source/modules/juce_audio_devices/native/juce_win32_ASIO.cpp
  76. +1
    -1
      source/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp
  77. +7
    -7
      source/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp
  78. +5
    -7
      source/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp
  79. +3
    -3
      source/modules/juce_audio_devices/native/juce_win32_Midi.cpp
  80. +1
    -1
      source/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
  81. +0
    -26
      source/modules/juce_audio_formats.h
  82. +0
    -53
      source/modules/juce_audio_formats/AppConfig.h
  83. +1
    -1
      source/modules/juce_audio_formats/Makefile
  84. +4
    -4
      source/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp
  85. +2
    -2
      source/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp
  86. +4
    -1
      source/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp
  87. +2
    -2
      source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp
  88. +7
    -3
      source/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp
  89. +3
    -3
      source/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp
  90. +2
    -2
      source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp
  91. +1
    -1
      source/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp
  92. +2
    -2
      source/modules/juce_audio_formats/format/juce_AudioFormatManager.h
  93. +1
    -1
      source/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp
  94. +1
    -5
      source/modules/juce_audio_formats/juce_audio_formats.cpp
  95. +2
    -2
      source/modules/juce_audio_formats/sampler/juce_Sampler.cpp
  96. +0
    -28
      source/modules/juce_audio_processors.h
  97. +0
    -58
      source/modules/juce_audio_processors/AppConfig.h
  98. +2
    -1
      source/modules/juce_audio_processors/Makefile
  99. +4
    -4
      source/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp
  100. +63
    -60
      source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm

+ 1
- 1
source/backend/CarlaHostCommon.cpp View File

@@ -18,7 +18,7 @@
#include "CarlaHost.h"
#include "CarlaString.hpp"

#include "juce_core.h"
#include "juce_core/juce_core.h"

namespace CB = CarlaBackend;



+ 2
- 2
source/backend/CarlaStandalone.cpp View File

@@ -27,10 +27,10 @@
#include "CarlaBackendUtils.hpp"
#include "CarlaBase64Utils.hpp"

#include "juce_audio_formats.h"
#include "juce_audio_formats/juce_audio_formats.h"

#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
# include "juce_gui_basics.h"
# include "juce_gui_basics/juce_gui_basics.h"
#else
namespace juce {
# include "juce_events/messages/juce_Initialisation.h"


+ 1
- 1
source/backend/CarlaStandaloneNSM.cpp View File

@@ -28,7 +28,7 @@
#include "CarlaHost.h"
#include "CarlaOscUtils.hpp"
#include "CarlaString.hpp"
#include "juce_core.h"
#include "juce_core/juce_core.h"

namespace CB = CarlaBackend;



+ 2
- 2
source/backend/CarlaUtils.cpp View File

@@ -25,10 +25,10 @@
#include "CarlaThread.hpp"
#include "LinkedList.hpp"

#include "juce_audio_formats.h"
#include "juce_audio_formats/juce_audio_formats.h"

#ifdef CARLA_OS_MAC
# include "juce_audio_processors.h"
# include "juce_audio_processors/juce_audio_processors.h"
#endif

#include "../native-plugins/_data.cpp"


+ 1
- 1
source/backend/engine/CarlaEngine.cpp View File

@@ -35,7 +35,7 @@
#include "CarlaMIDI.h"

#include "jackbridge/JackBridge.hpp"
#include "juce_core.h"
#include "juce_core/juce_core.h"

using juce::CharPointer_UTF8;
using juce::File;


+ 24
- 8
source/backend/engine/CarlaEngineGraph.cpp View File

@@ -22,6 +22,15 @@
#include "CarlaMathUtils.hpp"
#include "CarlaMIDI.h"

// FIXME: update to new Juce API
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif

using juce::AudioPluginInstance;
using juce::AudioProcessor;
using juce::AudioProcessorEditor;
@@ -1045,12 +1054,12 @@ const String getProcessorFullPortName(AudioProcessor* const proc, const uint32_t
}
else if (portId >= kAudioOutputPortOffset)
{
CARLA_SAFE_ASSERT_RETURN(proc->getNumOutputChannels() > 0, String());
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumOutputChannels() > 0, String());
fullPortName += ":" + proc->getOutputChannelName(static_cast<int>(portId-kAudioOutputPortOffset));
}
else if (portId >= kAudioInputPortOffset)
{
CARLA_SAFE_ASSERT_RETURN(proc->getNumInputChannels() > 0, String());
CARLA_SAFE_ASSERT_RETURN(proc->getTotalNumInputChannels() > 0, String());
fullPortName += ":" + proc->getInputChannelName(static_cast<int>(portId-kAudioInputPortOffset));
}
else
@@ -1070,13 +1079,13 @@ void addNodeToPatchbay(CarlaEngine* const engine, const uint32_t groupId, const
const int icon((clientId >= 0) ? PATCHBAY_ICON_PLUGIN : PATCHBAY_ICON_HARDWARE);
engine->callback(ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED, groupId, icon, clientId, 0.0f, proc->getName().toRawUTF8());

for (int i=0, numInputs=proc->getNumInputChannels(); i<numInputs; ++i)
for (int i=0, numInputs=proc->getTotalNumInputChannels(); i<numInputs; ++i)
{
engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast<int>(kAudioInputPortOffset)+i,
PATCHBAY_PORT_TYPE_AUDIO|PATCHBAY_PORT_IS_INPUT, 0.0f, proc->getInputChannelName(i).toRawUTF8());
}

for (int i=0, numOutputs=proc->getNumOutputChannels(); i<numOutputs; ++i)
for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); i<numOutputs; ++i)
{
engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_ADDED, groupId, static_cast<int>(kAudioOutputPortOffset)+i,
PATCHBAY_PORT_TYPE_AUDIO, 0.0f, proc->getOutputChannelName(i).toRawUTF8());
@@ -1101,13 +1110,13 @@ void removeNodeFromPatchbay(CarlaEngine* const engine, const uint32_t groupId, c
CARLA_SAFE_ASSERT_RETURN(engine != nullptr,);
CARLA_SAFE_ASSERT_RETURN(proc != nullptr,);

for (int i=0, numInputs=proc->getNumInputChannels(); i<numInputs; ++i)
for (int i=0, numInputs=proc->getTotalNumInputChannels(); i<numInputs; ++i)
{
engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast<int>(kAudioInputPortOffset)+i,
0, 0.0f, nullptr);
}

for (int i=0, numOutputs=proc->getNumOutputChannels(); i<numOutputs; ++i)
for (int i=0, numOutputs=proc->getTotalNumOutputChannels(); i<numOutputs; ++i)
{
engine->callback(ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED, groupId, static_cast<int>(kAudioOutputPortOffset)+i,
0, 0.0f, nullptr);
@@ -1799,7 +1808,7 @@ bool PatchbayGraph::getGroupAndPortIdFromFullName(const bool external, const cha
return true;
}

for (int j=0, numInputs=proc->getNumInputChannels(); j<numInputs; ++j)
for (int j=0, numInputs=proc->getTotalNumInputChannels(); j<numInputs; ++j)
{
if (proc->getInputChannelName(j) != portName)
continue;
@@ -1808,7 +1817,7 @@ bool PatchbayGraph::getGroupAndPortIdFromFullName(const bool external, const cha
return true;
}

for (int j=0, numOutputs=proc->getNumOutputChannels(); j<numOutputs; ++j)
for (int j=0, numOutputs=proc->getTotalNumOutputChannels(); j<numOutputs; ++j)
{
if (proc->getOutputChannelName(j) != portName)
continue;
@@ -2252,4 +2261,11 @@ bool CarlaEngine::disconnectExternalGraphPort(const uint connectionType, const u

CARLA_BACKEND_END_NAMESPACE

// enable -Wdeprecated-declarations again
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
# pragma GCC diagnostic pop
#endif

// -----------------------------------------------------------------------

+ 2
- 1
source/backend/engine/CarlaEngineGraph.hpp View File

@@ -23,7 +23,8 @@
#include "CarlaPatchbayUtils.hpp"
#include "CarlaStringList.hpp"

#include "juce_audio_processors.h"
#include "juce_audio_processors/juce_audio_processors.h"

using juce::AudioProcessorGraph;
using juce::AudioSampleBuffer;
using juce::MidiBuffer;


+ 1
- 1
source/backend/engine/CarlaEngineJack.cpp View File

@@ -26,7 +26,7 @@
#include "CarlaStringList.hpp"

#include "jackey.h"
#include "juce_audio_basics.h"
#include "juce_audio_basics/juce_audio_basics.h"

#ifdef __SSE2_MATH__
# include <xmmintrin.h>


+ 2
- 2
source/backend/engine/CarlaEngineNative.cpp View File

@@ -34,10 +34,10 @@
#include "CarlaHost.h"
#include "CarlaNative.hpp"

#include "juce_audio_basics.h"
#include "juce_audio_basics/juce_audio_basics.h"

#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
# include "juce_gui_basics.h"
# include "juce_gui_basics/juce_gui_basics.h"
#else
namespace juce {
# include "juce_events/messages/juce_Initialisation.h"


+ 1
- 1
source/backend/engine/CarlaEngineRtAudio.cpp View File

@@ -24,7 +24,7 @@
#include "RtLinkedList.hpp"

#include "jackbridge/JackBridge.hpp"
#include "juce_audio_basics.h"
#include "juce_audio_basics/juce_audio_basics.h"

#include "rtaudio/RtAudio.h"
#include "rtmidi/RtMidi.h"


+ 1
- 1
source/backend/plugin/CarlaPlugin.cpp View File

@@ -25,7 +25,7 @@

#include <ctime>

#include "juce_core.h"
#include "juce_core/juce_core.h"

using juce::CharPointer_UTF8;
using juce::File;


+ 1
- 1
source/backend/plugin/CarlaPluginFluidSynth.cpp View File

@@ -22,7 +22,7 @@

#include "CarlaMathUtils.hpp"

#include "juce_core.h"
#include "juce_core/juce_core.h"

#include <fluidsynth.h>



+ 1
- 1
source/backend/plugin/CarlaPluginInternal.hpp View File

@@ -28,7 +28,7 @@
#include "CarlaString.hpp"
#include "RtLinkedList.hpp"

#include "juce_audio_basics.h"
#include "juce_audio_basics/juce_audio_basics.h"

using juce::FloatVectorOperations;



+ 4
- 4
source/backend/plugin/CarlaPluginJuce.cpp View File

@@ -24,7 +24,7 @@
#include "CarlaMathUtils.hpp"
#include "JucePluginWindow.hpp"

#include "juce_audio_processors.h"
#include "juce_audio_processors/juce_audio_processors.h"

using namespace juce;

@@ -359,9 +359,9 @@ public:
bool needsCtrlIn, needsCtrlOut;
needsCtrlIn = needsCtrlOut = false;

aIns = (fInstance->getNumInputChannels() > 0) ? static_cast<uint32_t>(fInstance->getNumInputChannels()) : 0;
aOuts = (fInstance->getNumOutputChannels() > 0) ? static_cast<uint32_t>(fInstance->getNumOutputChannels()) : 0;
params = (fInstance->getNumParameters() > 0) ? static_cast<uint32_t>(fInstance->getNumParameters()) : 0;
aIns = (fInstance->getTotalNumInputChannels() > 0) ? static_cast<uint32_t>(fInstance->getTotalNumInputChannels()) : 0;
aOuts = (fInstance->getTotalNumOutputChannels() > 0) ? static_cast<uint32_t>(fInstance->getTotalNumOutputChannels()) : 0;
params = (fInstance->getNumParameters() > 0) ? static_cast<uint32_t>(fInstance->getNumParameters()) : 0;

if (fInstance->acceptsMidi())
{


+ 1
- 1
source/backend/plugin/CarlaPluginLV2.cpp View File

@@ -37,7 +37,7 @@ extern "C" {
#include "rtmempool/rtmempool-lv2.h"
}

#include "juce_core.h"
#include "juce_core/juce_core.h"

using juce::File;



+ 1
- 1
source/backend/plugin/CarlaPluginLinuxSampler.cpp View File

@@ -31,7 +31,7 @@
#include "CarlaBackendUtils.hpp"
#include "CarlaMathUtils.hpp"

#include "juce_core.h"
#include "juce_core/juce_core.h"

#include <linuxsampler/Sampler.h>



+ 1
- 1
source/backend/plugin/CarlaPluginNative.cpp View File

@@ -21,7 +21,7 @@
#include "CarlaMathUtils.hpp"
#include "CarlaNative.h"

#include "juce_core.h"
#include "juce_core/juce_core.h"

using juce::jmax;
using juce::String;


+ 1
- 1
source/backend/plugin/CarlaPluginVST2.cpp View File

@@ -29,7 +29,7 @@
#include "CarlaMathUtils.hpp"
#include "CarlaPluginUI.hpp"

#include "juce_core.h"
#include "juce_core/juce_core.h"

#include <pthread.h>



+ 2
- 2
source/bridges-plugin/CarlaBridgePlugin.cpp View File

@@ -30,10 +30,10 @@
#endif

#include "jackbridge/JackBridge.hpp"
#include "juce_core.h"
#include "juce_core/juce_core.h"

#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
# include "juce_gui_basics.h"
# include "juce_gui_basics/juce_gui_basics.h"
using juce::JUCEApplication;
using juce::JUCEApplicationBase;
using juce::Timer;


+ 1
- 1
source/bridges-ui/CarlaBridgeUI-LV2.cpp View File

@@ -21,7 +21,7 @@
#include "CarlaMIDI.h"
#include "LinkedList.hpp"

#include "juce_core.h"
#include "juce_core/juce_core.h"

#define URI_CARLA_ATOM_WORKER "http://kxstudio.sf.net/ns/carla/atomWorker"



+ 5
- 4
source/discovery/carla-discovery.cpp View File

@@ -23,7 +23,7 @@

#if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN)
# define USE_JUCE_PROCESSORS
# include "juce_audio_processors.h"
# include "juce_audio_processors/juce_audio_processors.h"
#endif

#ifdef BUILD_BRIDGE
@@ -46,14 +46,15 @@

#include <iostream>

#include "juce_core.h"
#include "juce_core/juce_core.h"

#define DISCOVERY_OUT(x, y) std::cout << "\ncarla-discovery::" << x << "::" << y << std::endl;

using juce::CharPointer_UTF8;
using juce::File;
using juce::String;
using juce::StringArray;

#define DISCOVERY_OUT(x, y) std::cout << "\ncarla-discovery::" << x << "::" << y << std::endl;

CARLA_BACKEND_USE_NAMESPACE

// --------------------------------------------------------------------------


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

@@ -0,0 +1,391 @@
#ifndef CARLA_JUCE_APPCONFIG_H_INCLUDED
#define CARLA_JUCE_APPCONFIG_H_INCLUDED
// --------------------------------------------------------------------------------------------------------------------
// Check OS
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
# define APPCONFIG_OS_WIN64
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
# define APPCONFIG_OS_WIN32
#elif defined(__APPLE__)
# define APPCONFIG_OS_MAC
#elif defined(__HAIKU__)
# define APPCONFIG_OS_HAIKU
#elif defined(__linux__) || defined(__linux)
# define APPCONFIG_OS_LINUX
#else
# warning Unsupported platform!
#endif
#if defined(APPCONFIG_OS_WIN32) || defined(APPCONFIG_OS_WIN64)
# define APPCONFIG_OS_WIN
#elif defined(APPCONFIG_OS_LINUX) || defined(APPCONFIG_OS_MAC)
# define APPCONFIG_OS_UNIX
#endif
// --------------------------------------------------------------------------------------------------------------------
// always enabled
#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1
#define JUCE_MODULE_AVAILABLE_juce_audio_formats 1
#define JUCE_MODULE_AVAILABLE_juce_core 1
// always disabled
#define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 0
#define JUCE_MODULE_AVAILABLE_juce_audio_utils 0
#define JUCE_MODULE_AVAILABLE_juce_cryptography 0
#define JUCE_MODULE_AVAILABLE_juce_opengl 0
#define JUCE_MODULE_AVAILABLE_juce_video 0
// conditional
#if defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN)
# define JUCE_MODULE_AVAILABLE_juce_audio_devices 1
# define JUCE_MODULE_AVAILABLE_juce_audio_processors 1
# define JUCE_MODULE_AVAILABLE_juce_data_structures 1
# define JUCE_MODULE_AVAILABLE_juce_events 1
# define JUCE_MODULE_AVAILABLE_juce_graphics 1
# define JUCE_MODULE_AVAILABLE_juce_gui_basics 1
# define JUCE_MODULE_AVAILABLE_juce_gui_extra 1
#else
# define JUCE_MODULE_AVAILABLE_juce_audio_devices 0
# define JUCE_MODULE_AVAILABLE_juce_audio_processors 0
# define JUCE_MODULE_AVAILABLE_juce_data_structures 0
# define JUCE_MODULE_AVAILABLE_juce_events 0
# define JUCE_MODULE_AVAILABLE_juce_graphics 0
# define JUCE_MODULE_AVAILABLE_juce_gui_basics 0
# define JUCE_MODULE_AVAILABLE_juce_gui_extra 0
#endif
// misc
#define JUCE_DISABLE_JUCE_VERSION_PRINTING 1
#define JUCE_STANDALONE_APPLICATION 0
#define JUCE_STRING_UTF_TYPE 8
#define JUCE_USE_VFORK 1
#if ! (defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN))
# define JUCE_MODAL_LOOPS_PERMITTED 0
# define JUCE_AUDIO_PROCESSOR_NO_GUI 1
#endif
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_basics
// nothing here
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_devices
//=============================================================================
/** Config: JUCE_ASIO
Enables ASIO audio devices (MS Windows only).
Turning this on means that you'll need to have the Steinberg ASIO SDK installed
on your Windows build machine.
See the comments in the ASIOAudioIODevice class's header file for more
info about this.
*/
#ifdef APPCONFIG_OS_WIN
#define JUCE_ASIO 1
#else
#define JUCE_ASIO 0
#endif
/** Config: JUCE_WASAPI
Enables WASAPI audio devices (Windows Vista and above).
*/
#define JUCE_WASAPI 0
/** Config: JUCE_DIRECTSOUND
Enables DirectSound audio (MS Windows only).
*/
#ifdef APPCONFIG_OS_WIN
#define JUCE_DIRECTSOUND 1
#else
#define JUCE_DIRECTSOUND 0
#endif
/** Config: JUCE_ALSA
Enables ALSA audio devices (Linux only).
*/
#if 0 //APPCONFIG_OS_LINUX
#define JUCE_ALSA 1
#define JUCE_ALSA_MIDI_INPUT_NAME "Carla"
#define JUCE_ALSA_MIDI_OUTPUT_NAME "Carla"
#define JUCE_ALSA_MIDI_INPUT_PORT_NAME "Midi In"
#define JUCE_ALSA_MIDI_OUTPUT_PORT_NAME "Midi Out"
#else
#define JUCE_ALSA 0
#endif
/** Config: JUCE_JACK
Enables JACK audio devices (Linux only).
*/
#if 0 //APPCONFIG_OS_LINUX
#define JUCE_JACK 1
#define JUCE_JACK_CLIENT_NAME "Carla"
#else
#define JUCE_JACK 0
#endif
//=============================================================================
/** Config: JUCE_USE_CDREADER
Enables the AudioCDReader class (on supported platforms).
*/
#define JUCE_USE_CDREADER 0
/** Config: JUCE_USE_CDBURNER
Enables the AudioCDBurner class (on supported platforms).
*/
#define JUCE_USE_CDBURNER 0
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_formats
//=============================================================================
/** Config: JUCE_USE_FLAC
Enables the FLAC audio codec classes (available on all platforms).
If your app doesn't need to read FLAC files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_FLAC 1
/** Config: JUCE_USE_OGGVORBIS
Enables the Ogg-Vorbis audio codec classes (available on all platforms).
If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_OGGVORBIS 1
/** Config: JUCE_USE_MP3AUDIOFORMAT
Enables the software-based MP3AudioFormat class.
IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile
this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing
that Raw Material Software is in no way responsible for any patent, copyright, or other
legal issues that you may suffer as a result.
The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party
intellectual property. If you wish to use it, please seek your own independent advice about the
legality of doing so. If you are not willing to accept full responsibility for the consequences
of using this code, then do not enable this setting.
*/
#define JUCE_USE_MP3AUDIOFORMAT 0
/** Config: JUCE_USE_LAME_AUDIO_FORMAT
Enables the LameEncoderAudioFormat class.
*/
#define JUCE_USE_LAME_AUDIO_FORMAT 1
/** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT
Enables the Windows Media SDK codecs.
*/
#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_processors
//=============================================================================
/** Config: JUCE_PLUGINHOST_VST
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be
installed on your machine.
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU
*/
#ifndef VESTIGE_HEADER
# define JUCE_PLUGINHOST_VST 1
#else
# define JUCE_PLUGINHOST_VST 0
#endif
/** Config: JUCE_PLUGINHOST_VST3
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be
installed on your machine.
@see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU
*/
#if defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN)
# define JUCE_PLUGINHOST_VST3 1
#else
# define JUCE_PLUGINHOST_VST3 0
#endif
/** Config: JUCE_PLUGINHOST_AU
Enables the AudioUnit plugin hosting classes. This is Mac-only, of course.
@see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST
*/
#ifdef APPCONFIG_OS_MAC
# define JUCE_PLUGINHOST_AU 1
#else
# define JUCE_PLUGINHOST_AU 0
#endif
#define JUCE_PLUGINHOST_LADSPA 0
// --------------------------------------------------------------------------------------------------------------------
// juce_core
//=============================================================================
/** Config: JUCE_FORCE_DEBUG
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings,
but if you define this value, you can override this to force it to be true or false.
*/
#define JUCE_FORCE_DEBUG 0
//=============================================================================
/** Config: JUCE_LOG_ASSERTIONS
If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog()
to write a message when an assertion happens.
Enabling it will also leave this turned on in release builds. When it's disabled,
however, the jassert and jassertfalse macros will not be compiled in a
release build.
@see jassert, jassertfalse, Logger
*/
#define JUCE_LOG_ASSERTIONS 1
//=============================================================================
/** Config: JUCE_CHECK_MEMORY_LEAKS
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector
class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes.
*/
#ifdef DEBUG
#define JUCE_CHECK_MEMORY_LEAKS 1
#else
#define JUCE_CHECK_MEMORY_LEAKS 0
#endif
//=============================================================================
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
In a Visual C++ build, this can be used to stop the required system libs being
automatically added to the link stage.
*/
#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0
/** Config: JUCE_INCLUDE_ZLIB_CODE
This can be used to disable Juce's embedded 3rd-party zlib code.
You might need to tweak this if you're linking to an external zlib library in your app,
but for normal apps, this option should be left alone.
If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to
specify the path where your zlib headers live.
*/
#define JUCE_INCLUDE_ZLIB_CODE 1
/** Config: JUCE_USE_CURL
Enables http/https support via libcurl (Linux only). Enabling this will add an additional
run-time dynmic dependency to libcurl.
If you disable this then https/ssl support will not be available on linux.
*/
#define JUCE_USE_CURL 0
/* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS
If enabled, this will add some exception-catching code to forward unhandled exceptions
to your JUCEApplicationBase::unhandledException() callback.
*/
#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0
// --------------------------------------------------------------------------------------------------------------------
// juce_data_structures
// nothing here
// --------------------------------------------------------------------------------------------------------------------
// juce_events
// nothing here
// --------------------------------------------------------------------------------------------------------------------
// juce_graphics
//=============================================================================
/** Config: JUCE_USE_COREIMAGE_LOADER
On OSX, enabling this flag means that the CoreImage codecs will be used to load
PNG/JPEG/GIF files. It is enabled by default, but you may want to disable it if
you'd rather use libpng, libjpeg, etc.
*/
#define JUCE_USE_COREIMAGE_LOADER 1
/** Config: JUCE_USE_DIRECTWRITE
Enabling this flag means that DirectWrite will be used when available for font
management and layout.
*/
#define JUCE_USE_DIRECTWRITE 1
#define JUCE_INCLUDE_PNGLIB_CODE 1
#define JUCE_INCLUDE_JPEGLIB_CODE 1
#define USE_COREGRAPHICS_RENDERING 1
// --------------------------------------------------------------------------------------------------------------------
// juce_gui_basics
//=============================================================================
/** Config: JUCE_ENABLE_REPAINT_DEBUGGING
If this option is turned on, each area of the screen that gets repainted will
flash in a random colour, so that you can see exactly which bits of your
components are being drawn.
*/
#define JUCE_ENABLE_REPAINT_DEBUGGING 0
/** JUCE_USE_XRANDR: Enables Xrandr multi-monitor support (Linux only).
Unless you specifically want to disable this, it's best to leave this option turned on.
Note that your users do not need to have Xrandr installed for your JUCE app to run, as
the availability of Xrandr is queried during runtime.
*/
#define JUCE_USE_XRANDR 0
/** JUCE_USE_XINERAMA: Enables Xinerama multi-monitor support (Linux only).
Unless you specifically want to disable this, it's best to leave this option turned on.
This will be used as a fallback if JUCE_USE_XRANDR not set or libxrandr cannot be found.
Note that your users do not need to have Xrandr installed for your JUCE app to run, as
the availability of Xinerama is queried during runtime.
*/
#define JUCE_USE_XINERAMA 0
/** Config: JUCE_USE_XSHM
Enables X shared memory for faster rendering on Linux. This is best left turned on
unless you have a good reason to disable it.
*/
#define JUCE_USE_XSHM 1
/** Config: JUCE_USE_XRENDER
Enables XRender to allow semi-transparent windowing on Linux.
*/
#define JUCE_USE_XRENDER 0
/** Config: JUCE_USE_XCURSOR
Uses XCursor to allow ARGB cursor on Linux. This is best left turned on unless you have
a good reason to disable it.
*/
#define JUCE_USE_XCURSOR 1
// --------------------------------------------------------------------------------------------------------------------
// juce_gui_extra
//=============================================================================
/** Config: JUCE_WEB_BROWSER
This lets you disable the WebBrowserComponent class (Mac and Windows).
If you're not using any embedded web-pages, turning this off may reduce your code size.
*/
#define JUCE_WEB_BROWSER 0
/** Config: JUCE_ENABLE_LIVE_CONSTANT_EDITOR
This lets you turn on the JUCE_ENABLE_LIVE_CONSTANT_EDITOR support. See the documentation
for that macro for more details.
*/
#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 0
// --------------------------------------------------------------------------------------------------------------------
#endif // CARLA_JUCE_APPCONFIG_H_INCLUDED

+ 0
- 26
source/modules/juce_audio_basics.h View File

@@ -1,26 +0,0 @@
/*
* Carla Juce setup
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program 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.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef CARLA_JUCE_AUDIO_BASICS_H_INCLUDED
#define CARLA_JUCE_AUDIO_BASICS_H_INCLUDED

#include "juce_core.h"

#include "juce_audio_basics/AppConfig.h"
#include "juce_audio_basics/juce_audio_basics.h"

#endif // CARLA_JUCE_AUDIO_BASICS_H_INCLUDED

+ 0
- 14
source/modules/juce_audio_basics/AppConfig.h View File

@@ -1,14 +0,0 @@
/*
==============================================================================
Build options for juce_audio_basics static library
==============================================================================
*/
#ifndef CARLA_JUCE_AUDIO_BASICS_APPCONFIG_H_INCLUDED
#define CARLA_JUCE_AUDIO_BASICS_APPCONFIG_H_INCLUDED
#include "../juce_core/AppConfig.h"
#endif // CARLA_JUCE_AUDIO_BASICS_APPCONFIG_H_INCLUDED

+ 1
- 1
source/modules/juce_audio_basics/Makefile View File

@@ -10,7 +10,7 @@ include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(JUCE_AUDIO_BASICS_FLAGS) -w
BUILD_CXX_FLAGS += $(JUCE_AUDIO_BASICS_FLAGS) -I..

# ----------------------------------------------------------------------------------------------------------------------------



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

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


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

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


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

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


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

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

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

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


+ 12
- 5
source/modules/juce_audio_basics/juce_audio_basics.cpp View File

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


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

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


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

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


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

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


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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


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

@@ -1,30 +0,0 @@
/*
* Carla Juce setup
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program 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.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED
#define CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED

#include "juce_events.h"
#include "juce_audio_basics.h"
#include "juce_audio_formats.h"

#if 1 //JUCE_MAC || JUCE_WINDOWS
# include "juce_audio_devices/AppConfig.h"
# include "juce_audio_devices/juce_audio_devices.h"
#endif

#endif // CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED

+ 0
- 79
source/modules/juce_audio_devices/AppConfig.h View File

@@ -1,79 +0,0 @@
/*
==============================================================================
Build options for juce_audio_devices static library
==============================================================================
*/
#ifndef CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED
#define CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED
#include "../juce_events/AppConfig.h"
#include "../juce_audio_basics/AppConfig.h"
#include "../juce_audio_formats/AppConfig.h"
//=============================================================================
/** Config: JUCE_ASIO
Enables ASIO audio devices (MS Windows only).
Turning this on means that you'll need to have the Steinberg ASIO SDK installed
on your Windows build machine.
See the comments in the ASIOAudioIODevice class's header file for more
info about this.
*/
#if JUCE_WINDOWS
#define JUCE_ASIO 1
#else
#define JUCE_ASIO 0
#endif
/** Config: JUCE_WASAPI
Enables WASAPI audio devices (Windows Vista and above).
*/
#define JUCE_WASAPI 0
/** Config: JUCE_DIRECTSOUND
Enables DirectSound audio (MS Windows only).
*/
#if JUCE_WINDOWS
#define JUCE_DIRECTSOUND 1
#else
#define JUCE_DIRECTSOUND 0
#endif
/** Config: JUCE_ALSA
Enables ALSA audio devices (Linux only).
*/
#if 0 //JUCE_LINUX
#define JUCE_ALSA 1
#define JUCE_ALSA_MIDI_INPUT_NAME "Carla"
#define JUCE_ALSA_MIDI_OUTPUT_NAME "Carla"
#define JUCE_ALSA_MIDI_INPUT_PORT_NAME "Midi In"
#define JUCE_ALSA_MIDI_OUTPUT_PORT_NAME "Midi Out"
#else
#define JUCE_ALSA 0
#endif
/** Config: JUCE_JACK
Enables JACK audio devices (Linux only).
*/
#if 0 //JUCE_LINUX
#define JUCE_JACK 1
#define JUCE_JACK_CLIENT_NAME "Carla"
#else
#define JUCE_JACK 0
#endif
//=============================================================================
/** Config: JUCE_USE_CDREADER
Enables the AudioCDReader class (on supported platforms).
*/
#define JUCE_USE_CDREADER 0
/** Config: JUCE_USE_CDBURNER
Enables the AudioCDBurner class (on supported platforms).
*/
#define JUCE_USE_CDBURNER 0
#endif // CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED

+ 1
- 1
source/modules/juce_audio_devices/Makefile View File

@@ -10,7 +10,7 @@ include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(JUCE_AUDIO_DEVICES_FLAGS) -w
BUILD_CXX_FLAGS += $(JUCE_AUDIO_DEVICES_FLAGS) -I..

ifeq ($(WIN32),true)
# BUILD_CXX_FLAGS += -I$(CWD)/includes/asio


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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

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

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


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

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


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

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


+ 0
- 26
source/modules/juce_audio_formats.h View File

@@ -1,26 +0,0 @@
/*
* Carla Juce setup
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program 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.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED
#define CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED

#include "juce_audio_basics.h"

#include "juce_audio_formats/AppConfig.h"
#include "juce_audio_formats/juce_audio_formats.h"

#endif // CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED

+ 0
- 53
source/modules/juce_audio_formats/AppConfig.h View File

@@ -1,53 +0,0 @@
/*
==============================================================================
Build options for juce_audio_formats static library
==============================================================================
*/
#ifndef CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED
#define CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED
#include "../juce_audio_basics/AppConfig.h"
//=============================================================================
/** Config: JUCE_USE_FLAC
Enables the FLAC audio codec classes (available on all platforms).
If your app doesn't need to read FLAC files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_FLAC 1
/** Config: JUCE_USE_OGGVORBIS
Enables the Ogg-Vorbis audio codec classes (available on all platforms).
If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_OGGVORBIS 1
/** Config: JUCE_USE_MP3AUDIOFORMAT
Enables the software-based MP3AudioFormat class.
IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile
this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing
that Raw Material Software is in no way responsible for any patent, copyright, or other
legal issues that you may suffer as a result.
The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party
intellectual property. If you wish to use it, please seek your own independent advice about the
legality of doing so. If you are not willing to accept full responsibility for the consequences
of using this code, then do not enable this setting.
*/
#define JUCE_USE_MP3AUDIOFORMAT 0
/** Config: JUCE_USE_LAME_AUDIO_FORMAT
Enables the LameEncoderAudioFormat class.
*/
#define JUCE_USE_LAME_AUDIO_FORMAT 1
/** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT
Enables the Windows Media SDK codecs.
*/
#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0
#endif // CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED

+ 1
- 1
source/modules/juce_audio_formats/Makefile View File

@@ -10,7 +10,7 @@ include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(JUCE_AUDIO_FORMATS_FLAGS) -w
BUILD_CXX_FLAGS += $(JUCE_AUDIO_FORMATS_FLAGS) -I..

# ----------------------------------------------------------------------------------------------------------------------------



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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


+ 0
- 28
source/modules/juce_audio_processors.h View File

@@ -1,28 +0,0 @@
/*
* Carla Juce setup
* Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program 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.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/

#ifndef CARLA_JUCE_AUDIO_PROCESSORS_H_INCLUDED
#define CARLA_JUCE_AUDIO_PROCESSORS_H_INCLUDED

#include "juce_gui_basics.h"
#include "juce_audio_basics.h"

// patched to not use gui on non-win/mac
#include "juce_audio_processors/AppConfig.h"
#include "juce_audio_processors/juce_audio_processors.h"

#endif // CARLA_JUCE_AUDIO_PROCESSORS_H_INCLUDED

+ 0
- 58
source/modules/juce_audio_processors/AppConfig.h View File

@@ -1,58 +0,0 @@
/*
==============================================================================
Build options for juce_audio_processors static library
==============================================================================
*/
#ifndef CARLA_JUCE_AUDIO_PROCESSORS_APPCONFIG_H_INCLUDED
#define CARLA_JUCE_AUDIO_PROCESSORS_APPCONFIG_H_INCLUDED
#include "../juce_gui_basics/AppConfig.h"
#include "../juce_audio_basics/AppConfig.h"
//=============================================================================
/** Config: JUCE_PLUGINHOST_VST
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be
installed on your machine.
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU
*/
#ifndef VESTIGE_HEADER
# define JUCE_PLUGINHOST_VST 1
#else
# define JUCE_PLUGINHOST_VST 0
#endif
/** Config: JUCE_PLUGINHOST_VST3
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be
installed on your machine.
@see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU
*/
#if JUCE_WINDOWS || JUCE_MAC
# define JUCE_PLUGINHOST_VST3 1
#else
# define JUCE_PLUGINHOST_VST3 0
#endif
/** Config: JUCE_PLUGINHOST_AU
Enables the AudioUnit plugin hosting classes. This is Mac-only, of course.
@see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST
*/
#if JUCE_MAC
# define JUCE_PLUGINHOST_AU 1
#else
# define JUCE_PLUGINHOST_AU 0
#endif
#define JUCE_PLUGINHOST_LADSPA 0
// Fix MinGW VST3 build
#if JUCE_WINDOWS
# define _set_abort_behavior(...)
#endif
#endif // CARLA_JUCE_AUDIO_PROCESSORS_APPCONFIG_H_INCLUDED

+ 2
- 1
source/modules/juce_audio_processors/Makefile View File

@@ -10,12 +10,13 @@ include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(JUCE_AUDIO_PROCESSORS_FLAGS) -I$(CWD)/includes/ladspa -I$(CWD)/includes/vst2 -I$(CWD)/includes/vst3 -w
BUILD_CXX_FLAGS += $(JUCE_AUDIO_PROCESSORS_FLAGS) -I$(CWD)/includes/ladspa -I$(CWD)/includes/vst2 -I$(CWD)/includes/vst3 -I..

ifeq ($(CARLA_VESTIGE_HEADER),true)
BUILD_CXX_FLAGS += -DVESTIGE_HEADER
else
# needed by vst3
BUILD_CXX_FLAGS += -w
ifeq ($(DEBUG),true)
BUILD_CXX_FLAGS += -DDEVELOPMENT -D_DEBUG
else


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

@@ -33,19 +33,19 @@ void AudioPluginFormatManager::addDefaultFormats()
for (int i = formats.size(); --i >= 0;)
{
#if JUCE_PLUGINHOST_VST
jassert (dynamic_cast <VSTPluginFormat*> (formats[i]) == nullptr);
jassert (dynamic_cast<VSTPluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_VST3
jassert (dynamic_cast <VST3PluginFormat*> (formats[i]) == nullptr);
jassert (dynamic_cast<VST3PluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_AU && JUCE_MAC
jassert (dynamic_cast <AudioUnitPluginFormat*> (formats[i]) == nullptr);
jassert (dynamic_cast<AudioUnitPluginFormat*> (formats[i]) == nullptr);
#endif
#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX
jassert (dynamic_cast <LADSPAPluginFormat*> (formats[i]) == nullptr);
jassert (dynamic_cast<LADSPAPluginFormat*> (formats[i]) == nullptr);
#endif
}
#endif


+ 63
- 60
source/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm View File

@@ -209,7 +209,7 @@ namespace AudioUnitFormatHelpers
if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID())
manufacturer = String::fromCFString ((CFStringRef) manuString);
const short resFileId = CFBundleOpenBundleResourceMap (bundleRef);
const ResFileRefNum resFileId = CFBundleOpenBundleResourceMap (bundleRef);
UseResFile (resFileId);
const OSType thngType = stringToOSType ("thng");
@@ -379,8 +379,8 @@ public:
desc.category = AudioUnitFormatHelpers::getCategory (componentDesc.componentType);
desc.manufacturerName = manufacturer;
desc.version = version;
desc.numInputChannels = getNumInputChannels();
desc.numOutputChannels = getNumOutputChannels();
desc.numInputChannels = getTotalNumInputChannels();
desc.numOutputChannels = getTotalNumOutputChannels();
desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice);
}
@@ -540,7 +540,7 @@ public:
for (AudioUnitElement j = 0; j < numOutputBusChannels; ++j)
{
abl->mBuffers[j].mNumberChannels = 1;
abl->mBuffers[j].mDataByteSize = sizeof (float) * (size_t) numSamples;
abl->mBuffers[j].mDataByteSize = (UInt32) (sizeof (float) * (size_t) numSamples);
abl->mBuffers[j].mData = buffer.getWritePointer ((int) (i * numOutputBusChannels + j));
}
}
@@ -578,7 +578,7 @@ public:
else
{
// Plugin not working correctly, so just bypass..
for (int i = 0; i < getNumOutputChannels(); ++i)
for (int i = getTotalNumOutputChannels(); --i >= 0;)
buffer.clear (i, 0, buffer.getNumSamples());
}
@@ -597,7 +597,7 @@ public:
//==============================================================================
const String getInputChannelName (int index) const override
{
if (isPositiveAndBelow (index, getNumInputChannels()))
if (isPositiveAndBelow (index, getTotalNumInputChannels()))
return "Input " + String (index + 1);
return String();
@@ -605,14 +605,14 @@ public:
const String getOutputChannelName (int index) const override
{
if (isPositiveAndBelow (index, getNumOutputChannels()))
if (isPositiveAndBelow (index, getTotalNumOutputChannels()))
return "Output " + String (index + 1);
return String();
}
bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumInputChannels()); }
bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumOutputChannels()); }
bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getTotalNumInputChannels()); }
bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getTotalNumOutputChannels()); }
//==============================================================================
int getNumParameters() override { return parameters.size(); }
@@ -841,9 +841,9 @@ public:
if (audioUnit != nullptr)
{
UInt32 dummy = 0, paramListSize = 0;
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
0, &dummy, &paramListSize);
UInt32 paramListSize = 0;
AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
0, &paramListSize, nullptr);
if (paramListSize > 0)
{
@@ -855,7 +855,7 @@ public:
AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global,
0, ids, &paramListSize);
for (int i = 0; i < numParams; ++i)
for (size_t i = 0; i < numParams; ++i)
{
AudioUnitParameterInfo info;
UInt32 sz = sizeof (info);
@@ -909,7 +909,7 @@ private:
CriticalSection lock;
bool wantsMidiMessages, producesMidiMessages, wasPlaying, prepared;
HeapBlock <AudioBufferList> outputBufferList;
HeapBlock<AudioBufferList> outputBufferList;
AudioTimeStamp timeStamp;
AudioSampleBuffer* currentBuffer;
AudioUnitElement numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses;
@@ -972,12 +972,7 @@ private:
kAudioUnitScope_Global, 0, &info, sizeof (info));
}
AUEventListenerCreate (eventListenerCallback, this,
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4
CFRunLoopGetMain(),
#else
nullptr,
#endif
AUEventListenerCreate (eventListenerCallback, this, CFRunLoopGetMain(),
kCFRunLoopDefaultMode, 0, 0, &eventListenerRef);
for (int i = 0; i < parameters.size(); ++i)
@@ -1007,6 +1002,10 @@ private:
event.mEventType = kAudioUnitEvent_PropertyChange;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
// Add a listener for parameter list changes
event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_ParameterList;
AUEventListenerAddEventType (eventListenerRef, nullptr, &event);
}
}
@@ -1037,7 +1036,11 @@ private:
break;
default:
sendAllParametersChangedEvents();
if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_ParameterList)
updateHostDisplay();
else if (event.mArgument.mProperty.mPropertyID == kAudioUnitProperty_PresentPreset)
sendAllParametersChangedEvents();
break;
}
}
@@ -1127,24 +1130,24 @@ private:
OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator,
UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const
{
AudioPlayHead* const ph = getPlayHead();
AudioPlayHead::CurrentPositionInfo result;
if (ph != nullptr && ph->getCurrentPosition (result))
if (AudioPlayHead* const ph = getPlayHead())
{
setIfNotNull (outTimeSig_Numerator, (UInt32) result.timeSigNumerator);
setIfNotNull (outTimeSig_Denominator, (UInt32) result.timeSigDenominator);
setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); //xxx
setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong
}
else
{
setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0);
setIfNotNull (outTimeSig_Numerator, (UInt32) 4);
setIfNotNull (outTimeSig_Denominator, (UInt32) 4);
setIfNotNull (outCurrentMeasureDownBeat, 0);
AudioPlayHead::CurrentPositionInfo result;
if (ph->getCurrentPosition (result))
{
setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0); //xxx
setIfNotNull (outTimeSig_Numerator, (UInt32) result.timeSigNumerator);
setIfNotNull (outTimeSig_Denominator, (UInt32) result.timeSigDenominator);
setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong
return noErr;
}
}
setIfNotNull (outDeltaSampleOffsetToNextBeat, (UInt32) 0);
setIfNotNull (outTimeSig_Numerator, (UInt32) 4);
setIfNotNull (outTimeSig_Denominator, (UInt32) 4);
setIfNotNull (outCurrentMeasureDownBeat, 0);
return noErr;
}
@@ -1152,34 +1155,34 @@ private:
Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling,
Float64* outCycleStartBeat, Float64* outCycleEndBeat)
{
AudioPlayHead* const ph = getPlayHead();
AudioPlayHead::CurrentPositionInfo result;
if (ph != nullptr && ph->getCurrentPosition (result))
if (AudioPlayHead* const ph = getPlayHead())
{
setIfNotNull (outIsPlaying, result.isPlaying);
AudioPlayHead::CurrentPositionInfo result;
if (outTransportStateChanged != nullptr)
if (ph->getCurrentPosition (result))
{
*outTransportStateChanged = result.isPlaying != wasPlaying;
wasPlaying = result.isPlaying;
}
setIfNotNull (outIsPlaying, result.isPlaying);
setIfNotNull (outCurrentSampleInTimeLine, result.timeInSamples);
setIfNotNull (outIsCycling, false);
setIfNotNull (outCycleStartBeat, 0);
setIfNotNull (outCycleEndBeat, 0);
}
else
{
setIfNotNull (outIsPlaying, false);
setIfNotNull (outTransportStateChanged, false);
setIfNotNull (outCurrentSampleInTimeLine, 0);
setIfNotNull (outIsCycling, false);
setIfNotNull (outCycleStartBeat, 0);
setIfNotNull (outCycleEndBeat, 0);
if (outTransportStateChanged != nullptr)
{
*outTransportStateChanged = result.isPlaying != wasPlaying;
wasPlaying = result.isPlaying;
}
setIfNotNull (outCurrentSampleInTimeLine, result.timeInSamples);
setIfNotNull (outIsCycling, result.isLooping);
setIfNotNull (outCycleStartBeat, result.ppqLoopStart);
setIfNotNull (outCycleEndBeat, result.ppqLoopEnd);
return noErr;
}
}
setIfNotNull (outIsPlaying, false);
setIfNotNull (outTransportStateChanged, false);
setIfNotNull (outCurrentSampleInTimeLine, 0);
setIfNotNull (outIsCycling, false);
setIfNotNull (outCycleStartBeat, 0.0);
setIfNotNull (outCycleEndBeat, 0.0);
return noErr;
}
@@ -1193,7 +1196,7 @@ private:
}
static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp*, UInt32 /*midiOutNum*/,
const struct MIDIPacketList* pktlist)
const MIDIPacketList* pktlist)
{
return static_cast<AudioUnitPluginInstance*> (hostRef)->renderMidiOutput (pktlist);
}
@@ -1394,7 +1397,7 @@ private:
&& AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,
0, &dataSize, &isWritable) == noErr)
{
HeapBlock <AudioUnitCocoaViewInfo> info;
HeapBlock<AudioUnitCocoaViewInfo> info;
info.calloc (dataSize, 1);
if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global,


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

Loading…
Cancel
Save