diff --git a/Makefile b/Makefile index 6b7036f8c..05b848121 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,6 @@ ALL_LIBS += $(MODULEDIR)/carla_engine_plugin.a ALL_LIBS += $(MODULEDIR)/carla_plugin.a ALL_LIBS += $(MODULEDIR)/jackbridge.a ALL_LIBS += $(MODULEDIR)/native-plugins.a -ALL_LIBS += $(MODULEDIR)/juce_audio_basics.a ALL_LIBS += $(MODULEDIR)/juce_core.a ALL_LIBS += $(MODULEDIR)/lilv.a ALL_LIBS += $(MODULEDIR)/rtmempool.a @@ -150,7 +149,6 @@ theme: libs # Binaries (posix32) LIBS_POSIX32 = $(MODULEDIR)/jackbridge.posix32.a -LIBS_POSIX32 += $(MODULEDIR)/juce_audio_basics.posix32.a LIBS_POSIX32 += $(MODULEDIR)/juce_core.posix32.a LIBS_POSIX32 += $(MODULEDIR)/lilv.posix32.a LIBS_POSIX32 += $(MODULEDIR)/rtmempool.posix32.a @@ -163,7 +161,6 @@ posix32: $(LIBS_POSIX32) # Binaries (posix64) LIBS_POSIX64 = $(MODULEDIR)/jackbridge.posix64.a -LIBS_POSIX64 += $(MODULEDIR)/juce_audio_basics.posix64.a LIBS_POSIX64 += $(MODULEDIR)/juce_core.posix64.a LIBS_POSIX64 += $(MODULEDIR)/lilv.posix64.a LIBS_POSIX64 += $(MODULEDIR)/rtmempool.posix64.a @@ -180,7 +177,6 @@ LIBS_WIN32 = $(MODULEDIR)/jackbridge.win32.a else LIBS_WIN32 = $(MODULEDIR)/jackbridge.win32e.a endif -LIBS_WIN32 += $(MODULEDIR)/juce_audio_basics.win32.a LIBS_WIN32 += $(MODULEDIR)/juce_core.win32.a LIBS_WIN32 += $(MODULEDIR)/lilv.win32.a LIBS_WIN32 += $(MODULEDIR)/rtmempool.win32.a @@ -197,7 +193,6 @@ LIBS_WIN64 = $(MODULEDIR)/jackbridge.win64.a else LIBS_WIN64 = $(MODULEDIR)/jackbridge.win64e.a endif -LIBS_WIN64 += $(MODULEDIR)/juce_audio_basics.win64.a LIBS_WIN64 += $(MODULEDIR)/juce_core.win64.a LIBS_WIN64 += $(MODULEDIR)/lilv.win64.a LIBS_WIN64 += $(MODULEDIR)/rtmempool.win64.a diff --git a/resources/48x48/juce.png b/resources/48x48/juce.png deleted file mode 100644 index 365d3d542..000000000 Binary files a/resources/48x48/juce.png and /dev/null differ diff --git a/resources/resources.qrc b/resources/resources.qrc index 606b0f5a5..852862e75 100644 --- a/resources/resources.qrc +++ b/resources/resources.qrc @@ -39,7 +39,6 @@ 48x48/canvas.png 48x48/jack.png - 48x48/juce.png 48x48/folder.png 48x48/warning.png 48x48/wine.png diff --git a/source/Makefile.mk b/source/Makefile.mk index 1f2b8f968..370b8e0a0 100644 --- a/source/Makefile.mk +++ b/source/Makefile.mk @@ -401,7 +401,6 @@ ifeq ($(MACOS),true) DGL_LIBS = -framework OpenGL -framework Cocoa HYLIA_FLAGS = -DLINK_PLATFORM_MACOSX=1 JACKBRIDGE_LIBS = -ldl -lpthread -JUCE_AUDIO_BASICS_LIBS = -framework Accelerate JUCE_CORE_LIBS = -framework AppKit LILV_LIBS = -ldl -lm RTAUDIO_FLAGS += -D__MACOSX_CORE__ diff --git a/source/backend/Makefile b/source/backend/Makefile index f441d334e..806ca4c79 100644 --- a/source/backend/Makefile +++ b/source/backend/Makefile @@ -26,7 +26,6 @@ STANDALONE_LIBS = $(MODULEDIR)/carla_engine.a STANDALONE_LIBS += $(MODULEDIR)/carla_plugin.a STANDALONE_LIBS += $(MODULEDIR)/jackbridge.a -STANDALONE_LIBS += $(MODULEDIR)/juce_audio_basics.a STANDALONE_LIBS += $(MODULEDIR)/juce_core.a STANDALONE_LIBS += $(MODULEDIR)/lilv.a STANDALONE_LIBS += $(MODULEDIR)/native-plugins.a @@ -43,14 +42,12 @@ endif STANDALONE_LIBS += $(MODULEDIR)/rtaudio.a STANDALONE_LIBS += $(MODULEDIR)/rtmidi.a -UTILS_LIBS = $(MODULEDIR)/juce_audio_basics.a UTILS_LIBS += $(MODULEDIR)/juce_core.a UTILS_LIBS += $(MODULEDIR)/lilv.a # ---------------------------------------------------------------------------------------------------------------------------- STANDALONE_LINK_FLAGS = $(JACKBRIDGE_LIBS) -STANDALONE_LINK_FLAGS += $(JUCE_AUDIO_BASICS_LIBS) STANDALONE_LINK_FLAGS += $(JUCE_CORE_LIBS) STANDALONE_LINK_FLAGS += $(LILV_LIBS) STANDALONE_LINK_FLAGS += $(NATIVE_PLUGINS_LIBS) @@ -79,7 +76,6 @@ ifeq ($(HAVE_X11),true) STANDALONE_LINK_FLAGS += $(X11_LIBS) endif -UTILS_LINK_FLAGS = $(JUCE_AUDIO_BASICS_LIBS) UTILS_LINK_FLAGS += $(JUCE_CORE_LIBS) UTILS_LINK_FLAGS += $(LILV_LIBS) diff --git a/source/backend/engine/CarlaEngineGraph.cpp b/source/backend/engine/CarlaEngineGraph.cpp index 0753e4e5d..61923279f 100644 --- a/source/backend/engine/CarlaEngineGraph.cpp +++ b/source/backend/engine/CarlaEngineGraph.cpp @@ -883,8 +883,6 @@ void RackGraph::processHelper(CarlaEngine::ProtectedData* const data, const floa { CARLA_SAFE_ASSERT_RETURN(audioBuffers.outBuf[1] != nullptr,); - const int iframes(static_cast(frames)); - const CarlaRecursiveMutexLocker _cml(audioBuffers.mutex); if (inBuf != nullptr && inputs > 0) @@ -910,7 +908,7 @@ void RackGraph::processHelper(CarlaEngine::ProtectedData* const data, const floa } if (noConnections) - carla_zeroFloats(audioBuffers.inBuf[0], iframes); + carla_zeroFloats(audioBuffers.inBuf[0], frames); noConnections = true; diff --git a/source/backend/plugin/CarlaPluginBridge.cpp b/source/backend/plugin/CarlaPluginBridge.cpp index f83d9de8c..c78c7e70e 100644 --- a/source/backend/plugin/CarlaPluginBridge.cpp +++ b/source/backend/plugin/CarlaPluginBridge.cpp @@ -1035,9 +1035,9 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); for (uint32_t i=0; i < pData->cvOut.count; ++i) - FloatVectorOperations::clear(cvOut[i], static_cast(frames)); + carla_zeroFloats(cvOut[i], frames); return; } @@ -1344,8 +1344,6 @@ public: CARLA_SAFE_ASSERT_RETURN(cvOut != nullptr, false); } - const int iframes(static_cast(frames)); - // -------------------------------------------------------------------------------------------------------- // Try lock, silence otherwise @@ -1356,9 +1354,9 @@ public: else if (! pData->singleMutex.tryLock()) { for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], iframes); + carla_zeroFloats(audioOut[i], frames); for (uint32_t i=0; i < pData->cvOut.count; ++i) - FloatVectorOperations::clear(cvOut[i], iframes); + carla_zeroFloats(cvOut[i], frames); return false; } @@ -1366,7 +1364,7 @@ public: // Reset audio buffers for (uint32_t i=0; i < fInfo.aIns; ++i) - FloatVectorOperations::copy(fShmAudioPool.data + (i * frames), audioIn[i], iframes); + carla_copyFloats(fShmAudioPool.data + (i * frames), audioIn[i], frames); // -------------------------------------------------------------------------------------------------------- // TimeInfo @@ -1410,7 +1408,7 @@ public: } for (uint32_t i=0; i < fInfo.aOuts; ++i) - FloatVectorOperations::copy(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), iframes); + carla_copyFloats(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), frames); #ifndef BUILD_BRIDGE // -------------------------------------------------------------------------------------------------------- @@ -1453,7 +1451,7 @@ public: if (isPair) { CARLA_ASSERT(i+1 < pData->audioOut.count); - FloatVectorOperations::copy(oldBufLeft, audioOut[i], iframes); + carla_copyFloats(oldBufLeft, audioOut[i], frames); } float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; @@ -1494,7 +1492,7 @@ public: if (latframes <= frames) { for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(pData->latency.buffers[i], audioIn[i]+(frames-latframes), static_cast(latframes)); + carla_copyFloats(pData->latency.buffers[i], audioIn[i]+(frames-latframes), latframes); } else { diff --git a/source/backend/plugin/CarlaPluginDSSI.cpp b/source/backend/plugin/CarlaPluginDSSI.cpp index bd08d3aeb..6e66f0097 100644 --- a/source/backend/plugin/CarlaPluginDSSI.cpp +++ b/source/backend/plugin/CarlaPluginDSSI.cpp @@ -777,7 +777,7 @@ public: pData->param.createNew(params, true); fParamBuffers = new float[params]; - FloatVectorOperations::clear(fParamBuffers, static_cast(params)); + carla_zeroFloats(fParamBuffers, params); } const uint portNameSize(pData->engine->getMaxPortNameSize()); @@ -1294,7 +1294,7 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); return; } @@ -1764,8 +1764,6 @@ public: return false; } - const int iframes(static_cast(frames)); - // -------------------------------------------------------------------------------------------------------- // Set audio buffers @@ -1775,11 +1773,11 @@ public: if (! customMonoOut) { for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(fAudioOutBuffers[i], iframes); + carla_zeroFloats(fAudioOutBuffers[i], frames); } for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(fAudioInBuffers[i], audioIn[i]+timeOffset, iframes); + carla_copyFloats(fAudioInBuffers[i], audioIn[i]+timeOffset, frames); // -------------------------------------------------------------------------------------------------------- // Run plugin @@ -1794,7 +1792,7 @@ public: // Mixdown for forced stereo if (customMonoOut) - FloatVectorOperations::clear(fAudioOutBuffers[instn], iframes); + carla_zeroFloats(fAudioOutBuffers[instn], frames); // ---------------------------------------------------------------------------------------------------- // Run it @@ -1816,15 +1814,15 @@ public: // Mixdown for forced stereo if (customMonoOut) - FloatVectorOperations::multiply(fAudioOutBuffers[instn], 0.5f, iframes); + carla_multiply(fAudioOutBuffers[instn], 0.5f, frames); else if (customStereoOut) - FloatVectorOperations::copy(fExtraStereoBuffer[instn], fAudioOutBuffers[instn], iframes); + carla_copyFloats(fExtraStereoBuffer[instn], fAudioOutBuffers[instn], frames); } if (customStereoOut) { - FloatVectorOperations::copy(fAudioOutBuffers[0], fExtraStereoBuffer[0], iframes); - FloatVectorOperations::copy(fAudioOutBuffers[1], fExtraStereoBuffer[1], iframes); + carla_copyFloats(fAudioOutBuffers[0], fExtraStereoBuffer[0], frames); + carla_copyFloats(fAudioOutBuffers[1], fExtraStereoBuffer[1], frames); } #ifndef BUILD_BRIDGE @@ -1867,7 +1865,7 @@ public: if (isPair) { CARLA_ASSERT(i+1 < pData->audioOut.count); - FloatVectorOperations::copy(oldBufLeft, fAudioOutBuffers[i], iframes); + carla_copyFloats(oldBufLeft, fAudioOutBuffers[i], frames); } float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; @@ -1909,7 +1907,7 @@ public: if (latframes <= frames) { for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(pData->latency.buffers[i], audioIn[i]+(frames-latframes), static_cast(latframes)); + carla_copyFloats(pData->latency.buffers[i], audioIn[i]+(frames-latframes), latframes); } else { @@ -1947,15 +1945,13 @@ public: CARLA_ASSERT_INT(newBufferSize > 0, newBufferSize); carla_debug("CarlaPluginDSSI::bufferSizeChanged(%i) - start", newBufferSize); - const int iBufferSize(static_cast(newBufferSize)); - for (uint32_t i=0; i < pData->audioIn.count; ++i) { if (fAudioInBuffers[i] != nullptr) delete[] fAudioInBuffers[i]; fAudioInBuffers[i] = new float[newBufferSize]; - FloatVectorOperations::clear(fAudioInBuffers[i], iBufferSize); + carla_zeroFloats(fAudioInBuffers[i], newBufferSize); } for (uint32_t i=0; i < pData->audioOut.count; ++i) @@ -1964,7 +1960,7 @@ public: delete[] fAudioOutBuffers[i]; fAudioOutBuffers[i] = new float[newBufferSize]; - FloatVectorOperations::clear(fAudioOutBuffers[i], iBufferSize); + carla_zeroFloats(fAudioOutBuffers[i], newBufferSize); } if (fExtraStereoBuffer[0] != nullptr) @@ -1983,8 +1979,8 @@ public: { fExtraStereoBuffer[0] = new float[newBufferSize]; fExtraStereoBuffer[1] = new float[newBufferSize]; - FloatVectorOperations::clear(fExtraStereoBuffer[0], iBufferSize); - FloatVectorOperations::clear(fExtraStereoBuffer[1], iBufferSize); + carla_zeroFloats(fExtraStereoBuffer[0], newBufferSize); + carla_zeroFloats(fExtraStereoBuffer[1], newBufferSize); } reconnectAudioPorts(); diff --git a/source/backend/plugin/CarlaPluginFluidSynth.cpp b/source/backend/plugin/CarlaPluginFluidSynth.cpp index 165a0cd35..2826c52b4 100644 --- a/source/backend/plugin/CarlaPluginFluidSynth.cpp +++ b/source/backend/plugin/CarlaPluginFluidSynth.cpp @@ -58,7 +58,7 @@ public: { carla_debug("CarlaPluginFluidSynth::CarlaPluginFluidSynth(%p, %i, %s)", engine, id, bool2str(use16Outs)); - FloatVectorOperations::clear(fParamBuffers, FluidSynthParametersMax); + carla_zeroFloats(fParamBuffers, FluidSynthParametersMax); carla_fill(fCurMidiProgs, 0, MAX_MIDI_CHANNELS); // create settings @@ -1013,7 +1013,7 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); return; } @@ -1405,13 +1405,13 @@ public: if (kUse16Outs) { for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(fAudio16Buffers[i], static_cast(frames)); + carla_zeroFloats(fAudio16Buffers[i], frames); // FIXME use '32' or '16' instead of outs - fluid_synth_process(fSynth, static_cast(frames), 0, nullptr, static_cast(pData->audioOut.count), fAudio16Buffers); + fluid_synth_process(fSynth, frames, 0, nullptr, static_cast(pData->audioOut.count), fAudio16Buffers); } else - fluid_synth_write_float(fSynth, static_cast(frames), outBuffer[0] + timeOffset, 0, 1, outBuffer[1] + timeOffset, 0, 1); + fluid_synth_write_float(fSynth, frames, outBuffer[0] + timeOffset, 0, 1, outBuffer[1] + timeOffset, 0, 1); #ifndef BUILD_BRIDGE // -------------------------------------------------------------------------------------------------------- @@ -1430,7 +1430,7 @@ public: if (doBalance) { if (i % 2 == 0) - FloatVectorOperations::copy(oldBufLeft, outBuffer[i]+timeOffset, static_cast(frames)); + carla_copyFloats(oldBufLeft, outBuffer[i]+timeOffset, frames); float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; float balRangeR = (pData->postProc.balanceRight + 1.0f)/2.0f; diff --git a/source/backend/plugin/CarlaPluginInternal.cpp b/source/backend/plugin/CarlaPluginInternal.cpp index 14af7148e..35f880beb 100644 --- a/source/backend/plugin/CarlaPluginInternal.cpp +++ b/source/backend/plugin/CarlaPluginInternal.cpp @@ -1,4 +1,4 @@ -/* +/* * Carla Plugin * Copyright (C) 2011-2014 Filipe Coelho * @@ -461,18 +461,18 @@ void CarlaPlugin::ProtectedData::Latency::recreateBuffers(const uint32_t newChan if (oldFrames > frames) { const uint32_t diff = oldFrames - frames; - FloatVectorOperations::copy(buffers[i], oldBuffers[i] + diff, static_cast(frames)); + carla_copyFloats(buffers[i], oldBuffers[i] + diff, frames); } else { const uint32_t diff = frames - oldFrames; - FloatVectorOperations::clear(buffers[i], static_cast(diff)); - FloatVectorOperations::copy(buffers[i] + diff, oldBuffers[i], static_cast(oldFrames)); + carla_zeroFloats(buffers[i], diff); + carla_copyFloats(buffers[i] + diff, oldBuffers[i], static_cast(oldFrames)); } } else { - FloatVectorOperations::clear(buffers[i], static_cast(frames)); + carla_zeroFloats(buffers[i], frames); } } } diff --git a/source/backend/plugin/CarlaPluginInternal.hpp b/source/backend/plugin/CarlaPluginInternal.hpp index 68998647a..8a9a14169 100644 --- a/source/backend/plugin/CarlaPluginInternal.hpp +++ b/source/backend/plugin/CarlaPluginInternal.hpp @@ -28,10 +28,6 @@ #include "CarlaString.hpp" #include "RtLinkedList.hpp" -#include "juce_audio_basics/juce_audio_basics.h" - -using juce::FloatVectorOperations; - CARLA_BACKEND_START_NAMESPACE // ----------------------------------------------------------------------- diff --git a/source/backend/plugin/CarlaPluginJack.cpp b/source/backend/plugin/CarlaPluginJack.cpp index 8a24ac407..d8affed08 100644 --- a/source/backend/plugin/CarlaPluginJack.cpp +++ b/source/backend/plugin/CarlaPluginJack.cpp @@ -611,7 +611,7 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); return; } @@ -888,8 +888,6 @@ public: CARLA_SAFE_ASSERT_RETURN(audioOut != nullptr, false); } - const int iframes(static_cast(frames)); - // -------------------------------------------------------------------------------------------------------- // Try lock, silence otherwise @@ -900,7 +898,7 @@ public: else if (! pData->singleMutex.tryLock()) { for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], iframes); + carla_zeroFloats(audioOut[i], frames); return false; } @@ -908,7 +906,7 @@ public: // Reset audio buffers for (uint32_t i=0; i < fInfo.aIns; ++i) - FloatVectorOperations::copy(fShmAudioPool.data + (i * frames), audioIn[i], iframes); + carla_copyFloats(fShmAudioPool.data + (i * frames), audioIn[i], frames); // -------------------------------------------------------------------------------------------------------- // TimeInfo @@ -958,7 +956,7 @@ public: } for (uint32_t i=0; i < fInfo.aOuts; ++i) - FloatVectorOperations::copy(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), iframes); + carla_copyFloats(audioOut[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), frames); #ifndef BUILD_BRIDGE // -------------------------------------------------------------------------------------------------------- @@ -995,7 +993,7 @@ public: if (isPair) { CARLA_ASSERT(i+1 < pData->audioOut.count); - FloatVectorOperations::copy(oldBufLeft, audioOut[i], iframes); + carla_copyFloats(oldBufLeft, audioOut[i], frames); } float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; diff --git a/source/backend/plugin/CarlaPluginLADSPA.cpp b/source/backend/plugin/CarlaPluginLADSPA.cpp index ec5e5f218..f4503d3c7 100644 --- a/source/backend/plugin/CarlaPluginLADSPA.cpp +++ b/source/backend/plugin/CarlaPluginLADSPA.cpp @@ -1,4 +1,4 @@ -/* +/* * Carla Plugin, LADSPA implementation * Copyright (C) 2011-2017 Filipe Coelho * @@ -494,7 +494,7 @@ public: pData->param.createNew(params, true); fParamBuffers = new float[params]; - FloatVectorOperations::clear(fParamBuffers, static_cast(params)); + carla_zeroFloats(fParamBuffers, params); } const uint portNameSize(pData->engine->getMaxPortNameSize()); @@ -902,7 +902,7 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); return; } @@ -1129,8 +1129,6 @@ public: return false; } - const int iframes(static_cast(frames)); - // -------------------------------------------------------------------------------------------------------- // Set audio buffers @@ -1140,11 +1138,11 @@ public: if (! customMonoOut) { for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(fAudioOutBuffers[i], iframes); + carla_zeroFloats(fAudioOutBuffers[i], frames); } for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(fAudioInBuffers[i], audioIn[i]+timeOffset, iframes); + carla_copyFloats(fAudioInBuffers[i], audioIn[i]+timeOffset, frames); // -------------------------------------------------------------------------------------------------------- // Run plugin @@ -1159,7 +1157,7 @@ public: // Mixdown for forced stereo if (customMonoOut) - FloatVectorOperations::clear(fAudioOutBuffers[instn], iframes); + carla_zeroFloats(fAudioOutBuffers[instn], frames); // ---------------------------------------------------------------------------------------------------- // Run it @@ -1172,15 +1170,15 @@ public: // Mixdown for forced stereo if (customMonoOut) - FloatVectorOperations::multiply(fAudioOutBuffers[instn], 0.5f, iframes); + carla_multiply(fAudioOutBuffers[instn], 0.5f, frames); else if (customStereoOut) - FloatVectorOperations::copy(fExtraStereoBuffer[instn], fAudioOutBuffers[instn], iframes); + carla_copyFloats(fExtraStereoBuffer[instn], fAudioOutBuffers[instn], frames); } if (customStereoOut) { - FloatVectorOperations::copy(fAudioOutBuffers[0], fExtraStereoBuffer[0], iframes); - FloatVectorOperations::copy(fAudioOutBuffers[1], fExtraStereoBuffer[1], iframes); + carla_copyFloats(fAudioOutBuffers[0], fExtraStereoBuffer[0], frames); + carla_copyFloats(fAudioOutBuffers[1], fExtraStereoBuffer[1], frames); } #ifndef BUILD_BRIDGE @@ -1223,7 +1221,7 @@ public: if (isPair) { CARLA_ASSERT(i+1 < pData->audioOut.count); - FloatVectorOperations::copy(oldBufLeft, fAudioOutBuffers[i], iframes); + carla_copyFloats(oldBufLeft, fAudioOutBuffers[i], frames); } float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; @@ -1265,7 +1263,7 @@ public: if (latframes <= frames) { for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(pData->latency.buffers[i], audioIn[i]+(frames-latframes), static_cast(latframes)); + carla_copyFloats(pData->latency.buffers[i], audioIn[i]+(frames-latframes), static_cast(latframes)); } else { @@ -1303,15 +1301,13 @@ public: CARLA_ASSERT_INT(newBufferSize > 0, newBufferSize); carla_debug("CarlaPluginLADSPA::bufferSizeChanged(%i) - start", newBufferSize); - const int iBufferSize(static_cast(newBufferSize)); - for (uint32_t i=0; i < pData->audioIn.count; ++i) { if (fAudioInBuffers[i] != nullptr) delete[] fAudioInBuffers[i]; fAudioInBuffers[i] = new float[newBufferSize]; - FloatVectorOperations::clear(fAudioInBuffers[i], iBufferSize); + carla_zeroFloats(fAudioInBuffers[i], newBufferSize); } for (uint32_t i=0; i < pData->audioOut.count; ++i) @@ -1320,7 +1316,7 @@ public: delete[] fAudioOutBuffers[i]; fAudioOutBuffers[i] = new float[newBufferSize]; - FloatVectorOperations::clear(fAudioOutBuffers[i], iBufferSize); + carla_zeroFloats(fAudioOutBuffers[i], newBufferSize); } if (fExtraStereoBuffer[0] != nullptr) @@ -1339,8 +1335,8 @@ public: { fExtraStereoBuffer[0] = new float[newBufferSize]; fExtraStereoBuffer[1] = new float[newBufferSize]; - FloatVectorOperations::clear(fExtraStereoBuffer[0], iBufferSize); - FloatVectorOperations::clear(fExtraStereoBuffer[1], iBufferSize); + carla_zeroFloats(fExtraStereoBuffer[0], newBufferSize); + carla_zeroFloats(fExtraStereoBuffer[1], newBufferSize); } reconnectAudioPorts(); diff --git a/source/backend/plugin/CarlaPluginLV2.cpp b/source/backend/plugin/CarlaPluginLV2.cpp index 38cc1681b..dc6da0108 100644 --- a/source/backend/plugin/CarlaPluginLV2.cpp +++ b/source/backend/plugin/CarlaPluginLV2.cpp @@ -1736,7 +1736,7 @@ public: { pData->param.createNew(params, true); fParamBuffers = new float[params]; - FloatVectorOperations::clear(fParamBuffers, static_cast(params)); + carla_zeroFloats(fParamBuffers, params); } if (const uint32_t count = static_cast(evIns.count())) @@ -2655,9 +2655,9 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); for (uint32_t i=0; i < pData->cvOut.count; ++i) - FloatVectorOperations::clear(cvOut[i], static_cast(frames)); + carla_zeroFloats(cvOut[i], frames); return; } @@ -3574,25 +3574,23 @@ public: return false; } - const int iframes(static_cast(frames)); - // -------------------------------------------------------------------------------------------------------- // Set audio buffers for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(fAudioInBuffers[i], audioIn[i]+timeOffset, iframes); + carla_copyFloats(fAudioInBuffers[i], audioIn[i]+timeOffset, frames); for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(fAudioOutBuffers[i], iframes); + carla_zeroFloats(fAudioOutBuffers[i], frames); // -------------------------------------------------------------------------------------------------------- // Set CV buffers for (uint32_t i=0; i < pData->cvIn.count; ++i) - FloatVectorOperations::copy(fCvInBuffers[i], cvIn[i]+timeOffset, iframes); + carla_copyFloats(fCvInBuffers[i], cvIn[i]+timeOffset, frames); for (uint32_t i=0; i < pData->cvOut.count; ++i) - FloatVectorOperations::clear(fCvOutBuffers[i], iframes); + carla_zeroFloats(fCvOutBuffers[i], frames); // -------------------------------------------------------------------------------------------------------- // Run plugin @@ -3662,7 +3660,7 @@ public: if (isPair) { CARLA_ASSERT(i+1 < pData->audioOut.count); - FloatVectorOperations::copy(oldBufLeft, fAudioOutBuffers[i], iframes); + carla_copyFloats(oldBufLeft, fAudioOutBuffers[i], frames); } float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; @@ -3703,7 +3701,7 @@ public: if (latframes <= frames) { for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(pData->latency.buffers[i], audioIn[i]+(frames-latframes), static_cast(latframes)); + carla_copyFloats(pData->latency.buffers[i], audioIn[i]+(frames-latframes), latframes); } else { diff --git a/source/backend/plugin/CarlaPluginLinuxSampler.cpp b/source/backend/plugin/CarlaPluginLinuxSampler.cpp index 9f2e16373..09a9e0f45 100644 --- a/source/backend/plugin/CarlaPluginLinuxSampler.cpp +++ b/source/backend/plugin/CarlaPluginLinuxSampler.cpp @@ -1,4 +1,4 @@ -/* +/* * Carla LinuxSampler Plugin * Copyright (C) 2011-2014 Filipe Coelho * @@ -799,7 +799,7 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); fParamBuffers[LinuxSamplerDiskStreamCount] = 0.0f; fParamBuffers[LinuxSamplerVoiceCount] = 0.0f; @@ -1128,7 +1128,7 @@ public: if (doBalance) { if (i % 2 == 0) - FloatVectorOperations::copy(oldBufLeft, outBuffer[i], static_cast(frames)); + carla_copyFloats(oldBufLeft, outBuffer[i], frames); float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; float balRangeR = (pData->postProc.balanceRight + 1.0f)/2.0f; diff --git a/source/backend/plugin/CarlaPluginNative.cpp b/source/backend/plugin/CarlaPluginNative.cpp index 65cf70d1e..7159ed98e 100644 --- a/source/backend/plugin/CarlaPluginNative.cpp +++ b/source/backend/plugin/CarlaPluginNative.cpp @@ -1,4 +1,4 @@ -/* +/* * Carla Native Plugin * Copyright (C) 2012-2014 Filipe Coelho * @@ -1327,9 +1327,9 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); for (uint32_t i=0; i < pData->cvOut.count; ++i) - FloatVectorOperations::clear(cvOut[i], static_cast(frames)); + carla_zeroFloats(cvOut[i], frames); return; } @@ -1860,20 +1860,20 @@ public: // Set audio buffers for (uint32_t i=0; i < pData->audioIn.count; ++i) - FloatVectorOperations::copy(fAudioInBuffers[i], audioIn[i]+timeOffset, static_cast(frames)); + carla_copyFloats(fAudioInBuffers[i], audioIn[i]+timeOffset, frames); for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(fAudioOutBuffers[i], static_cast(frames)); + carla_zeroFloats(fAudioOutBuffers[i], frames); #if 0 // -------------------------------------------------------------------------------------------------------- // Set CV buffers for (uint32_t i=0; i < pData->cvIn.count; ++i) - FloatVectorOperations::copy(fCvInBuffers[i], cvIn[i]+timeOffset, static_cast(frames)); + carla_copyFloats(fCvInBuffers[i], cvIn[i]+timeOffset, frames); for (uint32_t i=0; i < pData->cvOut.count; ++i) - FloatVectorOperations::clear(fCvOutBuffers[i], static_cast(frames)); + carla_zeroFloats(fCvOutBuffers[i], frames); #endif // -------------------------------------------------------------------------------------------------------- @@ -1932,7 +1932,7 @@ public: if (isPair) { CARLA_ASSERT(i+1 < pData->audioOut.count); - FloatVectorOperations::copy(oldBufLeft, fAudioOutBuffers[i], static_cast(frames)); + carla_copyFloats(oldBufLeft, fAudioOutBuffers[i], frames); } float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; diff --git a/source/backend/plugin/CarlaPluginVST2.cpp b/source/backend/plugin/CarlaPluginVST2.cpp index e9abcb545..05390b46d 100644 --- a/source/backend/plugin/CarlaPluginVST2.cpp +++ b/source/backend/plugin/CarlaPluginVST2.cpp @@ -993,7 +993,7 @@ public: { // disable any output sound for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(audioOut[i], static_cast(frames)); + carla_zeroFloats(audioOut[i], frames); return; } @@ -1489,7 +1489,7 @@ public: else { for (uint32_t i=0; i < pData->audioOut.count; ++i) - FloatVectorOperations::clear(vstOutBuffer[i], static_cast(frames)); + carla_zeroFloats(vstOutBuffer[i], frames); #if ! VST_FORCE_DEPRECATED fEffect->process(fEffect, (pData->audioIn.count > 0) ? vstInBuffer : nullptr, (pData->audioOut.count > 0) ? vstOutBuffer : nullptr, static_cast(frames)); @@ -1532,7 +1532,7 @@ public: if (isPair) { CARLA_ASSERT(i+1 < pData->audioOut.count); - FloatVectorOperations::copy(oldBufLeft, outBuffer[i]+timeOffset, static_cast(frames)); + carla_copyFloats(oldBufLeft, outBuffer[i]+timeOffset, frames); } float balRangeL = (pData->postProc.balanceLeft + 1.0f)/2.0f; diff --git a/source/bridges-plugin/Makefile b/source/bridges-plugin/Makefile index e0904cb1b..4a5210d93 100644 --- a/source/bridges-plugin/Makefile +++ b/source/bridges-plugin/Makefile @@ -43,13 +43,6 @@ LIBS_win64 = $(MODULEDIR)/jackbridge.win64e.a endif LINK_FLAGS += $(JACKBRIDGE_LIBS) -LIBS_native += $(MODULEDIR)/juce_audio_basics.a -LIBS_posix32 += $(MODULEDIR)/juce_audio_basics.posix32.a -LIBS_posix64 += $(MODULEDIR)/juce_audio_basics.posix64.a -LIBS_win32 += $(MODULEDIR)/juce_audio_basics.win32.a -LIBS_win64 += $(MODULEDIR)/juce_audio_basics.win64.a -LINK_FLAGS += $(JUCE_AUDIO_BASICS_LIBS) - LIBS_native += $(MODULEDIR)/juce_core.a LIBS_posix32 += $(MODULEDIR)/juce_core.posix32.a LIBS_posix64 += $(MODULEDIR)/juce_core.posix64.a diff --git a/source/carla_backend.pro b/source/carla_backend.pro index 58f5c5c17..8459a4bc3 100644 --- a/source/carla_backend.pro +++ b/source/carla_backend.pro @@ -76,15 +76,7 @@ INCLUDEPATH = \ LIBS = \ # Pre-Compiled modules ../build/modules/Debug/jackbridge.a \ - ../build/modules/Debug/juce_audio_basics.a \ - ../build/modules/Debug/juce_audio_devices.a \ - ../build/modules/Debug/juce_audio_formats.a \ - ../build/modules/Debug/juce_audio_processors.a \ ../build/modules/Debug/juce_core.a \ - ../build/modules/Debug/juce_data_structures.a \ - ../build/modules/Debug/juce_events.a \ - ../build/modules/Debug/juce_graphics.a \ - ../build/modules/Debug/juce_gui_basics.a \ ../build/modules/Debug/lilv.a \ ../build/modules/Debug/native-plugins.a \ ../build/modules/Debug/rtmempool.a diff --git a/source/libjack/Makefile b/source/libjack/Makefile index f6d691973..13da478cd 100644 --- a/source/libjack/Makefile +++ b/source/libjack/Makefile @@ -24,8 +24,6 @@ endif BUILD_C_FLAGS += -I$(CWD) -I$(CWD)/includes BUILD_CXX_FLAGS += -I$(CWD) -I$(CWD)/backend -I$(CWD)/includes -I$(CWD)/modules -I$(CWD)/utils LINK_FLAGS += $(MODULEDIR)/juce_core.a -LINK_FLAGS += $(MODULEDIR)/juce_audio_basics.a -LINK_FLAGS += $(JUCE_AUDIO_BASICS_LIBS) LINK_FLAGS += $(JUCE_CORE_LIBS) # ---------------------------------------------------------------------------------------------------------------------- diff --git a/source/libjack/libjack.cpp b/source/libjack/libjack.cpp index ed1218d3c..1b29db0e5 100644 --- a/source/libjack/libjack.cpp +++ b/source/libjack/libjack.cpp @@ -20,7 +20,6 @@ #include "CarlaThread.hpp" -using juce::FloatVectorOperations; using juce::Thread; using juce::Time; @@ -367,7 +366,7 @@ bool CarlaJackAppClient::initSharedMemmory() } fAudioTmpBuf = new float[fServer.bufferSize]; - FloatVectorOperations::clear(fAudioTmpBuf, fServer.bufferSize); + carla_zeroFloats(fAudioTmpBuf, fServer.bufferSize); // tell backend we're live const CarlaMutexLocker _cml(fShmNonRtServerControl.mutex); @@ -479,7 +478,7 @@ bool CarlaJackAppClient::handleRtData() delete[] fAudioTmpBuf; fAudioTmpBuf = new float[fServer.bufferSize]; - FloatVectorOperations::clear(fAudioTmpBuf, fServer.bufferSize); + carla_zeroFloats(fAudioTmpBuf, fServer.bufferSize); } } break; @@ -565,7 +564,7 @@ bool CarlaJackAppClient::handleRtData() float* const fdataRealOuts = fShmAudioPool.data+(fServer.bufferSize*fServer.numAudioIns); if (doBufferAddition && fServer.numAudioOuts > 0) - FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); + carla_zeroFloats(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); if (! fClients.isEmpty()) { @@ -611,7 +610,7 @@ bool CarlaJackAppClient::handleRtData() if (cmtl2.wasNotLocked() || jclient->processCb == nullptr || ! jclient->activated) { if (fServer.numAudioOuts > 0) - FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); + carla_zeroFloats(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); if (jclient->deactivated) fShmRtClientControl.data->procFlags = 1; @@ -680,7 +679,7 @@ bool CarlaJackAppClient::handleRtData() if (i < fServer.numAudioOuts) { const std::size_t remainingBufferSize = fServer.bufferSize * (fServer.numAudioOuts - i); - FloatVectorOperations::clear(fdataCopy, remainingBufferSize); + carla_zeroFloats(fdataCopy, remainingBufferSize); fdataCopy += remainingBufferSize; } @@ -711,7 +710,7 @@ bool CarlaJackAppClient::handleRtData() } if (needsTmpBufClear) - FloatVectorOperations::clear(fAudioTmpBuf, fServer.bufferSize); + carla_zeroFloats(fAudioTmpBuf, fServer.bufferSize); jclient->processCb(fServer.bufferSize, jclient->processCbPtr); @@ -720,21 +719,21 @@ bool CarlaJackAppClient::handleRtData() if (++numClientOutputsProcessed == 1) { // first client, we can copy stuff over - FloatVectorOperations::copy(fdataRealOuts, fdataCopyOuts, - fServer.bufferSize*fServer.numAudioOuts); + carla_copyFloats(fdataRealOuts, fdataCopyOuts, + fServer.bufferSize*fServer.numAudioOuts); } else { // subsequent clients, add data (then divide by number of clients later on) - FloatVectorOperations::add(fdataRealOuts, fdataCopyOuts, - fServer.bufferSize*fServer.numAudioOuts); + carla_add(fdataRealOuts, fdataCopyOuts, + fServer.bufferSize*fServer.numAudioOuts); if (doBufferAddition) { // for more than 1 client addition, we need to divide buffers now - FloatVectorOperations::multiply(fdataRealOuts, - 1.0f/static_cast(numClientOutputsProcessed), - fServer.bufferSize*fServer.numAudioOuts); + carla_multiply(fdataRealOuts, + 1.0f/static_cast(numClientOutputsProcessed), + fServer.bufferSize*fServer.numAudioOuts); } } @@ -742,9 +741,9 @@ bool CarlaJackAppClient::handleRtData() { for (uint8_t i=1; i 1 && ! doBufferAddition) { // more than 1 client active, need to divide buffers - FloatVectorOperations::multiply(fdataRealOuts, - 1.0f/static_cast(numClientOutputsProcessed), - fServer.bufferSize*fServer.numAudioOuts); + carla_multiply(fdataRealOuts, + 1.0f/static_cast(numClientOutputsProcessed), + fServer.bufferSize*fServer.numAudioOuts); } } // fClients.isEmpty() else if (fServer.numAudioOuts > 0) { - FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); + carla_zeroFloats(fdataRealOuts, fServer.bufferSize*fServer.numAudioOuts); } for (uint8_t i=0; i diff --git a/source/modules/AppConfig.h b/source/modules/AppConfig.h index 443c4ca09..95be33336 100644 --- a/source/modules/AppConfig.h +++ b/source/modules/AppConfig.h @@ -28,7 +28,6 @@ // -------------------------------------------------------------------------------------------------------------------- // always enabled -#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 #define JUCE_MODULE_AVAILABLE_juce_core 1 // always disabled @@ -39,6 +38,7 @@ #define JUCE_MODULE_AVAILABLE_juce_video 0 // also disabled +#define JUCE_MODULE_AVAILABLE_juce_audio_basics 0 #define JUCE_MODULE_AVAILABLE_juce_audio_devices 0 #define JUCE_MODULE_AVAILABLE_juce_audio_formats 0 #define JUCE_MODULE_AVAILABLE_juce_audio_processors 0 @@ -63,160 +63,6 @@ # define JUCE_AUDIOPROCESSOR_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 @@ -293,104 +139,6 @@ */ #define JUCE_ALLOW_STATIC_NULL_VARIABLES 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 0 - -#define JUCE_INCLUDE_PNGLIB_CODE 1 - -#define JUCE_INCLUDE_JPEGLIB_CODE 1 - -#ifdef APPCONFIG_OS_MAC -# define USE_COREGRAPHICS_RENDERING 1 -#else -# define USE_COREGRAPHICS_RENDERING 0 -#endif - -// -------------------------------------------------------------------------------------------------------------------- -// 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 diff --git a/source/modules/Makefile b/source/modules/Makefile index eb951f2d6..509dc20a6 100644 --- a/source/modules/Makefile +++ b/source/modules/Makefile @@ -9,7 +9,6 @@ all: clean: - $(MAKE) clean -C juce_audio_basics $(MAKE) clean -C juce_core $(MAKE) clean -C lilv $(MAKE) clean -C rtaudio diff --git a/source/modules/juce_audio_basics/Makefile b/source/modules/juce_audio_basics/Makefile deleted file mode 100644 index 471c5245e..000000000 --- a/source/modules/juce_audio_basics/Makefile +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/make -f -# Makefile for juce_audio_basics # -# ------------------------------ # -# Created by falkTX -# - -CWD=../.. -MODULENAME=juce_audio_basics -include ../Makefile.mk - -# ---------------------------------------------------------------------------------------------------------------------------- - -BUILD_CXX_FLAGS += $(JUCE_AUDIO_BASICS_FLAGS) -I.. - -# ---------------------------------------------------------------------------------------------------------------------------- - -ifeq ($(MACOS),true) -OBJS = $(OBJDIR)/$(MODULENAME).mm.o -OBJS_posix32 = $(OBJDIR)/$(MODULENAME).mm.posix32.o -OBJS_posix64 = $(OBJDIR)/$(MODULENAME).mm.posix64.o -else -OBJS = $(OBJDIR)/$(MODULENAME).cpp.o -OBJS_posix32 = $(OBJDIR)/$(MODULENAME).cpp.posix32.o -OBJS_posix64 = $(OBJDIR)/$(MODULENAME).cpp.posix64.o -endif -OBJS_win32 = $(OBJDIR)/$(MODULENAME).cpp.win32.o -OBJS_win64 = $(OBJDIR)/$(MODULENAME).cpp.win64.o - -# ---------------------------------------------------------------------------------------------------------------------------- - -all: $(MODULEDIR)/$(MODULENAME).a -posix32: $(MODULEDIR)/$(MODULENAME).posix32.a -posix64: $(MODULEDIR)/$(MODULENAME).posix64.a -win32: $(MODULEDIR)/$(MODULENAME).win32.a -win64: $(MODULEDIR)/$(MODULENAME).win64.a - -# ---------------------------------------------------------------------------------------------------------------------------- - -clean: - rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a - -debug: - $(MAKE) DEBUG=true - -# ---------------------------------------------------------------------------------------------------------------------------- - -$(MODULEDIR)/$(MODULENAME).a: $(OBJS) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).posix32.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).posix64.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).win32.a" - @rm -f $@ - @$(AR) crs $@ $^ - -$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64) - -@mkdir -p $(MODULEDIR) - @echo "Creating $(MODULENAME).win64.a" - @rm -f $@ - @$(AR) crs $@ $^ - -# ---------------------------------------------------------------------------------------------------------------------------- - -$(OBJDIR)/$(MODULENAME).cpp.o: $(MODULENAME).cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $<" - @$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ - -$(OBJDIR)/$(MODULENAME).cpp.%32.o: $(MODULENAME).cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (32bit)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ - -$(OBJDIR)/$(MODULENAME).cpp.%64.o: $(MODULENAME).cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (64bit)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ - -# ---------------------------------------------------------------------------------------------------------------------------- - -$(OBJDIR)/$(MODULENAME).mm.o: $(MODULENAME).cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $<" - @$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@ - -$(OBJDIR)/$(MODULENAME).mm.%32.o: $(MODULENAME).cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (32bit)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -ObjC++ -c -o $@ - -$(OBJDIR)/$(MODULENAME).mm.%64.o: $(MODULENAME).cpp - -@mkdir -p $(OBJDIR) - @echo "Compiling $< (64bit)" - @$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -ObjC++ -c -o $@ - -# ---------------------------------------------------------------------------------------------------------------------------- - --include $(OBJS:%.o=%.d) --include $(OBJS_posix32:%.o=%.d) --include $(OBJS_posix64:%.o=%.d) --include $(OBJS_win32:%.o=%.d) --include $(OBJS_win64:%.o=%.d) - -# ---------------------------------------------------------------------------------------------------------------------------- diff --git a/source/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h b/source/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h deleted file mode 100644 index 16e729db2..000000000 --- a/source/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - A subclass of AudioPlayHead can supply information about the position and - status of a moving play head during audio playback. - - One of these can be supplied to an AudioProcessor object so that it can find - out about the position of the audio that it is rendering. - - @see AudioProcessor::setPlayHead, AudioProcessor::getPlayHead -*/ -class JUCE_API AudioPlayHead -{ -protected: - //============================================================================== - AudioPlayHead() {} - -public: - virtual ~AudioPlayHead() {} - - //============================================================================== - /** Frame rate types. */ - enum FrameRateType - { - fps23976 = 0, - fps24 = 1, - fps25 = 2, - fps2997 = 3, - fps30 = 4, - fps2997drop = 5, - fps30drop = 6, - fps60 = 7, - fps60drop = 8, - fpsUnknown = 99 - }; - - //============================================================================== - /** This structure is filled-in by the AudioPlayHead::getCurrentPosition() method. - */ - struct JUCE_API CurrentPositionInfo - { - /** The tempo in BPM */ - double bpm; - - /** Time signature numerator, e.g. the 3 of a 3/4 time sig */ - int timeSigNumerator; - /** Time signature denominator, e.g. the 4 of a 3/4 time sig */ - int timeSigDenominator; - - /** The current play position, in samples from the start of the timeline. */ - int64 timeInSamples; - /** The current play position, in seconds from the start of the timeline. */ - double timeInSeconds; - - /** For timecode, the position of the start of the timeline, in seconds from 00:00:00:00. */ - double editOriginTime; - - /** The current play position, in pulses-per-quarter-note. */ - double ppqPosition; - - /** The position of the start of the last bar, in pulses-per-quarter-note. - - This is the time from the start of the timeline to the start of the current - bar, in ppq units. - - Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If - it's not available, the value will be 0. - */ - double ppqPositionOfLastBarStart; - - /** The video frame rate, if applicable. */ - FrameRateType frameRate; - - /** True if the transport is currently playing. */ - bool isPlaying; - - /** True if the transport is currently recording. - - (When isRecording is true, then isPlaying will also be true). - */ - bool isRecording; - - /** The current cycle start position in pulses-per-quarter-note. - Note that not all hosts or plugin formats may provide this value. - @see isLooping - */ - double ppqLoopStart; - - /** The current cycle end position in pulses-per-quarter-note. - Note that not all hosts or plugin formats may provide this value. - @see isLooping - */ - double ppqLoopEnd; - - /** True if the transport is currently looping. */ - bool isLooping; - - //============================================================================== - bool operator== (const CurrentPositionInfo& other) const noexcept; - bool operator!= (const CurrentPositionInfo& other) const noexcept; - - void resetToDefault(); - }; - - //============================================================================== - /** Fills-in the given structure with details about the transport's - position at the start of the current processing block. If this method returns - false then the current play head position is not available and the given - structure will be undefined. - - You can ONLY call this from your processBlock() method! Calling it at other - times will produce undefined behaviour, as the host may not have any context - in which a time would make sense, and some hosts will almost certainly have - multithreading issues if it's not called on the audio thread. - */ - virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0; - - /** Returns true if this object can control the transport. */ - virtual bool canControlTransport() { return false; } - - /** Starts or stops the audio. */ - virtual void transportPlay (bool shouldStartPlaying) { ignoreUnused (shouldStartPlaying); } - - /** Starts or stops recording the audio. */ - virtual void transportRecord (bool shouldStartRecording) { ignoreUnused (shouldStartRecording); } - - /** Rewinds the audio. */ - virtual void transportRewind() {} -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp b/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp deleted file mode 100644 index bffc3f4bf..000000000 --- a/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -AudioChannelSet::AudioChannelSet (uint32 c) : channels (c) {} -AudioChannelSet::AudioChannelSet (const Array& c) -{ - for (auto channel : c) - addChannel (channel); -} - -bool AudioChannelSet::operator== (const AudioChannelSet& other) const noexcept { return channels == other.channels; } -bool AudioChannelSet::operator!= (const AudioChannelSet& other) const noexcept { return channels != other.channels; } -bool AudioChannelSet::operator< (const AudioChannelSet& other) const noexcept { return channels < other.channels; } - -String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) -{ - if (type >= discreteChannel0) - return "Discrete " + String (type - discreteChannel0 + 1); - - switch (type) - { - case left: return NEEDS_TRANS("Left"); - case right: return NEEDS_TRANS("Right"); - case centre: return NEEDS_TRANS("Centre"); - case LFE: return NEEDS_TRANS("LFE"); - case leftSurround: return NEEDS_TRANS("Left Surround"); - case rightSurround: return NEEDS_TRANS("Right Surround"); - case leftCentre: return NEEDS_TRANS("Left Centre"); - case rightCentre: return NEEDS_TRANS("Right Centre"); - case centreSurround: return NEEDS_TRANS("Centre Surround"); - case leftSurroundRear: return NEEDS_TRANS("Left Surround Rear"); - case rightSurroundRear: return NEEDS_TRANS("Right Surround Rear"); - case topMiddle: return NEEDS_TRANS("Top Middle"); - case topFrontLeft: return NEEDS_TRANS("Top Front Left"); - case topFrontCentre: return NEEDS_TRANS("Top Front Centre"); - case topFrontRight: return NEEDS_TRANS("Top Front Right"); - case topRearLeft: return NEEDS_TRANS("Top Rear Left"); - case topRearCentre: return NEEDS_TRANS("Top Rear Centre"); - case topRearRight: return NEEDS_TRANS("Top Rear Right"); - case wideLeft: return NEEDS_TRANS("Wide Left"); - case wideRight: return NEEDS_TRANS("Wide Right"); - case LFE2: return NEEDS_TRANS("LFE 2"); - case leftSurroundSide: return NEEDS_TRANS("Left Surround Side"); - case rightSurroundSide: return NEEDS_TRANS("Right Surround Side"); - case ambisonicW: return NEEDS_TRANS("Ambisonic W"); - case ambisonicX: return NEEDS_TRANS("Ambisonic X"); - case ambisonicY: return NEEDS_TRANS("Ambisonic Y"); - case ambisonicZ: return NEEDS_TRANS("Ambisonic Z"); - case topSideLeft: return NEEDS_TRANS("Top Side Left"); - case topSideRight: return NEEDS_TRANS("Top Side Right"); - default: break; - } - - return "Unknown"; -} - -String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type) -{ - if (type >= discreteChannel0) - return String (type - discreteChannel0 + 1); - - switch (type) - { - case left: return "L"; - case right: return "R"; - case centre: return "C"; - case LFE: return "Lfe"; - case leftSurround: return "Ls"; - case rightSurround: return "Rs"; - case leftCentre: return "Lc"; - case rightCentre: return "Rc"; - case centreSurround: return "Cs"; - case leftSurroundRear: return "Lrs"; - case rightSurroundRear: return "Rrs"; - case topMiddle: return "Tm"; - case topFrontLeft: return "Tfl"; - case topFrontCentre: return "Tfc"; - case topFrontRight: return "Tfr"; - case topRearLeft: return "Trl"; - case topRearCentre: return "Trc"; - case topRearRight: return "Trr"; - case wideLeft: return "Wl"; - case wideRight: return "Wr"; - case LFE2: return "Lfe2"; - case leftSurroundSide: return "Lss"; - case rightSurroundSide: return "Rss"; - case ambisonicW: return "W"; - case ambisonicX: return "X"; - case ambisonicY: return "Y"; - case ambisonicZ: return "Z"; - case topSideLeft: return "Tsl"; - case topSideRight: return "Tsr"; - default: break; - } - - return {}; -} - -AudioChannelSet::ChannelType AudioChannelSet::getChannelTypeFromAbbreviation (const String& abbr) -{ - if (abbr.length() > 0 && (abbr[0] >= '0' && abbr[0] <= '9')) - return static_cast (static_cast (discreteChannel0) - + abbr.getIntValue() + 1); - - if (abbr == "L") return left; - if (abbr == "R") return right; - if (abbr == "C") return centre; - if (abbr == "Lfe") return LFE; - if (abbr == "Ls") return leftSurround; - if (abbr == "Rs") return rightSurround; - if (abbr == "Lc") return leftCentre; - if (abbr == "Rc") return rightCentre; - if (abbr == "Cs") return centreSurround; - if (abbr == "Lrs") return leftSurroundRear; - if (abbr == "Rrs") return rightSurroundRear; - if (abbr == "Tm") return topMiddle; - if (abbr == "Tfl") return topFrontLeft; - if (abbr == "Tfc") return topFrontCentre; - if (abbr == "Tfr") return topFrontRight; - if (abbr == "Trl") return topRearLeft; - if (abbr == "Trc") return topRearCentre; - if (abbr == "Trr") return topRearRight; - if (abbr == "Wl") return wideLeft; - if (abbr == "Wr") return wideRight; - if (abbr == "Lfe2") return LFE2; - if (abbr == "Lss") return leftSurroundSide; - if (abbr == "Rss") return rightSurroundSide; - if (abbr == "W") return ambisonicW; - if (abbr == "X") return ambisonicX; - if (abbr == "Y") return ambisonicY; - if (abbr == "Z") return ambisonicZ; - if (abbr == "Tsl") return topSideLeft; - if (abbr == "Tsr") return topSideRight; - - return unknown; -} - -String AudioChannelSet::getSpeakerArrangementAsString() const -{ - StringArray speakerTypes; - - for (auto& speaker : getChannelTypes()) - { - auto name = getAbbreviatedChannelTypeName (speaker); - - if (name.isNotEmpty()) - speakerTypes.add (name); - } - - return speakerTypes.joinIntoString (" "); -} - -AudioChannelSet AudioChannelSet::fromAbbreviatedString (const String& str) -{ - AudioChannelSet set; - - for (auto& abbr : StringArray::fromTokens (str, true)) - { - auto type = getChannelTypeFromAbbreviation (abbr); - - if (type != unknown) - set.addChannel (type); - } - - return set; -} - -String AudioChannelSet::getDescription() const -{ - if (isDiscreteLayout()) return "Discrete #" + String (size()); - if (*this == disabled()) return "Disabled"; - if (*this == mono()) return "Mono"; - if (*this == stereo()) return "Stereo"; - - if (*this == createLCR()) return "LCR"; - if (*this == createLRS()) return "LRS"; - if (*this == createLCRS()) return "LCRS"; - - if (*this == create5point0()) return "5.0 Surround"; - if (*this == create5point1()) return "5.1 Surround"; - if (*this == create6point0()) return "6.0 Surround"; - if (*this == create6point1()) return "6.1 Surround"; - if (*this == create6point0Music()) return "6.0 (Music) Surround"; - if (*this == create6point1Music()) return "6.1 (Music) Surround"; - if (*this == create7point0()) return "7.0 Surround"; - if (*this == create7point1()) return "7.1 Surround"; - if (*this == create7point0SDDS()) return "7.0 Surround SDDS"; - if (*this == create7point1SDDS()) return "7.1 Surround SDDS"; - if (*this == create7point0point2()) return "7.0.2 Surround"; - if (*this == create7point1point2()) return "7.1.2 Surround"; - - if (*this == quadraphonic()) return "Quadraphonic"; - if (*this == pentagonal()) return "Pentagonal"; - if (*this == hexagonal()) return "Hexagonal"; - if (*this == octagonal()) return "Octagonal"; - if (*this == ambisonic()) return "Ambisonic"; - - return "Unknown"; -} - -bool AudioChannelSet::isDiscreteLayout() const noexcept -{ - for (auto& speaker : getChannelTypes()) - if (speaker <= topSideRight) - return false; - - return true; -} - -int AudioChannelSet::size() const noexcept -{ - return channels.countNumberOfSetBits(); -} - -AudioChannelSet::ChannelType AudioChannelSet::getTypeOfChannel (int index) const noexcept -{ - int bit = channels.findNextSetBit(0); - - for (int i = 0; i < index && bit >= 0; ++i) - bit = channels.findNextSetBit (bit + 1); - - return static_cast (bit); -} - -int AudioChannelSet::getChannelIndexForType (AudioChannelSet::ChannelType type) const noexcept -{ - int idx = 0; - - for (int bit = channels.findNextSetBit (0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) - { - if (static_cast (bit) == type) - return idx; - - idx++; - } - - return -1; -} - -Array AudioChannelSet::getChannelTypes() const -{ - Array result; - - for (int bit = channels.findNextSetBit(0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) - result.add (static_cast (bit)); - - return result; -} - -void AudioChannelSet::addChannel (ChannelType newChannel) -{ - const int bit = static_cast (newChannel); - jassert (bit >= 0 && bit < 1024); - channels.setBit (bit); -} - -void AudioChannelSet::removeChannel (ChannelType newChannel) -{ - const int bit = static_cast (newChannel); - jassert (bit >= 0 && bit < 1024); - channels.clearBit (bit); -} - -AudioChannelSet AudioChannelSet::disabled() { return {}; } -AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (1u << centre); } -AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } -AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } -AudioChannelSet AudioChannelSet::createLRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surround)); } -AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); } -AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); } -AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround)); } -AudioChannelSet AudioChannelSet::create6point0Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); } -AudioChannelSet AudioChannelSet::create6point1Music() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftSurroundSide) | (1u << rightSurroundSide)); } -AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::create7point0SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } -AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::create7point1SDDS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurround) | (1u << rightSurround) | (1u << leftCentre) | (1u << rightCentre)); } -AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); } -AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << leftSurround) | (1u << rightSurround)); } -AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << centreSurround) | (1u << leftSurroundRear) | (1u << rightSurroundRear)); } -AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurround) | (1u << rightSurround) | (1u << centreSurround) | (1u << wideLeft) | (1u << wideRight)); } -AudioChannelSet AudioChannelSet::create7point0point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } -AudioChannelSet AudioChannelSet::create7point1point2() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << LFE) | (1u << leftSurroundSide) | (1u << rightSurroundSide) | (1u << leftSurroundRear) | (1u << rightSurroundRear) | (1u << topSideLeft) | (1u << topSideRight)); } - - -AudioChannelSet AudioChannelSet::discreteChannels (int numChannels) -{ - AudioChannelSet s; - s.channels.setRange (discreteChannel0, numChannels, true); - return s; -} - -AudioChannelSet AudioChannelSet::canonicalChannelSet (int numChannels) -{ - if (numChannels == 1) return AudioChannelSet::mono(); - if (numChannels == 2) return AudioChannelSet::stereo(); - if (numChannels == 3) return AudioChannelSet::createLCR(); - if (numChannels == 4) return AudioChannelSet::quadraphonic(); - if (numChannels == 5) return AudioChannelSet::create5point0(); - if (numChannels == 6) return AudioChannelSet::create5point1(); - if (numChannels == 7) return AudioChannelSet::create7point0(); - if (numChannels == 8) return AudioChannelSet::create7point1(); - - return discreteChannels (numChannels); -} - -AudioChannelSet AudioChannelSet::namedChannelSet (int numChannels) -{ - if (numChannels == 1) return AudioChannelSet::mono(); - if (numChannels == 2) return AudioChannelSet::stereo(); - if (numChannels == 3) return AudioChannelSet::createLCR(); - if (numChannels == 4) return AudioChannelSet::quadraphonic(); - if (numChannels == 5) return AudioChannelSet::create5point0(); - if (numChannels == 6) return AudioChannelSet::create5point1(); - if (numChannels == 7) return AudioChannelSet::create7point0(); - if (numChannels == 8) return AudioChannelSet::create7point1(); - - return {}; -} - -Array AudioChannelSet::channelSetsWithNumberOfChannels (int numChannels) -{ - Array retval; - - if (numChannels != 0) - { - retval.add (AudioChannelSet::discreteChannels (numChannels)); - - if (numChannels == 1) - { - retval.add (AudioChannelSet::mono()); - } - else if (numChannels == 2) - { - retval.add (AudioChannelSet::stereo()); - } - else if (numChannels == 3) - { - retval.add (AudioChannelSet::createLCR()); - retval.add (AudioChannelSet::createLRS()); - } - else if (numChannels == 4) - { - retval.add (AudioChannelSet::quadraphonic()); - retval.add (AudioChannelSet::createLCRS()); - retval.add (AudioChannelSet::ambisonic()); - } - else if (numChannels == 5) - { - retval.add (AudioChannelSet::create5point0()); - retval.add (AudioChannelSet::pentagonal()); - } - else if (numChannels == 6) - { - retval.add (AudioChannelSet::create5point1()); - retval.add (AudioChannelSet::create6point0()); - retval.add (AudioChannelSet::create6point0Music()); - retval.add (AudioChannelSet::hexagonal()); - } - else if (numChannels == 7) - { - retval.add (AudioChannelSet::create7point0()); - retval.add (AudioChannelSet::create7point0SDDS()); - retval.add (AudioChannelSet::create6point1()); - retval.add (AudioChannelSet::create6point1Music()); - } - else if (numChannels == 8) - { - retval.add (AudioChannelSet::create7point1()); - retval.add (AudioChannelSet::create7point1SDDS()); - retval.add (AudioChannelSet::octagonal()); - } - } - - return retval; -} - -AudioChannelSet JUCE_CALLTYPE AudioChannelSet::channelSetWithChannels (const Array& channelArray) -{ - AudioChannelSet set; - - for (auto ch : channelArray) - { - jassert (! set.channels[static_cast (ch)]); - - set.addChannel (ch); - } - - return set; -} - -//============================================================================== -AudioChannelSet JUCE_CALLTYPE AudioChannelSet::fromWaveChannelMask (int32 dwChannelMask) -{ - return AudioChannelSet (static_cast ((dwChannelMask & ((1 << 18) - 1)) << 1)); -} - -int32 AudioChannelSet::getWaveChannelMask() const noexcept -{ - if (channels.getHighestBit() > topRearRight) - return -1; - - return (channels.toInteger() >> 1); -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h b/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h deleted file mode 100644 index 431ae389d..000000000 --- a/source/modules/juce_audio_basics/buffers/juce_AudioChannelSet.h +++ /dev/null @@ -1,408 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Represents a set of audio channel types. - - For example, you might have a set of left + right channels, which is a stereo - channel set. It is a collection of values from the AudioChannelSet::ChannelType - enum, where each type may only occur once within the set. - - The documentation below lists which AudioChannelSet corresponds to which native - layouts used by AAX, VST2/VST3 and CoreAudio/AU. The layout tags in CoreAudio - are particularly confusing. For example, the layout which is labeled as "7.1 SDDS" - in Logic Pro, corresponds to CoreAudio/AU's kAudioChannelLayoutTag_DTS_7_0 tag, whereas - AAX's DTS 7.1 Layout corresponds to CoreAudio/AU's - kAudioChannelLayoutTag_MPEG_7_1_A format, etc. Please do not use the CoreAudio tag - as an indication to the actual layout of the speakers. - - @see Bus -*/ -class JUCE_API AudioChannelSet -{ -public: - /** Creates an empty channel set. - You can call addChannel to add channels to the set. - */ - AudioChannelSet() noexcept {} - - /** Creates a zero-channel set which can be used to indicate that a - bus is disabled. */ - static AudioChannelSet JUCE_CALLTYPE disabled(); - - //============================================================================== - /** Creates a one-channel mono set (centre). - - Is equivalent to: kMonoAAX (VST), AAX_eStemFormat_Mono (AAX), kAudioChannelLayoutTag_Mono (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE mono(); - - - /** Creates a set containing a stereo set (left, right). - - Is equivalent to: kStereo (VST), AAX_eStemFormat_Stereo (AAX), kAudioChannelLayoutTag_Stereo (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE stereo(); - - - //============================================================================== - /** Creates a set containing an LCR set (left, right, centre). - - Is equivalent to: k30Cine (VST), AAX_eStemFormat_LCR (AAX), kAudioChannelLayoutTag_MPEG_3_0_A (CoreAudio) - - This format is referred to as "LRC" in Cubase. - This format is referred to as "LCR" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE createLCR(); - - - /** Creates a set containing an LRS set (left, right, surround). - - Is equivalent to: k30Music (VST), n/a (AAX), kAudioChannelLayoutTag_ITU_2_1 (CoreAudio) - - This format is referred to as "LRS" in Cubase. - */ - static AudioChannelSet JUCE_CALLTYPE createLRS(); - - - /** Creates a set containing an LCRS set (left, right, centre, surround). - - Is equivalent to: k40Cine (VST), AAX_eStemFormat_LCRS (AAX), kAudioChannelLayoutTag_MPEG_4_0_A (CoreAudio) - - This format is referred to as "LCRS (Pro Logic)" in Logic Pro. - This format is referred to as "LRCS" in Cubase. - This format is referred to as "LCRS" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE createLCRS(); - - - //============================================================================== - /** Creates a set for a 5.0 surround setup (left, right, centre, leftSurround, rightSurround). - - Is equivalent to: k50 (VST), AAX_eStemFormat_5_0 (AAX), kAudioChannelLayoutTag_MPEG_5_0_A (CoreAudio) - - This format is referred to as "5.0" in Cubase. - This format is referred to as "5.0" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create5point0(); - - - /** Creates a set for a 5.1 surround setup (left, right, centre, leftSurround, rightSurround, LFE). - - Is equivalent to: k51 (VST), AAX_eStemFormat_5_1 (AAX), kAudioChannelLayoutTag_MPEG_5_1_A (CoreAudio) - - This format is referred to as "5.1 (ITU 775)" in Logic Pro. - This format is referred to as "5.1" in Cubase. - This format is referred to as "5.1" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create5point1(); - - - /** Creates a set for a 6.0 Cine surround setup (left, right, centre, leftSurround, rightSurround, centreSurround). - - Is equivalent to: k60Cine (VST), AAX_eStemFormat_6_0 (AAX), kAudioChannelLayoutTag_AudioUnit_6_0 (CoreAudio) - - Logic Pro incorrectly uses this for the surround format labeled "6.1 (ES/EX)". - This format is referred to as "6.0 Cine" in Cubase. - This format is referred to as "6.0" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create6point0(); - - - /** Creates a set for a 6.1 Cine surround setup (left, right, centre, leftSurround, rightSurround, centreSurround, LFE). - - Is equivalent to: k61Cine (VST), AAX_eStemFormat_6_1 (AAX), kAudioChannelLayoutTag_MPEG_6_1_A (CoreAudio) - - This format is referred to as "6.1" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create6point1(); - - - /** Creates a set for a 6.0 Music surround setup (left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide). - - Is equivalent to: k60Music (VST), n/a (AAX), kAudioChannelLayoutTag_DTS_6_0_A (CoreAudio) - - This format is referred to as "6.0 Music" in Cubase. - */ - static AudioChannelSet JUCE_CALLTYPE create6point0Music(); - - - /** Creates a set for a 6.0 Music surround setup (left, right, leftSurround, rightSurround, leftSurroundSide, rightSurroundSide, LFE). - - Is equivalent to: k61Music (VST), n/a (AAX), kAudioChannelLayoutTag_DTS_6_1_A (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE create6point1Music(); - - - /** Creates a set for a DTS 7.0 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear). - - Is equivalent to: k70Music (VST), AAX_eStemFormat_7_0_DTS (AAX), kAudioChannelLayoutTag_AudioUnit_7_0 (CoreAudio) - - This format is referred to as "7.0" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create7point0(); - - - /** Creates a set for a SDDS 7.0 surround setup (left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre). - - Is equivalent to: k70Cine (VST), AAX_eStemFormat_7_0_SDDS (AAX), kAudioChannelLayoutTag_AudioUnit_7_0_Front (CoreAudio) - - This format is referred to as "7.0 SDDS" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create7point0SDDS(); - - - /** Creates a set for a DTS 7.1 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE). - - Is equivalent to: k71CineSideFill (VST), AAX_eStemFormat_7_1_DTS (AAX), kAudioChannelLayoutTag_MPEG_7_1_C/kAudioChannelLayoutTag_ITU_3_4_1 (CoreAudio) - - This format is referred to as "7.1 (3/4.1)" in Logic Pro. - This format is referred to as "7.1" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create7point1(); - - - /** Creates a set for a 7.1 surround setup (left, right, centre, leftSurround, rightSurround, leftCentre, rightCentre, LFE). - - Is equivalent to: k71Cine (VST), AAX_eStemFormat_7_1_SDDS (AAX), kAudioChannelLayoutTag_MPEG_7_1_A (CoreAudio) - - This format is referred to as "7.1 (SDDS)" in Logic Pro. - This format is referred to as "7.1 SDDS" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE create7point1SDDS(); - - /** Creates a set for Dolby Atmos 7.0.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, topSideLeft, topSideRight). - - Is equivalent to: n/a (VST), AAX_eStemFormat_7_0_2 (AAX), n/a (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE create7point0point2(); - - /** Creates a set for Dolby Atmos 7.1.2 surround setup (left, right, centre, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear, LFE, topSideLeft, topSideRight). - - Is equivalent to: k71_2 (VST), AAX_eStemFormat_7_1_2 (AAX), n/a (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE create7point1point2(); - - - //============================================================================== - /** Creates a set for ambisonic surround setups (ambisonicW, ambisonicX, ambisonicY, ambisonicZ). - - Is equivalent to: kBFormat (VST), n/a (AAX), kAudioChannelLayoutTag_Ambisonic_B_Format (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE ambisonic(); - - - /** Creates a set for quadraphonic surround setup (left, right, leftSurround, rightSurround) - - Is equivalent to: k40Music (VST), AAX_eStemFormat_Quad (AAX), kAudioChannelLayoutTag_Quadraphonic (CoreAudio) - - This format is referred to as "Quadraphonic" in Logic Pro. - This format is referred to as "Quadro" in Cubase. - This format is referred to as "Quad" in Pro Tools. - */ - static AudioChannelSet JUCE_CALLTYPE quadraphonic(); - - - /** Creates a set for pentagonal surround setup (left, right, centre, leftSurroundRear, rightSurroundRear). - - Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Pentagonal (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE pentagonal(); - - - /** Creates a set for hexagonal surround setup (left, right, leftSurroundRear, rightSurroundRear, centre, surroundCentre). - - Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Hexagonal (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE hexagonal(); - - - /** Creates a set for octagonal surround setup (left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight). - - Is equivalent to: n/a (VST), n/a (AAX), kAudioChannelLayoutTag_Octagonal (CoreAudio) - */ - static AudioChannelSet JUCE_CALLTYPE octagonal(); - - //============================================================================== - /** Creates a set of untyped discrete channels. */ - static AudioChannelSet JUCE_CALLTYPE discreteChannels (int numChannels); - - /** Create a canonical channel set for a given number of channels. - For example, numChannels = 1 will return mono, numChannels = 2 will return stereo, etc. */ - static AudioChannelSet JUCE_CALLTYPE canonicalChannelSet (int numChannels); - - /** Create a channel set for a given number of channels which is non-discrete. - If numChannels is larger than the number of channels of the surround format - with the maximum amount of channels (currently 7.1 Surround), then this - function returns an empty set.*/ - static AudioChannelSet JUCE_CALLTYPE namedChannelSet (int numChannels); - - /** Return an array of channel sets which have a given number of channels */ - static Array JUCE_CALLTYPE channelSetsWithNumberOfChannels (int numChannels); - - //============================================================================== - /** Represents different audio channel types. */ - enum ChannelType - { - unknown = 0, - - left = 1, // L - right = 2, // R - centre = 3, // C (sometimes M for mono) - - LFE = 4, - leftSurround = 5, // Ls - rightSurround = 6, // Rs - leftCentre = 7, // Lc (AAX/VST), Lc used as Lss in AU for most layouts - rightCentre = 8, // Rc (AAX/VST), Rc used as Rss in AU for most layouts - centreSurround = 9, // Cs/S - surround = centreSurround, // Cs/S - leftSurroundSide = 10, // Lss (AXX), Side Left "Sl" (VST), Left Centre "LC" (AU) - rightSurroundSide = 11, // Rss (AXX), Side right "Sr" (VST), Right Centre "Rc" (AU) - topMiddle = 12, - topFrontLeft = 13, - topFrontCentre = 14, - topFrontRight = 15, - topRearLeft = 16, - topRearCentre = 17, - topRearRight = 18, - LFE2 = 19, - leftSurroundRear = 20, // Lsr (AAX), Lcs (VST), Rls (AU) - rightSurroundRear = 21, // Rsr (AAX), Rcs (VST), Rrs (AU) - wideLeft = 22, - wideRight = 23, - - - ambisonicW = 24, - ambisonicX = 25, - ambisonicY = 26, - ambisonicZ = 27, - - // Used by Dolby Atmos 7.0.2 and 7.1.2 - topSideLeft = 28, // Lts (AAX), Tsl (VST) - topSideRight = 29, // Rts (AAX), Tsr (VST) - - - discreteChannel0 = 64 /**< Non-typed individual channels are indexed upwards from this value. */ - }; - - /** Returns the name of a given channel type. For example, this method may return "Surround Left". */ - static String JUCE_CALLTYPE getChannelTypeName (ChannelType); - - /** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */ - static String JUCE_CALLTYPE getAbbreviatedChannelTypeName (ChannelType); - - /** Returns the channel type from an abbreviated name. */ - static ChannelType JUCE_CALLTYPE getChannelTypeFromAbbreviation (const String& abbreviation); - - //============================================================================== - enum - { - maxChannelsOfNamedLayout = 10 - }; - - /** Adds a channel to the set. */ - void addChannel (ChannelType newChannelType); - - /** Removes a channel from the set. */ - void removeChannel (ChannelType newChannelType); - - /** Returns the number of channels in the set. */ - int size() const noexcept; - - /** Returns true if there are no channels in the set. */ - bool isDisabled() const noexcept { return size() == 0; } - - /** Returns an array of all the types in this channel set. */ - Array getChannelTypes() const; - - /** Returns the type of one of the channels in the set, by index. */ - ChannelType getTypeOfChannel (int channelIndex) const noexcept; - - /** Returns the index for a particular channel-type. - Will return -1 if the this set does not contain a channel of this type. */ - int getChannelIndexForType (ChannelType type) const noexcept; - - /** Returns a string containing a whitespace-separated list of speaker types - corresponding to each channel. For example in a 5.1 arrangement, - the string may be "L R C Lfe Ls Rs". If the speaker arrangement is unknown, - the returned string will be empty.*/ - String getSpeakerArrangementAsString() const; - - /** Returns an AudioChannelSet from a string returned by getSpeakerArrangementAsString - - @see getSpeakerArrangementAsString */ - static AudioChannelSet fromAbbreviatedString (const String& set); - - /** Returns the description of the current layout. For example, this method may return - "Quadraphonic". Note that the returned string may not be unique. */ - String getDescription() const; - - /** Returns if this is a channel layout made-up of discrete channels. */ - bool isDiscreteLayout() const noexcept; - - /** Intersect two channel layouts. */ - void intersect (const AudioChannelSet& other) { channels &= other.channels; } - - /** Creates a channel set for a list of channel types. This function will assert - if you supply a duplicate channel. - - Note that this method ignores the order in which the channels are given, i.e. - two arrays with the same elements but in a different order will still result - in the same channel set. - */ - static AudioChannelSet JUCE_CALLTYPE channelSetWithChannels (const Array&); - - //============================================================================== - // Conversion between wave and juce channel layout identifiers - - /** Create an AudioChannelSet from a WAVEFORMATEXTENSIBLE channelMask (typically used - in .wav files). */ - static AudioChannelSet JUCE_CALLTYPE fromWaveChannelMask (int32 dwChannelMask); - - /** Returns a WAVEFORMATEXTENSIBLE channelMask representation (typically used in .wav - files) of the receiver. - - Returns -1 if the receiver cannot be represented in a WAVEFORMATEXTENSIBLE channelMask - representation. - */ - int32 getWaveChannelMask() const noexcept; - - //============================================================================== - bool operator== (const AudioChannelSet&) const noexcept; - bool operator!= (const AudioChannelSet&) const noexcept; - bool operator< (const AudioChannelSet&) const noexcept; - -private: - //============================================================================== - BigInteger channels; - - //============================================================================== - explicit AudioChannelSet (uint32); - explicit AudioChannelSet (const Array&); -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp deleted file mode 100644 index 3f31379dd..000000000 --- a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ /dev/null @@ -1,603 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - const double maxVal = (double) 0x7fff; - char* intData = static_cast (dest); - - if (dest != (void*) source || destBytesPerSample <= 4) - { - for (int i = 0; i < numSamples; ++i) - { - *(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - intData += destBytesPerSample; - } - } - else - { - intData += destBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= destBytesPerSample; - *(uint16*) intData = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - } - } -} - -void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - const double maxVal = (double) 0x7fff; - char* intData = static_cast (dest); - - if (dest != (void*) source || destBytesPerSample <= 4) - { - for (int i = 0; i < numSamples; ++i) - { - *(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - intData += destBytesPerSample; - } - } - else - { - intData += destBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= destBytesPerSample; - *(uint16*) intData = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - } - } -} - -void AudioDataConverters::convertFloatToInt24LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - const double maxVal = (double) 0x7fffff; - char* intData = static_cast (dest); - - if (dest != (void*) source || destBytesPerSample <= 4) - { - for (int i = 0; i < numSamples; ++i) - { - ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); - intData += destBytesPerSample; - } - } - else - { - intData += destBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= destBytesPerSample; - ByteOrder::littleEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); - } - } -} - -void AudioDataConverters::convertFloatToInt24BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - const double maxVal = (double) 0x7fffff; - char* intData = static_cast (dest); - - if (dest != (void*) source || destBytesPerSample <= 4) - { - for (int i = 0; i < numSamples; ++i) - { - ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); - intData += destBytesPerSample; - } - } - else - { - intData += destBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= destBytesPerSample; - ByteOrder::bigEndian24BitToChars (roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i])), intData); - } - } -} - -void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - const double maxVal = (double) 0x7fffffff; - char* intData = static_cast (dest); - - if (dest != (void*) source || destBytesPerSample <= 4) - { - for (int i = 0; i < numSamples; ++i) - { - *(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - intData += destBytesPerSample; - } - } - else - { - intData += destBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= destBytesPerSample; - *(uint32*)intData = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - } - } -} - -void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - const double maxVal = (double) 0x7fffffff; - char* intData = static_cast (dest); - - if (dest != (void*) source || destBytesPerSample <= 4) - { - for (int i = 0; i < numSamples; ++i) - { - *(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - intData += destBytesPerSample; - } - } - else - { - intData += destBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= destBytesPerSample; - *(uint32*)intData = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); - } - } -} - -void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data! - - char* d = static_cast (dest); - - for (int i = 0; i < numSamples; ++i) - { - *(float*) d = source[i]; - - #if JUCE_BIG_ENDIAN - *(uint32*) d = ByteOrder::swap (*(uint32*) d); - #endif - - d += destBytesPerSample; - } -} - -void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* dest, int numSamples, const int destBytesPerSample) -{ - jassert (dest != (void*) source || destBytesPerSample <= 4); // This op can't be performed on in-place data! - - char* d = static_cast (dest); - - for (int i = 0; i < numSamples; ++i) - { - *(float*) d = source[i]; - - #if JUCE_LITTLE_ENDIAN - *(uint32*) d = ByteOrder::swap (*(uint32*) d); - #endif - - d += destBytesPerSample; - } -} - -//============================================================================== -void AudioDataConverters::convertInt16LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const float scale = 1.0f / 0x7fff; - const char* intData = static_cast (source); - - if (source != (void*) dest || srcBytesPerSample >= 4) - { - for (int i = 0; i < numSamples; ++i) - { - dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData); - intData += srcBytesPerSample; - } - } - else - { - intData += srcBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= srcBytesPerSample; - dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*(uint16*)intData); - } - } -} - -void AudioDataConverters::convertInt16BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const float scale = 1.0f / 0x7fff; - const char* intData = static_cast (source); - - if (source != (void*) dest || srcBytesPerSample >= 4) - { - for (int i = 0; i < numSamples; ++i) - { - dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData); - intData += srcBytesPerSample; - } - } - else - { - intData += srcBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= srcBytesPerSample; - dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*(uint16*)intData); - } - } -} - -void AudioDataConverters::convertInt24LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const float scale = 1.0f / 0x7fffff; - const char* intData = static_cast (source); - - if (source != (void*) dest || srcBytesPerSample >= 4) - { - for (int i = 0; i < numSamples; ++i) - { - dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData); - intData += srcBytesPerSample; - } - } - else - { - intData += srcBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= srcBytesPerSample; - dest[i] = scale * (short) ByteOrder::littleEndian24Bit (intData); - } - } -} - -void AudioDataConverters::convertInt24BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const float scale = 1.0f / 0x7fffff; - const char* intData = static_cast (source); - - if (source != (void*) dest || srcBytesPerSample >= 4) - { - for (int i = 0; i < numSamples; ++i) - { - dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData); - intData += srcBytesPerSample; - } - } - else - { - intData += srcBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= srcBytesPerSample; - dest[i] = scale * (short) ByteOrder::bigEndian24Bit (intData); - } - } -} - -void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const auto scale = 1.0f / (float) 0x7fffffff; - const char* intData = static_cast (source); - - if (source != (void*) dest || srcBytesPerSample >= 4) - { - for (int i = 0; i < numSamples; ++i) - { - dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData); - intData += srcBytesPerSample; - } - } - else - { - intData += srcBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= srcBytesPerSample; - dest[i] = scale * (int) ByteOrder::swapIfBigEndian (*(uint32*) intData); - } - } -} - -void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const auto scale = 1.0f / (float) 0x7fffffff; - const char* intData = static_cast (source); - - if (source != (void*) dest || srcBytesPerSample >= 4) - { - for (int i = 0; i < numSamples; ++i) - { - dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData); - intData += srcBytesPerSample; - } - } - else - { - intData += srcBytesPerSample * numSamples; - - for (int i = numSamples; --i >= 0;) - { - intData -= srcBytesPerSample; - dest[i] = scale * (int) ByteOrder::swapIfLittleEndian (*(uint32*) intData); - } - } -} - -void AudioDataConverters::convertFloat32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const char* s = static_cast (source); - - for (int i = 0; i < numSamples; ++i) - { - dest[i] = *(float*)s; - - #if JUCE_BIG_ENDIAN - uint32* const d = (uint32*) (dest + i); - *d = ByteOrder::swap (*d); - #endif - - s += srcBytesPerSample; - } -} - -void AudioDataConverters::convertFloat32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) -{ - const char* s = static_cast (source); - - for (int i = 0; i < numSamples; ++i) - { - dest[i] = *(float*)s; - - #if JUCE_LITTLE_ENDIAN - uint32* const d = (uint32*) (dest + i); - *d = ByteOrder::swap (*d); - #endif - - s += srcBytesPerSample; - } -} - - -//============================================================================== -void AudioDataConverters::convertFloatToFormat (const DataFormat destFormat, - const float* const source, - void* const dest, - const int numSamples) -{ - switch (destFormat) - { - case int16LE: convertFloatToInt16LE (source, dest, numSamples); break; - case int16BE: convertFloatToInt16BE (source, dest, numSamples); break; - case int24LE: convertFloatToInt24LE (source, dest, numSamples); break; - case int24BE: convertFloatToInt24BE (source, dest, numSamples); break; - case int32LE: convertFloatToInt32LE (source, dest, numSamples); break; - case int32BE: convertFloatToInt32BE (source, dest, numSamples); break; - case float32LE: convertFloatToFloat32LE (source, dest, numSamples); break; - case float32BE: convertFloatToFloat32BE (source, dest, numSamples); break; - default: jassertfalse; break; - } -} - -void AudioDataConverters::convertFormatToFloat (const DataFormat sourceFormat, - const void* const source, - float* const dest, - const int numSamples) -{ - switch (sourceFormat) - { - case int16LE: convertInt16LEToFloat (source, dest, numSamples); break; - case int16BE: convertInt16BEToFloat (source, dest, numSamples); break; - case int24LE: convertInt24LEToFloat (source, dest, numSamples); break; - case int24BE: convertInt24BEToFloat (source, dest, numSamples); break; - case int32LE: convertInt32LEToFloat (source, dest, numSamples); break; - case int32BE: convertInt32BEToFloat (source, dest, numSamples); break; - case float32LE: convertFloat32LEToFloat (source, dest, numSamples); break; - case float32BE: convertFloat32BEToFloat (source, dest, numSamples); break; - default: jassertfalse; break; - } -} - -//============================================================================== -void AudioDataConverters::interleaveSamples (const float** const source, - float* const dest, - const int numSamples, - const int numChannels) -{ - for (int chan = 0; chan < numChannels; ++chan) - { - int i = chan; - const float* src = source [chan]; - - for (int j = 0; j < numSamples; ++j) - { - dest [i] = src [j]; - i += numChannels; - } - } -} - -void AudioDataConverters::deinterleaveSamples (const float* const source, - float** const dest, - const int numSamples, - const int numChannels) -{ - for (int chan = 0; chan < numChannels; ++chan) - { - int i = chan; - float* dst = dest [chan]; - - for (int j = 0; j < numSamples; ++j) - { - dst [j] = source [i]; - i += numChannels; - } - } -} - - -//============================================================================== -#if JUCE_UNIT_TESTS - -class AudioConversionTests : public UnitTest -{ -public: - AudioConversionTests() : UnitTest ("Audio data conversion", "Audio") {} - - template - struct Test5 - { - static void test (UnitTest& unitTest, Random& r) - { - test (unitTest, false, r); - test (unitTest, true, r); - } - - static void test (UnitTest& unitTest, bool inPlace, Random& r) - { - const int numSamples = 2048; - int32 original [numSamples], converted [numSamples], reversed [numSamples]; - - { - AudioData::Pointer d (original); - bool clippingFailed = false; - - for (int i = 0; i < numSamples / 2; ++i) - { - d.setAsFloat (r.nextFloat() * 2.2f - 1.1f); - - if (! d.isFloatingPoint()) - clippingFailed = d.getAsFloat() > 1.0f || d.getAsFloat() < -1.0f || clippingFailed; - - ++d; - d.setAsInt32 (r.nextInt()); - ++d; - } - - unitTest.expect (! clippingFailed); - } - - // convert data from the source to dest format.. - ScopedPointer conv (new AudioData::ConverterInstance , - AudioData::Pointer >()); - conv->convertSamples (inPlace ? reversed : converted, original, numSamples); - - // ..and back again.. - conv = new AudioData::ConverterInstance , - AudioData::Pointer >(); - if (! inPlace) - zeromem (reversed, sizeof (reversed)); - - conv->convertSamples (reversed, inPlace ? reversed : converted, numSamples); - - { - int biggestDiff = 0; - AudioData::Pointer d1 (original); - AudioData::Pointer d2 (reversed); - - const int errorMargin = 2 * AudioData::Pointer::get32BitResolution() - + AudioData::Pointer::get32BitResolution(); - - for (int i = 0; i < numSamples; ++i) - { - biggestDiff = jmax (biggestDiff, std::abs (d1.getAsInt32() - d2.getAsInt32())); - ++d1; - ++d2; - } - - unitTest.expect (biggestDiff <= errorMargin); - } - } - }; - - template - struct Test3 - { - static void test (UnitTest& unitTest, Random& r) - { - Test5 ::test (unitTest, r); - Test5 ::test (unitTest, r); - } - }; - - template - struct Test2 - { - static void test (UnitTest& unitTest, Random& r) - { - Test3 ::test (unitTest, r); - Test3 ::test (unitTest, r); - Test3 ::test (unitTest, r); - Test3 ::test (unitTest, r); - Test3 ::test (unitTest, r); - Test3 ::test (unitTest, r); - } - }; - - template - struct Test1 - { - static void test (UnitTest& unitTest, Random& r) - { - Test2 ::test (unitTest, r); - Test2 ::test (unitTest, r); - } - }; - - void runTest() override - { - Random r = getRandom(); - beginTest ("Round-trip conversion: Int8"); - Test1 ::test (*this, r); - beginTest ("Round-trip conversion: Int16"); - Test1 ::test (*this, r); - beginTest ("Round-trip conversion: Int24"); - Test1 ::test (*this, r); - beginTest ("Round-trip conversion: Int32"); - Test1 ::test (*this, r); - beginTest ("Round-trip conversion: Float32"); - Test1 ::test (*this, r); - } -}; - -static AudioConversionTests audioConversionUnitTests; - -#endif - -} // namespace juce diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h deleted file mode 100644 index 6af682200..000000000 --- a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ /dev/null @@ -1,712 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - This class a container which holds all the classes pertaining to the AudioData::Pointer - audio sample format class. - - @see AudioData::Pointer. -*/ -class JUCE_API AudioData -{ -public: - //============================================================================== - // These types can be used as the SampleFormat template parameter for the AudioData::Pointer class. - - class Int8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit integer packed data format. */ - class UInt8; /**< Used as a template parameter for AudioData::Pointer. Indicates an 8-bit unsigned integer packed data format. */ - class Int16; /**< Used as a template parameter for AudioData::Pointer. Indicates an 16-bit integer packed data format. */ - class Int24; /**< Used as a template parameter for AudioData::Pointer. Indicates an 24-bit integer packed data format. */ - class Int32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit integer packed data format. */ - class Float32; /**< Used as a template parameter for AudioData::Pointer. Indicates an 32-bit float data format. */ - - //============================================================================== - // These types can be used as the Endianness template parameter for the AudioData::Pointer class. - - class BigEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in big-endian order. */ - class LittleEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in little-endian order. */ - class NativeEndian; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored in the CPU's native endianness. */ - - //============================================================================== - // These types can be used as the InterleavingType template parameter for the AudioData::Pointer class. - - class NonInterleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are stored contiguously. */ - class Interleaved; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples are interleaved with a number of other channels. */ - - //============================================================================== - // These types can be used as the Constness template parameter for the AudioData::Pointer class. - - class NonConst; /**< Used as a template parameter for AudioData::Pointer. Indicates that the pointer can be used for non-const data. */ - class Const; /**< Used as a template parameter for AudioData::Pointer. Indicates that the samples can only be used for const data.. */ - - #ifndef DOXYGEN - //============================================================================== - class BigEndian - { - public: - template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); } - template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); } - template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); } - template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); } - template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); } - enum { isBigEndian = 1 }; - }; - - class LittleEndian - { - public: - template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); } - template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); } - template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); } - template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); } - template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); } - enum { isBigEndian = 0 }; - }; - - #if JUCE_BIG_ENDIAN - class NativeEndian : public BigEndian {}; - #else - class NativeEndian : public LittleEndian {}; - #endif - - //============================================================================== - class Int8 - { - public: - inline Int8 (void* d) noexcept : data (static_cast (d)) {} - - inline void advance() noexcept { ++data; } - inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) (*data * (1.0 / (1.0 + maxValue))); } - inline float getAsFloatBE() const noexcept { return getAsFloatLE(); } - inline void setAsFloatLE (float newValue) noexcept { *data = (int8) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))); } - inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); } - inline int32 getAsInt32LE() const noexcept { return (int) (*((uint8*) data) << 24); } - inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); } - inline void setAsInt32LE (int newValue) noexcept { *data = (int8) (newValue >> 24); } - inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); } - inline void clear() noexcept { *data = 0; } - inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} - template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } - template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } - inline void copyFromSameType (Int8& source) noexcept { *data = *source.data; } - - int8* data; - enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 }; - }; - - class UInt8 - { - public: - inline UInt8 (void* d) noexcept : data (static_cast (d)) {} - - inline void advance() noexcept { ++data; } - inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) ((*data - 128) * (1.0 / (1.0 + maxValue))); } - inline float getAsFloatBE() const noexcept { return getAsFloatLE(); } - inline void setAsFloatLE (float newValue) noexcept { *data = (uint8) jlimit (0, 255, 128 + roundToInt (newValue * (1.0 + maxValue))); } - inline void setAsFloatBE (float newValue) noexcept { setAsFloatLE (newValue); } - inline int32 getAsInt32LE() const noexcept { return (int) (((uint8) (*data - 128)) << 24); } - inline int32 getAsInt32BE() const noexcept { return getAsInt32LE(); } - inline void setAsInt32LE (int newValue) noexcept { *data = (uint8) (128 + (newValue >> 24)); } - inline void setAsInt32BE (int newValue) noexcept { setAsInt32LE (newValue); } - inline void clear() noexcept { *data = 128; } - inline void clearMultiple (int num) noexcept { memset (data, 128, (size_t) num) ;} - template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } - template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } - inline void copyFromSameType (UInt8& source) noexcept { *data = *source.data; } - - uint8* data; - enum { bytesPerSample = 1, maxValue = 0x7f, resolution = (1 << 24), isFloat = 0 }; - }; - - class Int16 - { - public: - inline Int16 (void* d) noexcept : data (static_cast (d)) {} - - inline void advance() noexcept { ++data; } - inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfBigEndian (*data)); } - inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int16) ByteOrder::swapIfLittleEndian (*data)); } - inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); } - inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue)))); } - inline int32 getAsInt32LE() const noexcept { return (int32) (ByteOrder::swapIfBigEndian ((uint16) *data) << 16); } - inline int32 getAsInt32BE() const noexcept { return (int32) (ByteOrder::swapIfLittleEndian ((uint16) *data) << 16); } - inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint16) (newValue >> 16)); } - inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint16) (newValue >> 16)); } - inline void clear() noexcept { *data = 0; } - inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} - template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } - template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } - inline void copyFromSameType (Int16& source) noexcept { *data = *source.data; } - - uint16* data; - enum { bytesPerSample = 2, maxValue = 0x7fff, resolution = (1 << 16), isFloat = 0 }; - }; - - class Int24 - { - public: - inline Int24 (void* d) noexcept : data (static_cast (d)) {} - - inline void advance() noexcept { data += 3; } - inline void skip (int numSamples) noexcept { data += 3 * numSamples; } - inline float getAsFloatLE() const noexcept { return (float) (ByteOrder::littleEndian24Bit (data) * (1.0 / (1.0 + maxValue))); } - inline float getAsFloatBE() const noexcept { return (float) (ByteOrder::bigEndian24Bit (data) * (1.0 / (1.0 + maxValue))); } - inline void setAsFloatLE (float newValue) noexcept { ByteOrder::littleEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); } - inline void setAsFloatBE (float newValue) noexcept { ByteOrder::bigEndian24BitToChars (jlimit ((int) -maxValue, (int) maxValue, roundToInt (newValue * (1.0 + maxValue))), data); } - inline int32 getAsInt32LE() const noexcept { return (int32) (((unsigned int) ByteOrder::littleEndian24Bit (data)) << 8); } - inline int32 getAsInt32BE() const noexcept { return (int32) (((unsigned int) ByteOrder::bigEndian24Bit (data)) << 8); } - inline void setAsInt32LE (int32 newValue) noexcept { ByteOrder::littleEndian24BitToChars (newValue >> 8, data); } - inline void setAsInt32BE (int32 newValue) noexcept { ByteOrder::bigEndian24BitToChars (newValue >> 8, data); } - inline void clear() noexcept { data[0] = 0; data[1] = 0; data[2] = 0; } - inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} - template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } - template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } - inline void copyFromSameType (Int24& source) noexcept { data[0] = source.data[0]; data[1] = source.data[1]; data[2] = source.data[2]; } - - char* data; - enum { bytesPerSample = 3, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; - }; - - class Int32 - { - public: - inline Int32 (void* d) noexcept : data (static_cast (d)) {} - - inline void advance() noexcept { ++data; } - inline void skip (int numSamples) noexcept { data += numSamples; } - inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } - inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } - inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (int32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } - inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (int32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } - inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); } - inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data); } - inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); } - inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); } - inline void clear() noexcept { *data = 0; } - inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} - template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } - template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } - inline void copyFromSameType (Int32& source) noexcept { *data = *source.data; } - - uint32* data; - enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 }; - }; - - /** A 32-bit integer type, of which only the bottom 24 bits are used. */ - class Int24in32 : public Int32 - { - public: - inline Int24in32 (void* d) noexcept : Int32 (d) {} - - inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } - inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } - inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } - inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } - inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data) << 8; } - inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data) << 8; } - inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue >> 8); } - inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue >> 8); } - template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } - template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } - inline void copyFromSameType (Int24in32& source) noexcept { *data = *source.data; } - - enum { bytesPerSample = 4, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; - }; - - class Float32 - { - public: - inline Float32 (void* d) noexcept : data (static_cast (d)) {} - - inline void advance() noexcept { ++data; } - inline void skip (int numSamples) noexcept { data += numSamples; } - #if JUCE_BIG_ENDIAN - inline float getAsFloatBE() const noexcept { return *data; } - inline void setAsFloatBE (float newValue) noexcept { *data = newValue; } - inline float getAsFloatLE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; } - inline void setAsFloatLE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); } - #else - inline float getAsFloatLE() const noexcept { return *data; } - inline void setAsFloatLE (float newValue) noexcept { *data = newValue; } - inline float getAsFloatBE() const noexcept { union { uint32 asInt; float asFloat; } n; n.asInt = ByteOrder::swap (*(uint32*) data); return n.asFloat; } - inline void setAsFloatBE (float newValue) noexcept { union { uint32 asInt; float asFloat; } n; n.asFloat = newValue; *(uint32*) data = ByteOrder::swap (n.asInt); } - #endif - inline int32 getAsInt32LE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatLE()) * (double) maxValue); } - inline int32 getAsInt32BE() const noexcept { return (int32) roundToInt (jlimit (-1.0, 1.0, (double) getAsFloatBE()) * (double) maxValue); } - inline void setAsInt32LE (int32 newValue) noexcept { setAsFloatLE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } - inline void setAsInt32BE (int32 newValue) noexcept { setAsFloatBE ((float) (newValue * (1.0 / (1.0 + maxValue)))); } - inline void clear() noexcept { *data = 0; } - inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} - template inline void copyFromLE (SourceType& source) noexcept { setAsFloatLE (source.getAsFloat()); } - template inline void copyFromBE (SourceType& source) noexcept { setAsFloatBE (source.getAsFloat()); } - inline void copyFromSameType (Float32& source) noexcept { *data = *source.data; } - - float* data; - enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = (1 << 8), isFloat = 1 }; - }; - - //============================================================================== - class NonInterleaved - { - public: - inline NonInterleaved() noexcept {} - inline NonInterleaved (const NonInterleaved&) noexcept {} - inline NonInterleaved (const int) noexcept {} - inline void copyFrom (const NonInterleaved&) noexcept {} - template inline void advanceData (SampleFormatType& s) noexcept { s.advance(); } - template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numSamples); } - template inline void clear (SampleFormatType& s, int numSamples) noexcept { s.clearMultiple (numSamples); } - template inline static int getNumBytesBetweenSamples (const SampleFormatType&) noexcept { return SampleFormatType::bytesPerSample; } - - enum { isInterleavedType = 0, numInterleavedChannels = 1 }; - }; - - class Interleaved - { - public: - inline Interleaved() noexcept : numInterleavedChannels (1) {} - inline Interleaved (const Interleaved& other) noexcept : numInterleavedChannels (other.numInterleavedChannels) {} - inline Interleaved (const int numInterleavedChans) noexcept : numInterleavedChannels (numInterleavedChans) {} - inline void copyFrom (const Interleaved& other) noexcept { numInterleavedChannels = other.numInterleavedChannels; } - template inline void advanceData (SampleFormatType& s) noexcept { s.skip (numInterleavedChannels); } - template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numInterleavedChannels * numSamples); } - template inline void clear (SampleFormatType& s, int numSamples) noexcept { while (--numSamples >= 0) { s.clear(); s.skip (numInterleavedChannels); } } - template inline int getNumBytesBetweenSamples (const SampleFormatType&) const noexcept { return numInterleavedChannels * SampleFormatType::bytesPerSample; } - int numInterleavedChannels; - enum { isInterleavedType = 1 }; - }; - - //============================================================================== - class NonConst - { - public: - typedef void VoidType; - static inline void* toVoidPtr (VoidType* v) noexcept { return v; } - enum { isConst = 0 }; - }; - - class Const - { - public: - typedef const void VoidType; - static inline void* toVoidPtr (VoidType* v) noexcept { return const_cast (v); } - enum { isConst = 1 }; - }; - #endif - - //============================================================================== - /** - A pointer to a block of audio data with a particular encoding. - - This object can be used to read and write from blocks of encoded audio samples. To create one, you specify - the audio format as a series of template parameters, e.g. - @code - // this creates a pointer for reading from a const array of 16-bit little-endian packed samples. - AudioData::Pointer pointer (someRawAudioData); - - // These methods read the sample that is being pointed to - float firstSampleAsFloat = pointer.getAsFloat(); - int32 firstSampleAsInt = pointer.getAsInt32(); - ++pointer; // moves the pointer to the next sample. - pointer += 3; // skips the next 3 samples. - @endcode - - The convertSamples() method lets you copy a range of samples from one format to another, automatically - converting its format. - - @see AudioData::Converter - */ - template - class Pointer : private InterleavingType // (inherited for EBCO) - { - public: - //============================================================================== - /** Creates a non-interleaved pointer from some raw data in the appropriate format. - This constructor is only used if you've specified the AudioData::NonInterleaved option - - for interleaved formats, use the constructor that also takes a number of channels. - */ - Pointer (typename Constness::VoidType* sourceData) noexcept - : data (Constness::toVoidPtr (sourceData)) - { - // If you're using interleaved data, call the other constructor! If you're using non-interleaved data, - // you should pass NonInterleaved as the template parameter for the interleaving type! - static_assert (InterleavingType::isInterleavedType == 0, "Incorrect constructor for interleaved data"); - } - - /** Creates a pointer from some raw data in the appropriate format with the specified number of interleaved channels. - For non-interleaved data, use the other constructor. - */ - Pointer (typename Constness::VoidType* sourceData, int numInterleaved) noexcept - : InterleavingType (numInterleaved), data (Constness::toVoidPtr (sourceData)) - { - } - - /** Creates a copy of another pointer. */ - Pointer (const Pointer& other) noexcept - : InterleavingType (other), data (other.data) - { - } - - Pointer& operator= (const Pointer& other) noexcept - { - InterleavingType::operator= (other); - data = other.data; - return *this; - } - - //============================================================================== - /** Returns the value of the first sample as a floating point value. - The value will be in the range -1.0 to 1.0 for integer formats. For floating point - formats, the value could be outside that range, although -1 to 1 is the standard range. - */ - inline float getAsFloat() const noexcept { return Endianness::getAsFloat (data); } - - /** Sets the value of the first sample as a floating point value. - - (This method can only be used if the AudioData::NonConst option was used). - The value should be in the range -1.0 to 1.0 - for integer formats, values outside that - range will be clipped. For floating point formats, any value passed in here will be - written directly, although -1 to 1 is the standard range. - */ - inline void setAsFloat (float newValue) noexcept - { - // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! - static_assert (Constness::isConst == 0, "Attempt to write to a const pointer"); - Endianness::setAsFloat (data, newValue); - } - - /** Returns the value of the first sample as a 32-bit integer. - The value returned will be in the range 0x80000000 to 0x7fffffff, and shorter values will be - shifted to fill this range (e.g. if you're reading from 24-bit data, the values will be shifted up - by 8 bits when returned here). If the source data is floating point, values beyond -1.0 to 1.0 will - be clipped so that -1.0 maps onto -0x7fffffff and 1.0 maps to 0x7fffffff. - */ - inline int32 getAsInt32() const noexcept { return Endianness::getAsInt32 (data); } - - /** Sets the value of the first sample as a 32-bit integer. - This will be mapped to the range of the format that is being written - see getAsInt32(). - */ - inline void setAsInt32 (int32 newValue) noexcept - { - // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! - static_assert (Constness::isConst == 0, "Attempt to write to a const pointer"); - Endianness::setAsInt32 (data, newValue); - } - - /** Moves the pointer along to the next sample. */ - inline Pointer& operator++() noexcept { advance(); return *this; } - - /** Moves the pointer back to the previous sample. */ - inline Pointer& operator--() noexcept { this->advanceDataBy (data, -1); return *this; } - - /** Adds a number of samples to the pointer's position. */ - Pointer& operator+= (int samplesToJump) noexcept { this->advanceDataBy (data, samplesToJump); return *this; } - - /** Writes a stream of samples into this pointer from another pointer. - This will copy the specified number of samples, converting between formats appropriately. - */ - void convertSamples (Pointer source, int numSamples) const noexcept - { - // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! - static_assert (Constness::isConst == 0, "Attempt to write to a const pointer"); - - for (Pointer dest (*this); --numSamples >= 0;) - { - dest.data.copyFromSameType (source.data); - dest.advance(); - source.advance(); - } - } - - /** Writes a stream of samples into this pointer from another pointer. - This will copy the specified number of samples, converting between formats appropriately. - */ - template - void convertSamples (OtherPointerType source, int numSamples) const noexcept - { - // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! - static_assert (Constness::isConst == 0, "Attempt to write to a const pointer"); - - Pointer dest (*this); - - if (source.getRawData() != getRawData() || source.getNumBytesBetweenSamples() >= getNumBytesBetweenSamples()) - { - while (--numSamples >= 0) - { - Endianness::copyFrom (dest.data, source); - dest.advance(); - ++source; - } - } - else // copy backwards if we're increasing the sample width.. - { - dest += numSamples; - source += numSamples; - - while (--numSamples >= 0) - Endianness::copyFrom ((--dest).data, --source); - } - } - - /** Sets a number of samples to zero. */ - void clearSamples (int numSamples) const noexcept - { - Pointer dest (*this); - dest.clear (dest.data, numSamples); - } - - /** Scans a block of data, returning the lowest and highest levels as floats */ - Range findMinAndMax (size_t numSamples) const noexcept - { - if (numSamples == 0) - return Range(); - - Pointer dest (*this); - - if (isFloatingPoint()) - { - float mn = dest.getAsFloat(); - dest.advance(); - float mx = mn; - - while (--numSamples > 0) - { - const float v = dest.getAsFloat(); - dest.advance(); - - if (mx < v) mx = v; - if (v < mn) mn = v; - } - - return Range (mn, mx); - } - - int32 mn = dest.getAsInt32(); - dest.advance(); - int32 mx = mn; - - while (--numSamples > 0) - { - const int v = dest.getAsInt32(); - dest.advance(); - - if (mx < v) mx = v; - if (v < mn) mn = v; - } - - return Range (mn * (float) (1.0 / (1.0 + Int32::maxValue)), - mx * (float) (1.0 / (1.0 + Int32::maxValue))); - } - - /** Scans a block of data, returning the lowest and highest levels as floats */ - void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept - { - Range r (findMinAndMax (numSamples)); - minValue = r.getStart(); - maxValue = r.getEnd(); - } - - /** Returns true if the pointer is using a floating-point format. */ - static bool isFloatingPoint() noexcept { return (bool) SampleFormat::isFloat; } - - /** Returns true if the format is big-endian. */ - static bool isBigEndian() noexcept { return (bool) Endianness::isBigEndian; } - - /** Returns the number of bytes in each sample (ignoring the number of interleaved channels). */ - static int getBytesPerSample() noexcept { return (int) SampleFormat::bytesPerSample; } - - /** Returns the number of interleaved channels in the format. */ - int getNumInterleavedChannels() const noexcept { return (int) this->numInterleavedChannels; } - - /** Returns the number of bytes between the start address of each sample. */ - int getNumBytesBetweenSamples() const noexcept { return InterleavingType::getNumBytesBetweenSamples (data); } - - /** Returns the accuracy of this format when represented as a 32-bit integer. - This is the smallest number above 0 that can be represented in the sample format, converted to - a 32-bit range. E,g. if the format is 8-bit, its resolution is 0x01000000; if the format is 24-bit, - its resolution is 0x100. - */ - static int get32BitResolution() noexcept { return (int) SampleFormat::resolution; } - - /** Returns a pointer to the underlying data. */ - const void* getRawData() const noexcept { return data.data; } - - private: - //============================================================================== - SampleFormat data; - - inline void advance() noexcept { this->advanceData (data); } - - Pointer operator++ (int); // private to force you to use the more efficient pre-increment! - Pointer operator-- (int); - }; - - //============================================================================== - /** A base class for objects that are used to convert between two different sample formats. - - The AudioData::ConverterInstance implements this base class and can be templated, so - you can create an instance that converts between two particular formats, and then - store this in the abstract base class. - - @see AudioData::ConverterInstance - */ - class Converter - { - public: - virtual ~Converter() {} - - /** Converts a sequence of samples from the converter's source format into the dest format. */ - virtual void convertSamples (void* destSamples, const void* sourceSamples, int numSamples) const = 0; - - /** Converts a sequence of samples from the converter's source format into the dest format. - This method takes sub-channel indexes, which can be used with interleaved formats in order to choose a - particular sub-channel of the data to be used. - */ - virtual void convertSamples (void* destSamples, int destSubChannel, - const void* sourceSamples, int sourceSubChannel, int numSamples) const = 0; - }; - - //============================================================================== - /** - A class that converts between two templated AudioData::Pointer types, and which - implements the AudioData::Converter interface. - - This can be used as a concrete instance of the AudioData::Converter abstract class. - - @see AudioData::Converter - */ - template - class ConverterInstance : public Converter - { - public: - ConverterInstance (int numSourceChannels = 1, int numDestChannels = 1) - : sourceChannels (numSourceChannels), destChannels (numDestChannels) - {} - - void convertSamples (void* dest, const void* source, int numSamples) const override - { - SourceSampleType s (source, sourceChannels); - DestSampleType d (dest, destChannels); - d.convertSamples (s, numSamples); - } - - void convertSamples (void* dest, int destSubChannel, - const void* source, int sourceSubChannel, int numSamples) const override - { - jassert (destSubChannel < destChannels && sourceSubChannel < sourceChannels); - - SourceSampleType s (addBytesToPointer (source, sourceSubChannel * SourceSampleType::getBytesPerSample()), sourceChannels); - DestSampleType d (addBytesToPointer (dest, destSubChannel * DestSampleType::getBytesPerSample()), destChannels); - d.convertSamples (s, numSamples); - } - - private: - JUCE_DECLARE_NON_COPYABLE (ConverterInstance) - - const int sourceChannels, destChannels; - }; -}; - - - -//============================================================================== -/** - A set of routines to convert buffers of 32-bit floating point data to and from - various integer formats. - - Note that these functions are deprecated - the AudioData class provides a much more - flexible set of conversion classes now. -*/ -class JUCE_API AudioDataConverters -{ -public: - //============================================================================== - static void convertFloatToInt16LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2); - static void convertFloatToInt16BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 2); - - static void convertFloatToInt24LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3); - static void convertFloatToInt24BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 3); - - static void convertFloatToInt32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); - static void convertFloatToInt32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); - - static void convertFloatToFloat32LE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); - static void convertFloatToFloat32BE (const float* source, void* dest, int numSamples, int destBytesPerSample = 4); - - //============================================================================== - static void convertInt16LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2); - static void convertInt16BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 2); - - static void convertInt24LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3); - static void convertInt24BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 3); - - static void convertInt32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); - static void convertInt32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); - - static void convertFloat32LEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); - static void convertFloat32BEToFloat (const void* source, float* dest, int numSamples, int srcBytesPerSample = 4); - - //============================================================================== - enum DataFormat - { - int16LE, - int16BE, - int24LE, - int24BE, - int32LE, - int32BE, - float32LE, - float32BE, - }; - - static void convertFloatToFormat (DataFormat destFormat, - const float* source, void* dest, int numSamples); - - static void convertFormatToFloat (DataFormat sourceFormat, - const void* source, float* dest, int numSamples); - - //============================================================================== - static void interleaveSamples (const float** source, float* dest, - int numSamples, int numChannels); - - static void deinterleaveSamples (const float* source, float** dest, - int numSamples, int numChannels); - -private: - AudioDataConverters(); - JUCE_DECLARE_NON_COPYABLE (AudioDataConverters) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h deleted file mode 100644 index ac9addd56..000000000 --- a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ /dev/null @@ -1,1126 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - A multi-channel buffer of floating point audio samples. - - @see AudioSampleBuffer -*/ -template -class AudioBuffer -{ -public: - //============================================================================== - /** Creates an empty buffer with 0 channels and 0 length. */ - AudioBuffer() noexcept - : numChannels (0), size (0), allocatedBytes (0), - channels (static_cast (preallocatedChannelSpace)), - isClear (false) - { - } - - //============================================================================== - /** Creates a buffer with a specified number of channels and samples. - - The contents of the buffer will initially be undefined, so use clear() to - set all the samples to zero. - - The buffer will allocate its memory internally, and this will be released - when the buffer is deleted. If the memory can't be allocated, this will - throw a std::bad_alloc exception. - */ - AudioBuffer (int numChannelsToAllocate, - int numSamplesToAllocate) - : numChannels (numChannelsToAllocate), - size (numSamplesToAllocate) - { - jassert (size >= 0 && numChannels >= 0); - allocateData(); - } - - /** Creates a buffer using a pre-allocated block of memory. - - Note that if the buffer is resized or its number of channels is changed, it - will re-allocate memory internally and copy the existing data to this new area, - so it will then stop directly addressing this memory. - - @param dataToReferTo a pre-allocated array containing pointers to the data - for each channel that should be used by this buffer. The - buffer will only refer to this memory, it won't try to delete - it when the buffer is deleted or resized. - @param numChannelsToUse the number of channels to use - this must correspond to the - number of elements in the array passed in - @param numSamples the number of samples to use - this must correspond to the - size of the arrays passed in - */ - AudioBuffer (Type* const* dataToReferTo, - int numChannelsToUse, - int numSamples) - : numChannels (numChannelsToUse), - size (numSamples), - allocatedBytes (0) - { - jassert (dataToReferTo != nullptr); - jassert (numChannelsToUse >= 0 && numSamples >= 0); - allocateChannels (dataToReferTo, 0); - } - - /** Creates a buffer using a pre-allocated block of memory. - - Note that if the buffer is resized or its number of channels is changed, it - will re-allocate memory internally and copy the existing data to this new area, - so it will then stop directly addressing this memory. - - @param dataToReferTo a pre-allocated array containing pointers to the data - for each channel that should be used by this buffer. The - buffer will only refer to this memory, it won't try to delete - it when the buffer is deleted or resized. - @param numChannelsToUse the number of channels to use - this must correspond to the - number of elements in the array passed in - @param startSample the offset within the arrays at which the data begins - @param numSamples the number of samples to use - this must correspond to the - size of the arrays passed in - */ - AudioBuffer (Type* const* dataToReferTo, - int numChannelsToUse, - int startSample, - int numSamples) - : numChannels (numChannelsToUse), - size (numSamples), - allocatedBytes (0), - isClear (false) - { - jassert (dataToReferTo != nullptr); - jassert (numChannelsToUse >= 0 && startSample >= 0 && numSamples >= 0); - allocateChannels (dataToReferTo, startSample); - } - - /** Copies another buffer. - - This buffer will make its own copy of the other's data, unless the buffer was created - using an external data buffer, in which case boths buffers will just point to the same - shared block of data. - */ - AudioBuffer (const AudioBuffer& other) - : numChannels (other.numChannels), - size (other.size), - allocatedBytes (other.allocatedBytes) - { - if (allocatedBytes == 0) - { - allocateChannels (other.channels, 0); - } - else - { - allocateData(); - - if (other.isClear) - { - clear(); - } - else - { - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::copy (channels[i], other.channels[i], size); - } - } - } - - /** Copies another buffer onto this one. - This buffer's size will be changed to that of the other buffer. - */ - AudioBuffer& operator= (const AudioBuffer& other) - { - if (this != &other) - { - setSize (other.getNumChannels(), other.getNumSamples(), false, false, false); - - if (other.isClear) - { - clear(); - } - else - { - isClear = false; - - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::copy (channels[i], other.channels[i], size); - } - } - - return *this; - } - - /** Destructor. - This will free any memory allocated by the buffer. - */ - ~AudioBuffer() noexcept {} - - /** Move constructor */ - AudioBuffer (AudioBuffer&& other) noexcept - : numChannels (other.numChannels), - size (other.size), - allocatedBytes (other.allocatedBytes), - allocatedData (static_cast&&> (other.allocatedData)), - isClear (other.isClear) - { - if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) - { - channels = preallocatedChannelSpace; - memcpy (preallocatedChannelSpace, other.channels, sizeof (preallocatedChannelSpace)); - } - else - { - channels = other.channels; - } - - other.numChannels = 0; - other.size = 0; - other.allocatedBytes = 0; - } - - /** Move assignment */ - AudioBuffer& operator= (AudioBuffer&& other) noexcept - { - numChannels = other.numChannels; - size = other.size; - allocatedBytes = other.allocatedBytes; - allocatedData = static_cast&&> (other.allocatedData); - isClear = other.isClear; - - if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) - { - channels = preallocatedChannelSpace; - memcpy (preallocatedChannelSpace, other.channels, sizeof (preallocatedChannelSpace)); - } - else - { - channels = other.channels; - } - - other.numChannels = 0; - other.size = 0; - other.allocatedBytes = 0; - return *this; - } - - //============================================================================== - /** Returns the number of channels of audio data that this buffer contains. - @see getNumSamples, getReadPointer, getWritePointer - */ - int getNumChannels() const noexcept { return numChannels; } - - /** Returns the number of samples allocated in each of the buffer's channels. - @see getNumChannels, getReadPointer, getWritePointer - */ - int getNumSamples() const noexcept { return size; } - - /** Returns a pointer to an array of read-only samples in one of the buffer's channels. - For speed, this doesn't check whether the channel number is out of range, - so be careful when using it! - If you need to write to the data, do NOT call this method and const_cast the - result! Instead, you must call getWritePointer so that the buffer knows you're - planning on modifying the data. - */ - const Type* getReadPointer (int channelNumber) const noexcept - { - jassert (isPositiveAndBelow (channelNumber, numChannels)); - return channels[channelNumber]; - } - - /** Returns a pointer to an array of read-only samples in one of the buffer's channels. - For speed, this doesn't check whether the channel number or index are out of range, - so be careful when using it! - If you need to write to the data, do NOT call this method and const_cast the - result! Instead, you must call getWritePointer so that the buffer knows you're - planning on modifying the data. - */ - const Type* getReadPointer (int channelNumber, int sampleIndex) const noexcept - { - jassert (isPositiveAndBelow (channelNumber, numChannels)); - jassert (isPositiveAndBelow (sampleIndex, size)); - return channels[channelNumber] + sampleIndex; - } - - /** Returns a writeable pointer to one of the buffer's channels. - For speed, this doesn't check whether the channel number is out of range, - so be careful when using it! - Note that if you're not planning on writing to the data, you should always - use getReadPointer instead. - */ - Type* getWritePointer (int channelNumber) noexcept - { - jassert (isPositiveAndBelow (channelNumber, numChannels)); - isClear = false; - return channels[channelNumber]; - } - - /** Returns a writeable pointer to one of the buffer's channels. - For speed, this doesn't check whether the channel number or index are out of range, - so be careful when using it! - Note that if you're not planning on writing to the data, you should - use getReadPointer instead. - */ - Type* getWritePointer (int channelNumber, int sampleIndex) noexcept - { - jassert (isPositiveAndBelow (channelNumber, numChannels)); - jassert (isPositiveAndBelow (sampleIndex, size)); - isClear = false; - return channels[channelNumber] + sampleIndex; - } - - /** Returns an array of pointers to the channels in the buffer. - - Don't modify any of the pointers that are returned, and bear in mind that - these will become invalid if the buffer is resized. - */ - const Type** getArrayOfReadPointers() const noexcept { return const_cast (channels); } - - /** Returns an array of pointers to the channels in the buffer. - - Don't modify any of the pointers that are returned, and bear in mind that - these will become invalid if the buffer is resized. - */ - Type** getArrayOfWritePointers() noexcept { isClear = false; return channels; } - - //============================================================================== - /** Changes the buffer's size or number of channels. - - This can expand or contract the buffer's length, and add or remove channels. - - If keepExistingContent is true, it will try to preserve as much of the - old data as it can in the new buffer. - - If clearExtraSpace is true, then any extra channels or space that is - allocated will be also be cleared. If false, then this space is left - uninitialised. - - If avoidReallocating is true, then changing the buffer's size won't reduce the - amount of memory that is currently allocated (but it will still increase it if - the new size is bigger than the amount it currently has). If this is false, then - a new allocation will be done so that the buffer uses takes up the minimum amount - of memory that it needs. - - If the required memory can't be allocated, this will throw a std::bad_alloc exception. - */ - void setSize (int newNumChannels, - int newNumSamples, - bool keepExistingContent = false, - bool clearExtraSpace = false, - bool avoidReallocating = false) - { - jassert (newNumChannels >= 0); - jassert (newNumSamples >= 0); - - if (newNumSamples != size || newNumChannels != numChannels) - { - const auto allocatedSamplesPerChannel = ((size_t) newNumSamples + 3) & ~3u; - const auto channelListSize = ((sizeof (Type*) * (size_t) (newNumChannels + 1)) + 15) & ~15u; - const auto newTotalBytes = ((size_t) newNumChannels * (size_t) allocatedSamplesPerChannel * sizeof (Type)) - + channelListSize + 32; - - if (keepExistingContent) - { - HeapBlock newData; - newData.allocate (newTotalBytes, clearExtraSpace || isClear); - - auto numSamplesToCopy = (size_t) jmin (newNumSamples, size); - - auto newChannels = reinterpret_cast (newData.get()); - auto newChan = reinterpret_cast (newData + channelListSize); - - for (int j = 0; j < newNumChannels; ++j) - { - newChannels[j] = newChan; - newChan += allocatedSamplesPerChannel; - } - - if (! isClear) - { - auto numChansToCopy = jmin (numChannels, newNumChannels); - - for (int i = 0; i < numChansToCopy; ++i) - FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy); - } - - allocatedData.swapWith (newData); - allocatedBytes = newTotalBytes; - channels = newChannels; - } - else - { - if (avoidReallocating && allocatedBytes >= newTotalBytes) - { - if (clearExtraSpace || isClear) - allocatedData.clear (newTotalBytes); - } - else - { - allocatedBytes = newTotalBytes; - allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); - channels = reinterpret_cast (allocatedData.get()); - } - - auto* chan = reinterpret_cast (allocatedData + channelListSize); - - for (int i = 0; i < newNumChannels; ++i) - { - channels[i] = chan; - chan += allocatedSamplesPerChannel; - } - } - - channels[newNumChannels] = 0; - size = newNumSamples; - numChannels = newNumChannels; - } - } - - /** Makes this buffer point to a pre-allocated set of channel data arrays. - - There's also a constructor that lets you specify arrays like this, but this - lets you change the channels dynamically. - - Note that if the buffer is resized or its number of channels is changed, it - will re-allocate memory internally and copy the existing data to this new area, - so it will then stop directly addressing this memory. - - @param dataToReferTo a pre-allocated array containing pointers to the data - for each channel that should be used by this buffer. The - buffer will only refer to this memory, it won't try to delete - it when the buffer is deleted or resized. - @param newNumChannels the number of channels to use - this must correspond to the - number of elements in the array passed in - @param newStartSample the offset within the arrays at which the data begins - @param newNumSamples the number of samples to use - this must correspond to the - size of the arrays passed in - */ - void setDataToReferTo (Type** dataToReferTo, - int newNumChannels, - int newStartSample, - int newNumSamples) - { - jassert (dataToReferTo != nullptr); - jassert (newNumChannels >= 0 && newNumSamples >= 0); - - if (allocatedBytes != 0) - { - allocatedBytes = 0; - allocatedData.free(); - } - - numChannels = newNumChannels; - size = newNumSamples; - - allocateChannels (dataToReferTo, newStartSample); - jassert (! isClear); - } - - /** Makes this buffer point to a pre-allocated set of channel data arrays. - - There's also a constructor that lets you specify arrays like this, but this - lets you change the channels dynamically. - - Note that if the buffer is resized or its number of channels is changed, it - will re-allocate memory internally and copy the existing data to this new area, - so it will then stop directly addressing this memory. - - @param dataToReferTo a pre-allocated array containing pointers to the data - for each channel that should be used by this buffer. The - buffer will only refer to this memory, it won't try to delete - it when the buffer is deleted or resized. - @param newNumChannels the number of channels to use - this must correspond to the - number of elements in the array passed in - @param newNumSamples the number of samples to use - this must correspond to the - size of the arrays passed in - */ - void setDataToReferTo (Type** dataToReferTo, - int newNumChannels, - int newNumSamples) - { - setDataToReferTo (dataToReferTo, newNumChannels, 0, newNumSamples); - } - - /** Resizes this buffer to match the given one, and copies all of its content across. - The source buffer can contain a different floating point type, so this can be used to - convert between 32 and 64 bit float buffer types. - */ - template - void makeCopyOf (const AudioBuffer& other, bool avoidReallocating = false) - { - setSize (other.getNumChannels(), other.getNumSamples(), false, false, avoidReallocating); - - if (other.hasBeenCleared()) - { - clear(); - } - else - { - isClear = false; - - for (int chan = 0; chan < numChannels; ++chan) - { - auto* dest = channels[chan]; - auto* src = other.getReadPointer (chan); - - for (int i = 0; i < size; ++i) - dest[i] = static_cast (src[i]); - } - } - } - - //============================================================================== - /** Clears all the samples in all channels. */ - void clear() noexcept - { - if (! isClear) - { - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::clear (channels[i], size); - - isClear = true; - } - } - - /** Clears a specified region of all the channels. - - For speed, this doesn't check whether the channel and sample number - are in-range, so be careful! - */ - void clear (int startSample, int numSamples) noexcept - { - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - if (! isClear) - { - if (startSample == 0 && numSamples == size) - isClear = true; - - for (int i = 0; i < numChannels; ++i) - FloatVectorOperations::clear (channels[i] + startSample, numSamples); - } - } - - /** Clears a specified region of just one channel. - - For speed, this doesn't check whether the channel and sample number - are in-range, so be careful! - */ - void clear (int channel, int startSample, int numSamples) noexcept - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - if (! isClear) - FloatVectorOperations::clear (channels[channel] + startSample, numSamples); - } - - /** Returns true if the buffer has been entirely cleared. - Note that this does not actually measure the contents of the buffer - it simply - returns a flag that is set when the buffer is cleared, and which is reset whenever - functions like getWritePointer() are invoked. That means the method does not take - any time, but it may return false negatives when in fact the buffer is still empty. - */ - bool hasBeenCleared() const noexcept { return isClear; } - - //============================================================================== - /** Returns a sample from the buffer. - The channel and index are not checked - they are expected to be in-range. If not, - an assertion will be thrown, but in a release build, you're into 'undefined behaviour' - territory. - */ - Type getSample (int channel, int sampleIndex) const noexcept - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (isPositiveAndBelow (sampleIndex, size)); - return *(channels[channel] + sampleIndex); - } - - /** Sets a sample in the buffer. - The channel and index are not checked - they are expected to be in-range. If not, - an assertion will be thrown, but in a release build, you're into 'undefined behaviour' - territory. - */ - void setSample (int destChannel, int destSample, Type newValue) noexcept - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (isPositiveAndBelow (destSample, size)); - *(channels[destChannel] + destSample) = newValue; - isClear = false; - } - - /** Adds a value to a sample in the buffer. - The channel and index are not checked - they are expected to be in-range. If not, - an assertion will be thrown, but in a release build, you're into 'undefined behaviour' - territory. - */ - void addSample (int destChannel, int destSample, Type valueToAdd) noexcept - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (isPositiveAndBelow (destSample, size)); - *(channels[destChannel] + destSample) += valueToAdd; - isClear = false; - } - - /** Applies a gain multiple to a region of one channel. - - For speed, this doesn't check whether the channel and sample number - are in-range, so be careful! - */ - void applyGain (int channel, int startSample, int numSamples, Type gain) noexcept - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - if (gain != (Type) 1 && ! isClear) - { - auto* d = channels[channel] + startSample; - - if (gain == 0) - FloatVectorOperations::clear (d, numSamples); - else - FloatVectorOperations::multiply (d, gain, numSamples); - } - } - - /** Applies a gain multiple to a region of all the channels. - - For speed, this doesn't check whether the sample numbers - are in-range, so be careful! - */ - void applyGain (int startSample, int numSamples, Type gain) noexcept - { - for (int i = 0; i < numChannels; ++i) - applyGain (i, startSample, numSamples, gain); - } - - /** Applies a gain multiple to all the audio data. */ - void applyGain (Type gain) noexcept - { - applyGain (0, size, gain); - } - - /** Applies a range of gains to a region of a channel. - - The gain that is applied to each sample will vary from - startGain on the first sample to endGain on the last Sample, - so it can be used to do basic fades. - - For speed, this doesn't check whether the sample numbers - are in-range, so be careful! - */ - void applyGainRamp (int channel, int startSample, int numSamples, - Type startGain, Type endGain) noexcept - { - if (! isClear) - { - if (startGain == endGain) - { - applyGain (channel, startSample, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - const auto increment = (endGain - startGain) / (float) numSamples; - auto* d = channels[channel] + startSample; - - while (--numSamples >= 0) - { - *d++ *= startGain; - startGain += increment; - } - } - } - } - - /** Applies a range of gains to a region of all channels. - - The gain that is applied to each sample will vary from - startGain on the first sample to endGain on the last Sample, - so it can be used to do basic fades. - - For speed, this doesn't check whether the sample numbers - are in-range, so be careful! - */ - void applyGainRamp (int startSample, int numSamples, - Type startGain, Type endGain) noexcept - { - for (int i = 0; i < numChannels; ++i) - applyGainRamp (i, startSample, numSamples, startGain, endGain); - } - - /** Adds samples from another buffer to this one. - - @param destChannel the channel within this buffer to add the samples to - @param destStartSample the start sample within this buffer's channel - @param source the source buffer to add from - @param sourceChannel the channel within the source buffer to read from - @param sourceStartSample the offset within the source buffer's channel to start reading samples from - @param numSamples the number of samples to process - @param gainToApplyToSource an optional gain to apply to the source samples before they are - added to this buffer's samples - - @see copyFrom - */ - void addFrom (int destChannel, - int destStartSample, - const AudioBuffer& source, - int sourceChannel, - int sourceStartSample, - int numSamples, - Type gainToApplyToSource = (Type) 1) noexcept - { - jassert (&source != this || sourceChannel != destChannel); - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); - jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); - - if (gainToApplyToSource != 0 && numSamples > 0 && ! source.isClear) - { - auto* d = channels[destChannel] + destStartSample; - auto* s = source.channels[sourceChannel] + sourceStartSample; - - if (isClear) - { - isClear = false; - - if (gainToApplyToSource != (Type) 1) - FloatVectorOperations::copyWithMultiply (d, s, gainToApplyToSource, numSamples); - else - FloatVectorOperations::copy (d, s, numSamples); - } - else - { - if (gainToApplyToSource != (Type) 1) - FloatVectorOperations::addWithMultiply (d, s, gainToApplyToSource, numSamples); - else - FloatVectorOperations::add (d, s, numSamples); - } - } - } - - - /** Adds samples from an array of floats to one of the channels. - - @param destChannel the channel within this buffer to add the samples to - @param destStartSample the start sample within this buffer's channel - @param source the source data to use - @param numSamples the number of samples to process - @param gainToApplyToSource an optional gain to apply to the source samples before they are - added to this buffer's samples - - @see copyFrom - */ - void addFrom (int destChannel, - int destStartSample, - const Type* source, - int numSamples, - Type gainToApplyToSource = (Type) 1) noexcept - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (source != nullptr); - - if (gainToApplyToSource != 0 && numSamples > 0) - { - auto* d = channels[destChannel] + destStartSample; - - if (isClear) - { - isClear = false; - - if (gainToApplyToSource != (Type) 1) - FloatVectorOperations::copyWithMultiply (d, source, gainToApplyToSource, numSamples); - else - FloatVectorOperations::copy (d, source, numSamples); - } - else - { - if (gainToApplyToSource != (Type) 1) - FloatVectorOperations::addWithMultiply (d, source, gainToApplyToSource, numSamples); - else - FloatVectorOperations::add (d, source, numSamples); - } - } - } - - - /** Adds samples from an array of floats, applying a gain ramp to them. - - @param destChannel the channel within this buffer to add the samples to - @param destStartSample the start sample within this buffer's channel - @param source the source data to use - @param numSamples the number of samples to process - @param startGain the gain to apply to the first sample (this is multiplied with - the source samples before they are added to this buffer) - @param endGain the gain to apply to the final sample. The gain is linearly - interpolated between the first and last samples. - */ - void addFromWithRamp (int destChannel, - int destStartSample, - const Type* source, - int numSamples, - Type startGain, - Type endGain) noexcept - { - if (startGain == endGain) - { - addFrom (destChannel, destStartSample, source, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (source != nullptr); - - if (numSamples > 0) - { - isClear = false; - const auto increment = (endGain - startGain) / numSamples; - auto* d = channels[destChannel] + destStartSample; - - while (--numSamples >= 0) - { - *d++ += startGain * *source++; - startGain += increment; - } - } - } - } - - /** Copies samples from another buffer to this one. - - @param destChannel the channel within this buffer to copy the samples to - @param destStartSample the start sample within this buffer's channel - @param source the source buffer to read from - @param sourceChannel the channel within the source buffer to read from - @param sourceStartSample the offset within the source buffer's channel to start reading samples from - @param numSamples the number of samples to process - - @see addFrom - */ - void copyFrom (int destChannel, - int destStartSample, - const AudioBuffer& source, - int sourceChannel, - int sourceStartSample, - int numSamples) noexcept - { - jassert (&source != this || sourceChannel != destChannel); - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && destStartSample + numSamples <= size); - jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); - jassert (sourceStartSample >= 0 && numSamples >= 0 && sourceStartSample + numSamples <= source.size); - - if (numSamples > 0) - { - if (source.isClear) - { - if (! isClear) - FloatVectorOperations::clear (channels[destChannel] + destStartSample, numSamples); - } - else - { - isClear = false; - FloatVectorOperations::copy (channels[destChannel] + destStartSample, - source.channels[sourceChannel] + sourceStartSample, - numSamples); - } - } - } - - /** Copies samples from an array of floats into one of the channels. - - @param destChannel the channel within this buffer to copy the samples to - @param destStartSample the start sample within this buffer's channel - @param source the source buffer to read from - @param numSamples the number of samples to process - - @see addFrom - */ - void copyFrom (int destChannel, - int destStartSample, - const Type* source, - int numSamples) noexcept - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (source != nullptr); - - if (numSamples > 0) - { - isClear = false; - FloatVectorOperations::copy (channels[destChannel] + destStartSample, source, numSamples); - } - } - - /** Copies samples from an array of floats into one of the channels, applying a gain to it. - - @param destChannel the channel within this buffer to copy the samples to - @param destStartSample the start sample within this buffer's channel - @param source the source buffer to read from - @param numSamples the number of samples to process - @param gain the gain to apply - - @see addFrom - */ - void copyFrom (int destChannel, - int destStartSample, - const Type* source, - int numSamples, - Type gain) noexcept - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (source != nullptr); - - if (numSamples > 0) - { - auto* d = channels[destChannel] + destStartSample; - - if (gain != (Type) 1) - { - if (gain == 0) - { - if (! isClear) - FloatVectorOperations::clear (d, numSamples); - } - else - { - isClear = false; - FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); - } - } - else - { - isClear = false; - FloatVectorOperations::copy (d, source, numSamples); - } - } - } - - /** Copies samples from an array of floats into one of the channels, applying a gain ramp. - - @param destChannel the channel within this buffer to copy the samples to - @param destStartSample the start sample within this buffer's channel - @param source the source buffer to read from - @param numSamples the number of samples to process - @param startGain the gain to apply to the first sample (this is multiplied with - the source samples before they are copied to this buffer) - @param endGain the gain to apply to the final sample. The gain is linearly - interpolated between the first and last samples. - - @see addFrom - */ - void copyFromWithRamp (int destChannel, - int destStartSample, - const Type* source, - int numSamples, - Type startGain, - Type endGain) noexcept - { - if (startGain == endGain) - { - copyFrom (destChannel, destStartSample, source, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (destChannel, numChannels)); - jassert (destStartSample >= 0 && numSamples >= 0 && destStartSample + numSamples <= size); - jassert (source != nullptr); - - if (numSamples > 0) - { - isClear = false; - const auto increment = (endGain - startGain) / numSamples; - auto* d = channels[destChannel] + destStartSample; - - while (--numSamples >= 0) - { - *d++ = startGain * *source++; - startGain += increment; - } - } - } - } - - /** Returns a Range indicating the lowest and highest sample values in a given section. - - @param channel the channel to read from - @param startSample the start sample within the channel - @param numSamples the number of samples to check - */ - Range findMinMax (int channel, int startSample, int numSamples) const noexcept - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - if (isClear) - return {}; - - return FloatVectorOperations::findMinAndMax (channels[channel] + startSample, numSamples); - } - - /** Finds the highest absolute sample value within a region of a channel. */ - Type getMagnitude (int channel, int startSample, int numSamples) const noexcept - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - if (isClear) - return {}; - - auto r = findMinMax (channel, startSample, numSamples); - - return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd()); - } - - /** Finds the highest absolute sample value within a region on all channels. */ - Type getMagnitude (int startSample, int numSamples) const noexcept - { - Type mag = 0; - - if (! isClear) - for (int i = 0; i < numChannels; ++i) - mag = jmax (mag, getMagnitude (i, startSample, numSamples)); - - return mag; - } - - /** Returns the root mean squared level for a region of a channel. */ - Type getRMSLevel (int channel, int startSample, int numSamples) const noexcept - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear) - return {}; - - auto* data = channels[channel] + startSample; - double sum = 0.0; - - for (int i = 0; i < numSamples; ++i) - { - const Type sample = data[i]; - sum += sample * sample; - } - - return (Type) std::sqrt (sum / numSamples); - } - - /** Reverses a part of a channel. */ - void reverse (int channel, int startSample, int numSamples) const noexcept - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); - - if (! isClear) - std::reverse (channels[channel] + startSample, - channels[channel] + startSample + numSamples); - } - - /** Reverses a part of the buffer. */ - void reverse (int startSample, int numSamples) const noexcept - { - for (int i = 0; i < numChannels; ++i) - reverse (i, startSample, numSamples); - } - - -private: - //============================================================================== - int numChannels, size; - size_t allocatedBytes; - Type** channels; - HeapBlock allocatedData; - Type* preallocatedChannelSpace[32]; - bool isClear; - - void allocateData() - { - jassert (size >= 0); - auto channelListSize = sizeof (Type*) * (size_t) (numChannels + 1); - allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32; - allocatedData.malloc (allocatedBytes); - channels = reinterpret_cast (allocatedData.get()); - auto* chan = (Type*) (allocatedData + channelListSize); - - for (int i = 0; i < numChannels; ++i) - { - channels[i] = chan; - chan += size; - } - - channels[numChannels] = nullptr; - isClear = false; - } - - void allocateChannels (Type* const* dataToReferTo, int offset) - { - jassert (offset >= 0); - - // (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools) - if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) - { - channels = static_cast (preallocatedChannelSpace); - } - else - { - allocatedData.malloc ((size_t) numChannels + 1, sizeof (Type*)); - channels = reinterpret_cast (allocatedData.get()); - } - - for (int i = 0; i < numChannels; ++i) - { - // you have to pass in the same number of valid pointers as numChannels - jassert (dataToReferTo[i] != nullptr); - - channels[i] = dataToReferTo[i] + offset; - } - - channels[numChannels] = nullptr; - isClear = false; - } - - JUCE_LEAK_DETECTOR (AudioBuffer) -}; - -//============================================================================== -/** - A multi-channel buffer of 32-bit floating point audio samples. - - This typedef is here for backwards compatibility with the older AudioSampleBuffer - class, which was fixed for 32-bit data, but is otherwise the same as the new - templated AudioBuffer class. - - @see AudioBuffer -*/ -typedef AudioBuffer AudioSampleBuffer; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp deleted file mode 100644 index 5523a4a2f..000000000 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ /dev/null @@ -1,1207 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -namespace FloatVectorHelpers -{ - #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); - #define JUCE_INCREMENT_SRC1_SRC2_DEST dest += (16 / sizeof (*dest)); src1 += (16 / sizeof (*dest)); src2 += (16 / sizeof (*dest)); - #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); - - #if JUCE_USE_SSE_INTRINSICS - inline static bool isAligned (const void* p) noexcept - { - return (((pointer_sized_int) p) & 15) == 0; - } - - struct BasicOps32 - { - typedef float Type; - typedef __m128 ParallelType; - typedef __m128 IntegerType; - enum { numParallel = 4 }; - - // Integer and parallel types are the same for SSE. On neon they have different types - static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } - static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } - - static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); } - static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); } - static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); } - static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_ps (dest, a); } - static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_ps (dest, a); } - - static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_ps (a, b); } - static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_ps (a, b); } - static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_ps (a, b); } - static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); } - static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); } - - static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_ps (a, b); } - static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_ps (a, b); } - static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_ps (a, b); } - static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_ps (a, b); } - - static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } - static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } - }; - - struct BasicOps64 - { - typedef double Type; - typedef __m128d ParallelType; - typedef __m128d IntegerType; - enum { numParallel = 2 }; - - // Integer and parallel types are the same for SSE. On neon they have different types - static forcedinline IntegerType toint (ParallelType v) noexcept { return v; } - static forcedinline ParallelType toflt (IntegerType v) noexcept { return v; } - - static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); } - static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); } - static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); } - static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_pd (dest, a); } - static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_pd (dest, a); } - - static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_pd (a, b); } - static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_pd (a, b); } - static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_pd (a, b); } - static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); } - static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); } - - static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return _mm_and_pd (a, b); } - static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return _mm_andnot_pd (a, b); } - static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return _mm_or_pd (a, b); } - static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return _mm_xor_pd (a, b); } - - static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); } - static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); } - }; - - - - #define JUCE_BEGIN_VEC_OP \ - typedef FloatVectorHelpers::ModeType::Mode Mode; \ - { \ - const int numLongOps = num / Mode::numParallel; - - #define JUCE_FINISH_VEC_OP(normalOp) \ - num &= (Mode::numParallel - 1); \ - if (num == 0) return; \ - } \ - for (int i = 0; i < num; ++i) normalOp; - - #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - if (FloatVectorHelpers::isAligned (dest)) JUCE_VEC_LOOP (vecOp, dummy, Mode::loadA, Mode::storeA, locals, JUCE_INCREMENT_DEST) \ - else JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ - JUCE_FINISH_VEC_OP (normalOp) - - #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - if (FloatVectorHelpers::isAligned (dest)) \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ - else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ - }\ - else \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ - else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ - } \ - JUCE_FINISH_VEC_OP (normalOp) - - #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - if (FloatVectorHelpers::isAligned (dest)) \ - { \ - if (FloatVectorHelpers::isAligned (src1)) \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeA, locals, increment) \ - } \ - else \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeA, locals, increment) \ - } \ - } \ - else \ - { \ - if (FloatVectorHelpers::isAligned (src1)) \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadA, Mode::storeU, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ - } \ - else \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadA, Mode::storeU, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ - } \ - } \ - JUCE_FINISH_VEC_OP (normalOp) - - #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - if (FloatVectorHelpers::isAligned (dest)) \ - { \ - if (FloatVectorHelpers::isAligned (src1)) \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ - } \ - else \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ - } \ - } \ - else \ - { \ - if (FloatVectorHelpers::isAligned (src1)) \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadA, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ - } \ - else \ - { \ - if (FloatVectorHelpers::isAligned (src2)) JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ - else JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ - } \ - } \ - JUCE_FINISH_VEC_OP (normalOp) - - - //============================================================================== - #elif JUCE_USE_ARM_NEON - - struct BasicOps32 - { - typedef float Type; - typedef float32x4_t ParallelType; - typedef uint32x4_t IntegerType; - union signMaskUnion { ParallelType f; IntegerType i; }; - enum { numParallel = 4 }; - - static forcedinline IntegerType toint (ParallelType v) noexcept { signMaskUnion u; u.f = v; return u.i; } - static forcedinline ParallelType toflt (IntegerType v) noexcept { signMaskUnion u; u.i = v; return u.f; } - - static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } - static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); } - static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); } - static forcedinline void storeA (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); } - static forcedinline void storeU (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); } - - static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return vaddq_f32 (a, b); } - static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return vsubq_f32 (a, b); } - static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return vmulq_f32 (a, b); } - static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); } - static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); } - - static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (vandq_u32 (toint (a), toint (b))); } - static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt (vbicq_u32 (toint (a), toint (b))); } - static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (vorrq_u32 (toint (a), toint (b))); } - static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (veorq_u32 (toint (a), toint (b))); } - - static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } - static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } - }; - - struct BasicOps64 - { - typedef double Type; - typedef double ParallelType; - typedef uint64 IntegerType; - union signMaskUnion { ParallelType f; IntegerType i; }; - enum { numParallel = 1 }; - - static forcedinline IntegerType toint (ParallelType v) noexcept { signMaskUnion u; u.f = v; return u.i; } - static forcedinline ParallelType toflt (IntegerType v) noexcept { signMaskUnion u; u.i = v; return u.f; } - - static forcedinline ParallelType load1 (Type v) noexcept { return v; } - static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; } - static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; } - static forcedinline void storeA (Type* dest, ParallelType a) noexcept { *dest = a; } - static forcedinline void storeU (Type* dest, ParallelType a) noexcept { *dest = a; } - - static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return a + b; } - static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return a - b; } - static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return a * b; } - static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); } - static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); } - - static forcedinline ParallelType bit_and (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) & toint (b)); } - static forcedinline ParallelType bit_not (ParallelType a, ParallelType b) noexcept { return toflt ((~toint (a)) & toint (b)); } - static forcedinline ParallelType bit_or (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) | toint (b)); } - static forcedinline ParallelType bit_xor (ParallelType a, ParallelType b) noexcept { return toflt (toint (a) ^ toint (b)); } - - static forcedinline Type max (ParallelType a) noexcept { return a; } - static forcedinline Type min (ParallelType a) noexcept { return a; } - }; - - #define JUCE_BEGIN_VEC_OP \ - typedef FloatVectorHelpers::ModeType::Mode Mode; \ - if (Mode::numParallel > 1) \ - { \ - const int numLongOps = num / Mode::numParallel; - - #define JUCE_FINISH_VEC_OP(normalOp) \ - num &= (Mode::numParallel - 1); \ - if (num == 0) return; \ - } \ - for (int i = 0; i < num; ++i) normalOp; - - #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ - JUCE_FINISH_VEC_OP (normalOp) - - #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ - JUCE_FINISH_VEC_OP (normalOp) - - #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - JUCE_VEC_LOOP_TWO_SOURCES (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ - JUCE_FINISH_VEC_OP (normalOp) - - #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ - JUCE_BEGIN_VEC_OP \ - setupOp \ - JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD (vecOp, Mode::loadU, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ - JUCE_FINISH_VEC_OP (normalOp) - - - //============================================================================== - #else - #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; - - #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; - - #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; - - #define JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST(normalOp, vecOp, locals, increment, setupOp) \ - for (int i = 0; i < num; ++i) normalOp; - - #endif - - //============================================================================== - #define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ - { \ - locals (srcLoad, dstLoad); \ - dstStore (dest, vecOp); \ - increment; \ - } - - #define JUCE_VEC_LOOP_TWO_SOURCES(vecOp, src1Load, src2Load, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ - { \ - locals (src1Load, src2Load); \ - dstStore (dest, vecOp); \ - increment; \ - } - - #define JUCE_VEC_LOOP_TWO_SOURCES_WITH_DEST_LOAD(vecOp, src1Load, src2Load, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ - { \ - locals (src1Load, src2Load, dstLoad); \ - dstStore (dest, vecOp); \ - increment; \ - } - - #define JUCE_LOAD_NONE(srcLoad, dstLoad) - #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); - #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); - #define JUCE_LOAD_SRC1_SRC2(src1Load, src2Load) const Mode::ParallelType s1 = src1Load (src1), s2 = src2Load (src2); - #define JUCE_LOAD_SRC1_SRC2_DEST(src1Load, src2Load, dstLoad) const Mode::ParallelType d = dstLoad (dest), s1 = src1Load (src1), s2 = src2Load (src2); - #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); - - union signMask32 { float f; uint32 i; }; - union signMask64 { double d; uint64 i; }; - - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - template struct ModeType { typedef BasicOps32 Mode; }; - template<> struct ModeType<8> { typedef BasicOps64 Mode; }; - - template - struct MinMax - { - typedef typename Mode::Type Type; - typedef typename Mode::ParallelType ParallelType; - - static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept - { - int numLongOps = num / Mode::numParallel; - - if (numLongOps > 1) - { - ParallelType val; - - #if ! JUCE_USE_ARM_NEON - if (isAligned (src)) - { - val = Mode::loadA (src); - - if (isMinimum) - { - while (--numLongOps > 0) - { - src += Mode::numParallel; - val = Mode::min (val, Mode::loadA (src)); - } - } - else - { - while (--numLongOps > 0) - { - src += Mode::numParallel; - val = Mode::max (val, Mode::loadA (src)); - } - } - } - else - #endif - { - val = Mode::loadU (src); - - if (isMinimum) - { - while (--numLongOps > 0) - { - src += Mode::numParallel; - val = Mode::min (val, Mode::loadU (src)); - } - } - else - { - while (--numLongOps > 0) - { - src += Mode::numParallel; - val = Mode::max (val, Mode::loadU (src)); - } - } - } - - Type result = isMinimum ? Mode::min (val) - : Mode::max (val); - - num &= (Mode::numParallel - 1); - src += Mode::numParallel; - - for (int i = 0; i < num; ++i) - result = isMinimum ? jmin (result, src[i]) - : jmax (result, src[i]); - - return result; - } - - return isMinimum ? juce::findMinimum (src, num) - : juce::findMaximum (src, num); - } - - static Range findMinAndMax (const Type* src, int num) noexcept - { - int numLongOps = num / Mode::numParallel; - - if (numLongOps > 1) - { - ParallelType mn, mx; - - #if ! JUCE_USE_ARM_NEON - if (isAligned (src)) - { - mn = Mode::loadA (src); - mx = mn; - - while (--numLongOps > 0) - { - src += Mode::numParallel; - const ParallelType v = Mode::loadA (src); - mn = Mode::min (mn, v); - mx = Mode::max (mx, v); - } - } - else - #endif - { - mn = Mode::loadU (src); - mx = mn; - - while (--numLongOps > 0) - { - src += Mode::numParallel; - const ParallelType v = Mode::loadU (src); - mn = Mode::min (mn, v); - mx = Mode::max (mx, v); - } - } - - Range result (Mode::min (mn), - Mode::max (mx)); - - num &= (Mode::numParallel - 1); - src += Mode::numParallel; - - for (int i = 0; i < num; ++i) - result = result.getUnionWith (src[i]); - - return result; - } - - return Range::findMinAndMax (src, num); - } - }; - #endif -} - -//============================================================================== -namespace -{ - #if JUCE_USE_VDSP_FRAMEWORK - // This casts away constness to account for slightly different vDSP function signatures - // in OSX 10.8 SDK and below. Can be safely removed once those SDKs are obsolete. - template - ValueType* osx108sdkCompatibilityCast (const ValueType* arg) noexcept { return const_cast (arg); } - #endif -} - -//============================================================================== -void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclr (dest, 1, (size_t) num); - #else - zeromem (dest, (size_t) num * sizeof (float)); - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclrD (dest, 1, (size_t) num); - #else - zeromem (dest, (size_t) num * sizeof (double)); - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vfill (&valueToFill, dest, 1, (size_t) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, - const Mode::ParallelType val = Mode::load1 (valueToFill);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vfillD (&valueToFill, dest, 1, (size_t) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, - const Mode::ParallelType val = Mode::load1 (valueToFill);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, int num) noexcept -{ - memcpy (dest, src, (size_t) num * sizeof (float)); -} - -void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept -{ - memcpy (dest, src, (size_t) num * sizeof (double)); -} - -void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmul (src, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmulD (src, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsadd (dest, 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, - const Mode::ParallelType amountToAdd = Mode::load1 (amount);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, - const Mode::ParallelType amountToAdd = Mode::load1 (amount);) -} - -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, float amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsadd (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType am = Mode::load1 (amount);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, double amount, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsaddD (osx108sdkCompatibilityCast (src), 1, &amount, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] + amount, Mode::add (am, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType am = Mode::load1 (amount);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vadd (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vaddD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vadd (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vaddD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] + src2[i], Mode::add (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsub (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsubD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsub (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsubD (src2, 1, src1, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] - src2[i], Mode::sub (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsma (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmaD (src, 1, &multiplier, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vma ((float*) src1, 1, (float*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmaD ((double*) src1, 1, (double*) src2, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] += src1[i] * src2[i], Mode::add (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, Mode::sub (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) -} - -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i] * multiplier, Mode::sub (d, Mode::mul (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) -} - -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], Mode::sub (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) -} - -void JUCE_CALLTYPE FloatVectorOperations::subtractWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST_DEST (dest[i] -= src1[i] * src2[i], Mode::sub (d, Mode::mul (s1, s2)), - JUCE_LOAD_SRC1_SRC2_DEST, - JUCE_INCREMENT_SRC1_SRC2_DEST, ) -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmul (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmulD (src, 1, dest, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmul (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmulD (src1, 1, src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = src1[i] * src2[i], Mode::mul (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmul (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vsmulD (dest, 1, &multiplier, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, float multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) -} - -void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, double multiplier, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) -} - -void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); - #else - copyWithMultiply (dest, src, -1.0f, num); - #endif -} - -void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); - #else - copyWithMultiply (dest, src, -1.0f, num); - #endif -} - -void FloatVectorOperations::abs (float* dest, const float* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vabs ((float*) src, 1, dest, 1, (vDSP_Length) num); - #else - FloatVectorHelpers::signMask32 signMask; - signMask.i = 0x7fffffffUL; - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = fabsf (src[i]), Mode::bit_and (s, mask), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mask = Mode::load1 (signMask.f);) - - ignoreUnused (signMask); - #endif -} - -void FloatVectorOperations::abs (double* dest, const double* src, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vabsD ((double*) src, 1, dest, 1, (vDSP_Length) num); - #else - FloatVectorHelpers::signMask64 signMask; - signMask.i = 0x7fffffffffffffffULL; - - 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 -} - -void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept -{ - #if JUCE_USE_ARM_NEON - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, - vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, ) - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, - Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType mult = Mode::load1 (multiplier);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src, float comp, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) -} - -void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src, double comp, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmin (src[i], comp), Mode::min (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) -} - -void JUCE_CALLTYPE FloatVectorOperations::min (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmin ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::min (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vminD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmin (src1[i], src2[i]), Mode::min (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src, float comp, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) -} - -void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src, double comp, int num) noexcept -{ - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (src[i], comp), Mode::max (s, cmp), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType cmp = Mode::load1 (comp);) -} - -void JUCE_CALLTYPE FloatVectorOperations::max (float* dest, const float* src1, const float* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmax ((float*) src1, 1, (float*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::max (double* dest, const double* src1, const double* src2, int num) noexcept -{ - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vmaxD ((double*) src1, 1, (double*) src2, 1, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC1_SRC2_DEST (dest[i] = jmax (src1[i], src2[i]), Mode::max (s1, s2), JUCE_LOAD_SRC1_SRC2, JUCE_INCREMENT_SRC1_SRC2_DEST, ) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::clip (float* dest, const float* src, float low, float high, int num) noexcept -{ - jassert(high >= low); - - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclip ((float*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::clip (double* dest, const double* src, double low, double high, int num) noexcept -{ - jassert(high >= low); - - #if JUCE_USE_VDSP_FRAMEWORK - vDSP_vclipD ((double*) src, 1, &low, &high, dest, 1, (vDSP_Length) num); - #else - JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = jmax (jmin (src[i], high), low), Mode::max (Mode::min (s, hi), lo), - JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, - const Mode::ParallelType lo = Mode::load1 (low); const Mode::ParallelType hi = Mode::load1 (high);) - #endif -} - -Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept -{ - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinAndMax (src, num); - #else - return Range::findMinAndMax (src, num); - #endif -} - -Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept -{ - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinAndMax (src, num); - #else - return Range::findMinAndMax (src, num); - #endif -} - -float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept -{ - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); - #else - return juce::findMinimum (src, num); - #endif -} - -double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept -{ - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); - #else - return juce::findMinimum (src, num); - #endif -} - -float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept -{ - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); - #else - return juce::findMaximum (src, num); - #endif -} - -double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept -{ - #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON - return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); - #else - return juce::findMaximum (src, num); - #endif -} - -void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept -{ - #if JUCE_USE_SSE_INTRINSICS - _MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); - #endif - ignoreUnused (shouldEnable); -} - -void JUCE_CALLTYPE FloatVectorOperations::disableDenormalisedNumberSupport() noexcept -{ - #if JUCE_USE_SSE_INTRINSICS - const unsigned int mxcsr = _mm_getcsr(); - _mm_setcsr (mxcsr | 0x8040); // add the DAZ and FZ bits - #endif -} - -//============================================================================== -//============================================================================== -#if JUCE_UNIT_TESTS - -class FloatVectorOperationsTests : public UnitTest -{ -public: - FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations", "Audio") {} - - template - struct TestRunner - { - static void runTest (UnitTest& u, Random random) - { - const int range = random.nextBool() ? 500 : 10; - const int num = random.nextInt (range) + 1; - - HeapBlock buffer1 ((size_t) num + 16), buffer2 ((size_t) num + 16); - HeapBlock buffer3 ((size_t) num + 16); - - #if JUCE_ARM - ValueType* const data1 = buffer1; - ValueType* const data2 = buffer2; - int* const int1 = buffer3; - #else - // These tests deliberately operate on misaligned memory and will be flagged up by - // checks for undefined behavior! - ValueType* const data1 = addBytesToPointer (buffer1.get(), random.nextInt (16)); - ValueType* const data2 = addBytesToPointer (buffer2.get(), random.nextInt (16)); - int* const int1 = addBytesToPointer (buffer3.get(), random.nextInt (16)); - #endif - - fillRandomly (random, data1, num); - fillRandomly (random, data2, num); - - Range minMax1 (FloatVectorOperations::findMinAndMax (data1, num)); - Range minMax2 (Range::findMinAndMax (data1, num)); - u.expect (minMax1 == minMax2); - - u.expect (valuesMatch (FloatVectorOperations::findMinimum (data1, num), juce::findMinimum (data1, num))); - u.expect (valuesMatch (FloatVectorOperations::findMaximum (data1, num), juce::findMaximum (data1, num))); - - u.expect (valuesMatch (FloatVectorOperations::findMinimum (data2, num), juce::findMinimum (data2, num))); - u.expect (valuesMatch (FloatVectorOperations::findMaximum (data2, num), juce::findMaximum (data2, num))); - - FloatVectorOperations::clear (data1, num); - u.expect (areAllValuesEqual (data1, num, 0)); - - FloatVectorOperations::fill (data1, (ValueType) 2, num); - u.expect (areAllValuesEqual (data1, num, (ValueType) 2)); - - FloatVectorOperations::add (data1, (ValueType) 2, num); - u.expect (areAllValuesEqual (data1, num, (ValueType) 4)); - - FloatVectorOperations::copy (data2, data1, num); - u.expect (areAllValuesEqual (data2, num, (ValueType) 4)); - - FloatVectorOperations::add (data2, data1, num); - u.expect (areAllValuesEqual (data2, num, (ValueType) 8)); - - FloatVectorOperations::copyWithMultiply (data2, data1, (ValueType) 4, num); - u.expect (areAllValuesEqual (data2, num, (ValueType) 16)); - - FloatVectorOperations::addWithMultiply (data2, data1, (ValueType) 4, num); - u.expect (areAllValuesEqual (data2, num, (ValueType) 32)); - - FloatVectorOperations::multiply (data1, (ValueType) 2, num); - u.expect (areAllValuesEqual (data1, num, (ValueType) 8)); - - FloatVectorOperations::multiply (data1, data2, num); - u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); - - FloatVectorOperations::negate (data2, data1, num); - u.expect (areAllValuesEqual (data2, num, (ValueType) -256)); - - FloatVectorOperations::subtract (data1, data2, num); - u.expect (areAllValuesEqual (data1, num, (ValueType) 512)); - - FloatVectorOperations::abs (data1, data2, num); - u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); - - FloatVectorOperations::abs (data2, data1, num); - u.expect (areAllValuesEqual (data2, num, (ValueType) 256)); - - fillRandomly (random, int1, num); - doConversionTest (u, data1, data2, int1, num); - - FloatVectorOperations::fill (data1, (ValueType) 2, num); - FloatVectorOperations::fill (data2, (ValueType) 3, num); - FloatVectorOperations::addWithMultiply (data1, data1, data2, num); - u.expect (areAllValuesEqual (data1, num, (ValueType) 8)); - } - - static void doConversionTest (UnitTest& u, float* data1, float* data2, int* const int1, int num) - { - FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num); - convertFixed (data2, int1, 2.0f, num); - u.expect (buffersMatch (data1, data2, num)); - } - - static void doConversionTest (UnitTest&, double*, double*, int*, int) {} - - static void fillRandomly (Random& random, ValueType* d, int num) - { - while (--num >= 0) - *d++ = (ValueType) (random.nextDouble() * 1000.0); - } - - static void fillRandomly (Random& random, int* d, int num) - { - while (--num >= 0) - *d++ = random.nextInt(); - } - - static void convertFixed (float* d, const int* s, ValueType multiplier, int num) - { - while (--num >= 0) - *d++ = (float) *s++ * multiplier; - } - - static bool areAllValuesEqual (const ValueType* d, int num, ValueType target) - { - while (--num >= 0) - if (*d++ != target) - return false; - - return true; - } - - static bool buffersMatch (const ValueType* d1, const ValueType* d2, int num) - { - while (--num >= 0) - if (! valuesMatch (*d1++, *d2++)) - return false; - - return true; - } - - static bool valuesMatch (ValueType v1, ValueType v2) - { - return std::abs (v1 - v2) < std::numeric_limits::epsilon(); - } - }; - - void runTest() override - { - beginTest ("FloatVectorOperations"); - - for (int i = 1000; --i >= 0;) - { - TestRunner::runTest (*this, getRandom()); - TestRunner::runTest (*this, getRandom()); - } - } -}; - -static FloatVectorOperationsTests vectorOpTests; - -#endif - -} // namespace juce diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h deleted file mode 100644 index c44bbd800..000000000 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -#if JUCE_INTEL - #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8f || n > 1.0e-8f)) n = 0; -#else - #define JUCE_SNAP_TO_ZERO(n) ignoreUnused (n) -#endif - -//============================================================================== -/** - A collection of simple vector operations on arrays of floats, accelerated with - SIMD instructions where possible. -*/ -class JUCE_API FloatVectorOperations -{ -public: - //============================================================================== - /** Clears a vector of floats. */ - static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept; - - /** Clears a vector of doubles. */ - static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept; - - /** Copies a repeated value into a vector of floats. */ - static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept; - - /** Copies a repeated value into a vector of doubles. */ - static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept; - - /** Copies a vector of floats. */ - static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept; - - /** Copies a vector of doubles. */ - static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept; - - /** Copies a vector of floats, multiplying each value by a given multiplier */ - static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; - - /** Copies a vector of doubles, multiplying each value by a given multiplier */ - static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; - - /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept; - - /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept; - - /** Adds a fixed value to each source value and stores it in the destination array. */ - static void JUCE_CALLTYPE add (float* dest, const float* src, float amount, int numValues) noexcept; - - /** Adds a fixed value to each source value and stores it in the destination array. */ - static void JUCE_CALLTYPE add (double* dest, const double* src, double amount, int numValues) noexcept; - - /** Adds the source values to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; - - /** Adds the source values to the destination values. */ - static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept; - - /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE add (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Adds each source1 value to the corresponding source2 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE add (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Subtracts the source values from the destination values. */ - static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; - - /** Subtracts the source values from the destination values. */ - static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept; - - /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE subtract (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Subtracts each source2 value from the corresponding source1 value and stores the result in the destination array. */ - static void JUCE_CALLTYPE subtract (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; - - /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then adds it to the destination value. */ - static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; - - /** Multiplies each source value by the given multiplier, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Multiplies each source1 value by the corresponding source2 value, then subtracts it to the destination value. */ - static void JUCE_CALLTYPE subtractWithMultiply (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Multiplies the destination values by the source values. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept; - - /** Multiplies the destination values by the source values. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept; - - /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src1, const float* src2, int numValues) noexcept; - - /** Multiplies each source1 value by the correspinding source2 value, then stores it in the destination array. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src1, const double* src2, int numValues) noexcept; - - /** Multiplies each of the destination values by a fixed multiplier. */ - static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; - - /** Multiplies each of the destination values by a fixed multiplier. */ - static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept; - - /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ - static void JUCE_CALLTYPE multiply (float* dest, const float* src, float multiplier, int num) noexcept; - - /** Multiplies each of the source values by a fixed multiplier and stores the result in the destination array. */ - static void JUCE_CALLTYPE multiply (double* dest, const double* src, double multiplier, int num) noexcept; - - /** Copies a source vector to a destination, negating each value. */ - static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; - - /** Copies a source vector to a destination, negating each value. */ - static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; - - /** Copies a source vector to a destination, taking the absolute of each value. */ - static void JUCE_CALLTYPE abs (float* dest, const float* src, int numValues) noexcept; - - /** Copies a source vector to a destination, taking the absolute of each value. */ - static void JUCE_CALLTYPE abs (double* dest, const double* src, int numValues) noexcept; - - /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ - static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; - - /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE min (float* dest, const float* src, float comp, int num) noexcept; - - /** Each element of dest will be the minimum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE min (double* dest, const double* src, double comp, int num) noexcept; - - /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE min (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Each element of dest will be the minimum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE min (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE max (float* dest, const float* src, float comp, int num) noexcept; - - /** Each element of dest will be the maximum of the corresponding element of the source array and the given comp value. */ - static void JUCE_CALLTYPE max (double* dest, const double* src, double comp, int num) noexcept; - - /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE max (float* dest, const float* src1, const float* src2, int num) noexcept; - - /** Each element of dest will be the maximum of the corresponding source1 and source2 values. */ - static void JUCE_CALLTYPE max (double* dest, const double* src1, const double* src2, int num) noexcept; - - /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ - static void JUCE_CALLTYPE clip (float* dest, const float* src, float low, float high, int num) noexcept; - - /** Each element of dest is calculated by hard clipping the corresponding src element so that it is in the range specified by the arguments low and high. */ - static void JUCE_CALLTYPE clip (double* dest, const double* src, double low, double high, int num) noexcept; - - /** Finds the miniumum and maximum values in the given array. */ - static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; - - /** Finds the miniumum and maximum values in the given array. */ - static Range JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept; - - /** Finds the miniumum value in the given array. */ - static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept; - - /** Finds the miniumum value in the given array. */ - static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept; - - /** Finds the maximum value in the given array. */ - static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept; - - /** Finds the maximum value in the given array. */ - static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept; - - /** On Intel CPUs, this method enables or disables the SSE flush-to-zero mode. - Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE - */ - static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; - - /** On Intel CPUs, this method enables the SSE flush-to-zero and denormalised-are-zero modes. - This effectively sets the DAZ and FZ bits of the MXCSR register. It's a convenient thing to - call before audio processing code where you really want to avoid denormalisation performance hits. - */ - static void JUCE_CALLTYPE disableDenormalisedNumberSupport() noexcept; -}; - -//============================================================================== -/** - Helper class providing an RAII-based mechanism for temporarily disabling - denormals on your CPU. -*/ -class ScopedNoDenormals -{ -public: - inline ScopedNoDenormals() noexcept - { - #if JUCE_USE_SSE_INTRINSICS - mxcsr = _mm_getcsr(); - _mm_setcsr (mxcsr | 0x8040); // add the DAZ and FZ bits - #endif - } - - - inline ~ScopedNoDenormals() noexcept - { - #if JUCE_USE_SSE_INTRINSICS - _mm_setcsr (mxcsr); - #endif - } - -private: - #if JUCE_USE_SSE_INTRINSICS - unsigned int mxcsr; - #endif -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.cpp b/source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.cpp deleted file mode 100644 index f38b66f95..000000000 --- a/source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -struct CatmullRomAlgorithm -{ - static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept - { - auto y0 = inputs[3]; - auto y1 = inputs[2]; - auto y2 = inputs[1]; - auto y3 = inputs[0]; - - auto halfY0 = 0.5f * y0; - auto halfY3 = 0.5f * y3; - - return y1 + offset * ((0.5f * y2 - halfY0) - + (offset * (((y0 + 2.0f * y2) - (halfY3 + 2.5f * y1)) - + (offset * ((halfY3 + 1.5f * y1) - (halfY0 + 1.5f * y2)))))); - } -}; - -CatmullRomInterpolator::CatmullRomInterpolator() noexcept { reset(); } -CatmullRomInterpolator::~CatmullRomInterpolator() noexcept {} - -void CatmullRomInterpolator::reset() noexcept -{ - subSamplePos = 1.0; - - for (auto& s : lastInputSamples) - s = 0; -} - -int CatmullRomInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept -{ - return interpolate (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); -} - -int CatmullRomInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept -{ - return interpolateAdding (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.h b/source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.h deleted file mode 100644 index 3876ae612..000000000 --- a/source/modules/juce_audio_basics/effects/juce_CatmullRomInterpolator.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -/** - Interpolator for resampling a stream of floats using Catmull-Rom interpolation. - - Note that the resampler is stateful, so when there's a break in the continuity - of the input stream you're feeding it, you should call reset() before feeding - it any new data. And like with any other stateful filter, if you're resampling - multiple channels, make sure each one uses its own CatmullRomInterpolator - object. - - @see LagrangeInterpolator -*/ -class JUCE_API CatmullRomInterpolator -{ -public: - CatmullRomInterpolator() noexcept; - ~CatmullRomInterpolator() noexcept; - - /** Resets the state of the interpolator. - Call this when there's a break in the continuity of the input data stream. - */ - void reset() noexcept; - - /** Resamples a stream of samples. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results into - @param numOutputSamplesToProduce the number of output samples that should be created - - @returns the actual number of input samples that were used - */ - int process (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce) noexcept; - - /** Resamples a stream of samples, adding the results to the output data - with a gain. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results to - the result values will be added - to any pre-existing data in this buffer after being multiplied by - the gain factor - @param numOutputSamplesToProduce the number of output samples that should be created - @param gain a gain factor to multiply the resulting samples by before - adding them to the destination buffer - - @returns the actual number of input samples that were used - */ - int processAdding (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - float gain) noexcept; - -private: - float lastInputSamples[5]; - double subSamplePos; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CatmullRomInterpolator) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_Decibels.h b/source/modules/juce_audio_basics/effects/juce_Decibels.h deleted file mode 100644 index 4fc2d2fdb..000000000 --- a/source/modules/juce_audio_basics/effects/juce_Decibels.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - This class contains some helpful static methods for dealing with decibel values. -*/ -class Decibels -{ -public: - //============================================================================== - /** Converts a dBFS value to its equivalent gain level. - - A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. Any - decibel value lower than minusInfinityDb will return a gain of 0. - */ - template - static Type decibelsToGain (const Type decibels, - const Type minusInfinityDb = (Type) defaultMinusInfinitydB) - { - return decibels > minusInfinityDb ? std::pow ((Type) 10.0, decibels * (Type) 0.05) - : Type(); - } - - /** Converts a gain level into a dBFS value. - - A gain of 1.0 = 0 dB, and lower gains map onto negative decibel values. - If the gain is 0 (or negative), then the method will return the value - provided as minusInfinityDb. - */ - template - static Type gainToDecibels (const Type gain, - const Type minusInfinityDb = (Type) defaultMinusInfinitydB) - { - return gain > Type() ? jmax (minusInfinityDb, (Type) std::log10 (gain) * (Type) 20.0) - : minusInfinityDb; - } - - //============================================================================== - /** Converts a decibel reading to a string, with the 'dB' suffix. - If the decibel value is lower than minusInfinityDb, the return value will - be "-INF dB". - */ - template - static String toString (const Type decibels, - const int decimalPlaces = 2, - const Type minusInfinityDb = (Type) defaultMinusInfinitydB) - { - String s; - - if (decibels <= minusInfinityDb) - { - s = "-INF dB"; - } - else - { - if (decibels >= Type()) - s << '+'; - - s << String (decibels, decimalPlaces) << " dB"; - } - - return s; - } - - -private: - //============================================================================== - enum - { - defaultMinusInfinitydB = -100 - }; - - Decibels(); // This class can't be instantiated, it's just a holder for static methods.. - JUCE_DECLARE_NON_COPYABLE (Decibels) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp b/source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp deleted file mode 100644 index 01338c8d9..000000000 --- a/source/modules/juce_audio_basics/effects/juce_IIRFilter.cpp +++ /dev/null @@ -1,341 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -IIRCoefficients::IIRCoefficients() noexcept -{ - zeromem (coefficients, sizeof (coefficients)); -} - -IIRCoefficients::~IIRCoefficients() noexcept {} - -IIRCoefficients::IIRCoefficients (const IIRCoefficients& other) noexcept -{ - memcpy (coefficients, other.coefficients, sizeof (coefficients)); -} - -IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexcept -{ - memcpy (coefficients, other.coefficients, sizeof (coefficients)); - return *this; -} - -IIRCoefficients::IIRCoefficients (double c1, double c2, double c3, - double c4, double c5, double c6) noexcept -{ - const double a = 1.0 / c4; - - coefficients[0] = (float) (c1 * a); - coefficients[1] = (float) (c2 * a); - coefficients[2] = (float) (c3 * a); - coefficients[3] = (float) (c5 * a); - coefficients[4] = (float) (c6 * a); -} - -IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, - const double frequency) noexcept -{ - return makeLowPass (sampleRate, frequency, 1.0 / std::sqrt (2.0)); -} - -IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, - const double frequency, - const double Q) noexcept -{ - jassert (sampleRate > 0.0); - jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); - - return IIRCoefficients (c1, - c1 * 2.0, - c1, - 1.0, - c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - 1.0 / Q * n + nSquared)); -} - -IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate, - const double frequency) noexcept -{ - return makeHighPass (sampleRate, frequency, 1.0 / std::sqrt(2.0)); -} - -IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate, - const double frequency, - const double Q) noexcept -{ - jassert (sampleRate > 0.0); - jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double n = std::tan (double_Pi * frequency / sampleRate); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); - - return IIRCoefficients (c1, - c1 * -2.0, - c1, - 1.0, - c1 * 2.0 * (nSquared - 1.0), - c1 * (1.0 - 1.0 / Q * n + nSquared)); -} - -IIRCoefficients IIRCoefficients::makeBandPass (const double sampleRate, - const double frequency) noexcept -{ - return makeBandPass (sampleRate, frequency, 1.0 / std::sqrt (2.0)); -} - -IIRCoefficients IIRCoefficients::makeBandPass (const double sampleRate, - const double frequency, - const double Q) noexcept -{ - jassert (sampleRate > 0.0); - jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); - - return IIRCoefficients (c1 * n / Q, - 0.0, - -c1 * n / Q, - 1.0, - c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - 1.0 / Q * n + nSquared)); -} - -IIRCoefficients IIRCoefficients::makeNotchFilter (const double sampleRate, - const double frequency) noexcept -{ - return makeNotchFilter (sampleRate, frequency, 1.0 / std::sqrt (2.0)); -} - -IIRCoefficients IIRCoefficients::makeNotchFilter (const double sampleRate, - const double frequency, - const double Q) noexcept -{ - jassert (sampleRate > 0.0); - jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + n / Q + nSquared); - - return IIRCoefficients (c1 * (1.0 + nSquared), - 2.0 * c1 * (1.0 - nSquared), - c1 * (1.0 + nSquared), - 1.0, - c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - n / Q + nSquared)); -} - -IIRCoefficients IIRCoefficients::makeAllPass (const double sampleRate, - const double frequency) noexcept -{ - return makeAllPass (sampleRate, frequency, 1.0 / std::sqrt (2.0)); -} - -IIRCoefficients IIRCoefficients::makeAllPass (const double sampleRate, - const double frequency, - const double Q) noexcept -{ - jassert (sampleRate > 0.0); - jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double n = 1.0 / std::tan (double_Pi * frequency / sampleRate); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + 1.0 / Q * n + nSquared); - - return IIRCoefficients (c1 * (1.0 - n / Q + nSquared), - c1 * 2.0 * (1.0 - nSquared), - 1.0, - 1.0, - c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - n / Q + nSquared)); -} - -IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate, - const double cutOffFrequency, - const double Q, - const float gainFactor) noexcept -{ - jassert (sampleRate > 0.0); - jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double A = jmax (0.0f, std::sqrt (gainFactor)); - const double aminus1 = A - 1.0; - const double aplus1 = A + 1.0; - const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; - const double coso = std::cos (omega); - const double beta = std::sin (omega) * std::sqrt (A) / Q; - const double aminus1TimesCoso = aminus1 * coso; - - return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta), - A * 2.0 * (aminus1 - aplus1 * coso), - A * (aplus1 - aminus1TimesCoso - beta), - aplus1 + aminus1TimesCoso + beta, - -2.0 * (aminus1 + aplus1 * coso), - aplus1 + aminus1TimesCoso - beta); -} - -IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate, - const double cutOffFrequency, - const double Q, - const float gainFactor) noexcept -{ - jassert (sampleRate > 0.0); - jassert (cutOffFrequency > 0.0 && cutOffFrequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double A = jmax (0.0f, std::sqrt (gainFactor)); - const double aminus1 = A - 1.0; - const double aplus1 = A + 1.0; - const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; - const double coso = std::cos (omega); - const double beta = std::sin (omega) * std::sqrt (A) / Q; - const double aminus1TimesCoso = aminus1 * coso; - - return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta), - A * -2.0 * (aminus1 + aplus1 * coso), - A * (aplus1 + aminus1TimesCoso - beta), - aplus1 - aminus1TimesCoso + beta, - 2.0 * (aminus1 - aplus1 * coso), - aplus1 - aminus1TimesCoso - beta); -} - -IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate, - const double frequency, - const double Q, - const float gainFactor) noexcept -{ - jassert (sampleRate > 0.0); - jassert (frequency > 0.0 && frequency <= sampleRate * 0.5); - jassert (Q > 0.0); - - const double A = jmax (0.0f, std::sqrt (gainFactor)); - const double omega = (double_Pi * 2.0 * jmax (frequency, 2.0)) / sampleRate; - const double alpha = 0.5 * std::sin (omega) / Q; - const double c2 = -2.0 * std::cos (omega); - const double alphaTimesA = alpha * A; - const double alphaOverA = alpha / A; - - return IIRCoefficients (1.0 + alphaTimesA, - c2, - 1.0 - alphaTimesA, - 1.0 + alphaOverA, - c2, - 1.0 - alphaOverA); -} - -//============================================================================== -IIRFilter::IIRFilter() noexcept - : v1 (0.0), v2 (0.0), active (false) -{ -} - -IIRFilter::IIRFilter (const IIRFilter& other) noexcept - : v1 (0.0), v2 (0.0), active (other.active) -{ - const SpinLock::ScopedLockType sl (other.processLock); - coefficients = other.coefficients; -} - -IIRFilter::~IIRFilter() noexcept -{ -} - -//============================================================================== -void IIRFilter::makeInactive() noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - active = false; -} - -void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - - coefficients = newCoefficients; - active = true; -} - -//============================================================================== -void IIRFilter::reset() noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - v1 = v2 = 0.0; -} - -float IIRFilter::processSingleSampleRaw (const float in) noexcept -{ - float out = coefficients.coefficients[0] * in + v1; - - JUCE_SNAP_TO_ZERO (out); - - v1 = coefficients.coefficients[1] * in - coefficients.coefficients[3] * out + v2; - v2 = coefficients.coefficients[2] * in - coefficients.coefficients[4] * out; - - return out; -} - -void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - - if (active) - { - const float c0 = coefficients.coefficients[0]; - const float c1 = coefficients.coefficients[1]; - const float c2 = coefficients.coefficients[2]; - const float c3 = coefficients.coefficients[3]; - const float c4 = coefficients.coefficients[4]; - float lv1 = v1, lv2 = v2; - - for (int i = 0; i < numSamples; ++i) - { - const float in = samples[i]; - const float out = c0 * in + lv1; - samples[i] = out; - - lv1 = c1 * in - c3 * out + lv2; - lv2 = c2 * in - c4 * out; - } - - JUCE_SNAP_TO_ZERO (lv1); v1 = lv1; - JUCE_SNAP_TO_ZERO (lv2); v2 = lv2; - } -} - -#undef JUCE_SNAP_TO_ZERO - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_IIRFilter.h b/source/modules/juce_audio_basics/effects/juce_IIRFilter.h deleted file mode 100644 index 9db85b582..000000000 --- a/source/modules/juce_audio_basics/effects/juce_IIRFilter.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -class IIRFilter; - -//============================================================================== -/** - A set of coefficients for use in an IIRFilter object. - - @see IIRFilter -*/ -class JUCE_API IIRCoefficients -{ -public: - //============================================================================== - /** Creates a null set of coefficients (which will produce silence). */ - IIRCoefficients() noexcept; - - /** Directly constructs an object from the raw coefficients. - Most people will want to use the static methods instead of this, but - the constructor is public to allow tinkerers to create their own custom - filters! - */ - IIRCoefficients (double c1, double c2, double c3, - double c4, double c5, double c6) noexcept; - - /** Creates a copy of another filter. */ - IIRCoefficients (const IIRCoefficients&) noexcept; - /** Creates a copy of another filter. */ - IIRCoefficients& operator= (const IIRCoefficients&) noexcept; - /** Destructor. */ - ~IIRCoefficients() noexcept; - - //============================================================================== - /** Returns the coefficients for a low-pass filter. */ - static IIRCoefficients makeLowPass (double sampleRate, - double frequency) noexcept; - - /** Returns the coefficients for a low-pass filter with variable Q. */ - static IIRCoefficients makeLowPass (double sampleRate, - double frequency, - double Q) noexcept; - - //============================================================================== - /** Returns the coefficients for a high-pass filter. */ - static IIRCoefficients makeHighPass (double sampleRate, - double frequency) noexcept; - - /** Returns the coefficients for a high-pass filter with variable Q. */ - static IIRCoefficients makeHighPass (double sampleRate, - double frequency, - double Q) noexcept; - - //============================================================================== - /** Returns the coefficients for a band-pass filter. */ - static IIRCoefficients makeBandPass (double sampleRate, double frequency) noexcept; - - /** Returns the coefficients for a band-pass filter with variable Q. */ - static IIRCoefficients makeBandPass (double sampleRate, - double frequency, - double Q) noexcept; - - //============================================================================== - /** Returns the coefficients for a notch filter. */ - static IIRCoefficients makeNotchFilter (double sampleRate, double frequency) noexcept; - - /** Returns the coefficients for a notch filter with variable Q. */ - static IIRCoefficients makeNotchFilter (double sampleRate, - double frequency, - double Q) noexcept; - - //============================================================================== - /** Returns the coefficients for an all-pass filter. */ - static IIRCoefficients makeAllPass (double sampleRate, double frequency) noexcept; - - /** Returns the coefficients for an all-pass filter with variable Q. */ - static IIRCoefficients makeAllPass (double sampleRate, - double frequency, - double Q) noexcept; - - //============================================================================== - /** Returns the coefficients for a low-pass shelf filter with variable Q and gain. - - The gain is a scale factor that the low frequencies are multiplied by, so values - greater than 1.0 will boost the low frequencies, values less than 1.0 will - attenuate them. - */ - static IIRCoefficients makeLowShelf (double sampleRate, - double cutOffFrequency, - double Q, - float gainFactor) noexcept; - - /** Returns the coefficients for a high-pass shelf filter with variable Q and gain. - - The gain is a scale factor that the high frequencies are multiplied by, so values - greater than 1.0 will boost the high frequencies, values less than 1.0 will - attenuate them. - */ - static IIRCoefficients makeHighShelf (double sampleRate, - double cutOffFrequency, - double Q, - float gainFactor) noexcept; - - /** Returns the coefficients for a peak filter centred around a - given frequency, with a variable Q and gain. - - The gain is a scale factor that the centre frequencies are multiplied by, so - values greater than 1.0 will boost the centre frequencies, values less than - 1.0 will attenuate them. - */ - static IIRCoefficients makePeakFilter (double sampleRate, - double centreFrequency, - double Q, - float gainFactor) noexcept; - - //============================================================================== - /** The raw coefficients. - You should leave these numbers alone unless you really know what you're doing. - */ - float coefficients[5]; -}; - -//============================================================================== -/** - An IIR filter that can perform low, high, or band-pass filtering on an - audio signal. - - @see IIRCoefficient, IIRFilterAudioSource -*/ -class JUCE_API IIRFilter -{ -public: - //============================================================================== - /** Creates a filter. - - Initially the filter is inactive, so will have no effect on samples that - you process with it. Use the setCoefficients() method to turn it into the - type of filter needed. - */ - IIRFilter() noexcept; - - /** Creates a copy of another filter. */ - IIRFilter (const IIRFilter&) noexcept; - - /** Destructor. */ - ~IIRFilter() noexcept; - - //============================================================================== - /** Clears the filter so that any incoming data passes through unchanged. */ - void makeInactive() noexcept; - - /** Applies a set of coefficients to this filter. */ - void setCoefficients (const IIRCoefficients& newCoefficients) noexcept; - - /** Returns the coefficients that this filter is using. */ - IIRCoefficients getCoefficients() const noexcept { return coefficients; } - - //============================================================================== - /** Resets the filter's processing pipeline, ready to start a new stream of data. - - Note that this clears the processing state, but the type of filter and - its coefficients aren't changed. To put a filter into an inactive state, use - the makeInactive() method. - */ - void reset() noexcept; - - /** Performs the filter operation on the given set of samples. */ - void processSamples (float* samples, int numSamples) noexcept; - - /** Processes a single sample, without any locking or checking. - - Use this if you need fast processing of a single value, but be aware that - this isn't thread-safe in the way that processSamples() is. - */ - float processSingleSampleRaw (float sample) noexcept; - -protected: - //============================================================================== - SpinLock processLock; - IIRCoefficients coefficients; - float v1, v2; - bool active; - - IIRFilter& operator= (const IIRFilter&); - JUCE_LEAK_DETECTOR (IIRFilter) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.cpp b/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.cpp deleted file mode 100644 index f8d5ee3bc..000000000 --- a/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2013 - Raw Material Software 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. - - ============================================================================== -*/ - -#if JUCE_INTEL - #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0; -#else - #define JUCE_SNAP_TO_ZERO(n) -#endif - -namespace juce -{ - -//============================================================================== -IIRFilterOld::IIRFilterOld() - : active (false), v1 (0), v2 (0) -{ - zeromem (coefficients, sizeof (coefficients)); -} - -IIRFilterOld::IIRFilterOld (const IIRFilterOld& other) - : active (other.active), v1 (0), v2 (0) -{ - const SpinLock::ScopedLockType sl (other.processLock); - memcpy (coefficients, other.coefficients, sizeof (coefficients)); -} - -IIRFilterOld::~IIRFilterOld() -{ -} - -//============================================================================== -void IIRFilterOld::reset() noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - v1 = v2 = 0; -} - -float IIRFilterOld::processSingleSampleRaw (const float in) noexcept -{ - float out = coefficients[0] * in + v1; - - JUCE_SNAP_TO_ZERO (out); - - v1 = coefficients[1] * in - coefficients[3] * out + v2; - v2 = coefficients[2] * in - coefficients[4] * out; - - return out; -} - -void IIRFilterOld::processSamples (float* const samples, - const int numSamples) noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - - if (active) - { - const float c0 = coefficients[0]; - const float c1 = coefficients[1]; - const float c2 = coefficients[2]; - const float c3 = coefficients[3]; - const float c4 = coefficients[4]; - float lv1 = v1, lv2 = v2; - - for (int i = 0; i < numSamples; ++i) - { - const float in = samples[i]; - const float out = c0 * in + lv1; - samples[i] = out; - - lv1 = c1 * in - c3 * out + lv2; - lv2 = c2 * in - c4 * out; - } - - JUCE_SNAP_TO_ZERO (lv1); v1 = lv1; - JUCE_SNAP_TO_ZERO (lv2); v2 = lv2; - } -} - -//============================================================================== -void IIRFilterOld::makeLowPass (const double sampleRate, - const double frequency) noexcept -{ - jassert (sampleRate > 0); - - const double n = 1.0 / tan (double_Pi * frequency / sampleRate); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); - - setCoefficients (c1, - c1 * 2.0f, - c1, - 1.0, - c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); -} - -void IIRFilterOld::makeHighPass (const double sampleRate, - const double frequency) noexcept -{ - const double n = tan (double_Pi * frequency / sampleRate); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); - - setCoefficients (c1, - c1 * -2.0f, - c1, - 1.0, - c1 * 2.0 * (nSquared - 1.0), - c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); -} - -void IIRFilterOld::makeLowShelf (const double sampleRate, - const double cutOffFrequency, - const double Q, - const float gainFactor) noexcept -{ - jassert (sampleRate > 0); - jassert (Q > 0); - - const double A = jmax (0.0f, gainFactor); - const double aminus1 = A - 1.0; - const double aplus1 = A + 1.0; - const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; - const double coso = std::cos (omega); - const double beta = std::sin (omega) * std::sqrt (A) / Q; - const double aminus1TimesCoso = aminus1 * coso; - - setCoefficients (A * (aplus1 - aminus1TimesCoso + beta), - A * 2.0 * (aminus1 - aplus1 * coso), - A * (aplus1 - aminus1TimesCoso - beta), - aplus1 + aminus1TimesCoso + beta, - -2.0 * (aminus1 + aplus1 * coso), - aplus1 + aminus1TimesCoso - beta); -} - -void IIRFilterOld::makeHighShelf (const double sampleRate, - const double cutOffFrequency, - const double Q, - const float gainFactor) noexcept -{ - jassert (sampleRate > 0); - jassert (Q > 0); - - const double A = jmax (0.0f, gainFactor); - const double aminus1 = A - 1.0; - const double aplus1 = A + 1.0; - const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; - const double coso = std::cos (omega); - const double beta = std::sin (omega) * std::sqrt (A) / Q; - const double aminus1TimesCoso = aminus1 * coso; - - setCoefficients (A * (aplus1 + aminus1TimesCoso + beta), - A * -2.0 * (aminus1 + aplus1 * coso), - A * (aplus1 + aminus1TimesCoso - beta), - aplus1 - aminus1TimesCoso + beta, - 2.0 * (aminus1 - aplus1 * coso), - aplus1 - aminus1TimesCoso - beta); -} - -void IIRFilterOld::makeBandPass (const double sampleRate, - const double centreFrequency, - const double Q, - const float gainFactor) noexcept -{ - jassert (sampleRate > 0); - jassert (Q > 0); - - const double A = jmax (0.0f, gainFactor); - const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; - const double alpha = 0.5 * std::sin (omega) / Q; - const double c2 = -2.0 * std::cos (omega); - const double alphaTimesA = alpha * A; - const double alphaOverA = alpha / A; - - setCoefficients (1.0 + alphaTimesA, - c2, - 1.0 - alphaTimesA, - 1.0 + alphaOverA, - c2, - 1.0 - alphaOverA); -} - -void IIRFilterOld::makeInactive() noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - active = false; -} - -//============================================================================== -void IIRFilterOld::copyCoefficientsFrom (const IIRFilterOld& other) noexcept -{ - const SpinLock::ScopedLockType sl (processLock); - - memcpy (coefficients, other.coefficients, sizeof (coefficients)); - active = other.active; -} - -//============================================================================== -void IIRFilterOld::setCoefficients (double c1, double c2, double c3, - double c4, double c5, double c6) noexcept -{ - const double a = 1.0 / c4; - - c1 *= a; - c2 *= a; - c3 *= a; - c5 *= a; - c6 *= a; - - const SpinLock::ScopedLockType sl (processLock); - - coefficients[0] = (float) c1; - coefficients[1] = (float) c2; - coefficients[2] = (float) c3; - coefficients[3] = (float) c5; - coefficients[4] = (float) c6; - - active = true; -} - -#undef JUCE_SNAP_TO_ZERO - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.h b/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.h deleted file mode 100644 index c6332171f..000000000 --- a/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2013 - Raw Material Software 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_IIRFILTER_OLD_JUCEHEADER__ -#define __JUCE_IIRFILTER_OLD_JUCEHEADER__ - - -namespace juce -{ - -//============================================================================== -/** - An IIR filter that can perform low, high, or band-pass filtering on an - audio signal. - - @see IIRFilterAudioSource -*/ -class JUCE_API IIRFilterOld -{ -public: - //============================================================================== - /** Creates a filter. - - Initially the filter is inactive, so will have no effect on samples that - you process with it. Use the appropriate method to turn it into the type - of filter needed. - */ - IIRFilterOld(); - - /** Creates a copy of another filter. */ - IIRFilterOld (const IIRFilterOld& other); - - /** Destructor. */ - ~IIRFilterOld(); - - //============================================================================== - /** Resets the filter's processing pipeline, ready to start a new stream of data. - - Note that this clears the processing state, but the type of filter and - its coefficients aren't changed. To put a filter into an inactive state, use - the makeInactive() method. - */ - void reset() noexcept; - - /** Performs the filter operation on the given set of samples. - */ - void processSamples (float* samples, - int numSamples) noexcept; - - /** Processes a single sample, without any locking or checking. - - Use this if you need fast processing of a single value, but be aware that - this isn't thread-safe in the way that processSamples() is. - */ - float processSingleSampleRaw (float sample) noexcept; - - //============================================================================== - /** Sets the filter up to act as a low-pass filter. - */ - void makeLowPass (double sampleRate, - double frequency) noexcept; - - /** Sets the filter up to act as a high-pass filter. - */ - void makeHighPass (double sampleRate, - double frequency) noexcept; - - //============================================================================== - /** Sets the filter up to act as a low-pass shelf filter with variable Q and gain. - - The gain is a scale factor that the low frequencies are multiplied by, so values - greater than 1.0 will boost the low frequencies, values less than 1.0 will - attenuate them. - */ - void makeLowShelf (double sampleRate, - double cutOffFrequency, - double Q, - float gainFactor) noexcept; - - /** Sets the filter up to act as a high-pass shelf filter with variable Q and gain. - - The gain is a scale factor that the high frequencies are multiplied by, so values - greater than 1.0 will boost the high frequencies, values less than 1.0 will - attenuate them. - */ - void makeHighShelf (double sampleRate, - double cutOffFrequency, - double Q, - float gainFactor) noexcept; - - /** Sets the filter up to act as a band pass filter centred around a - frequency, with a variable Q and gain. - - The gain is a scale factor that the centre frequencies are multiplied by, so - values greater than 1.0 will boost the centre frequencies, values less than - 1.0 will attenuate them. - */ - void makeBandPass (double sampleRate, - double centreFrequency, - double Q, - float gainFactor) noexcept; - - /** Clears the filter's coefficients so that it becomes inactive. - */ - void makeInactive() noexcept; - - //============================================================================== - /** Makes this filter duplicate the set-up of another one. - */ - void copyCoefficientsFrom (const IIRFilterOld& other) noexcept; - - -protected: - //============================================================================== - SpinLock processLock; - - void setCoefficients (double c1, double c2, double c3, - double c4, double c5, double c6) noexcept; - - bool active; - float coefficients[5]; - float v1, v2; - - // (use the copyCoefficientsFrom() method instead of this operator) - IIRFilterOld& operator= (const IIRFilterOld&); - JUCE_LEAK_DETECTOR (IIRFilterOld) -}; - - -} // namespace juce - -#endif // __JUCE_IIRFILTER_OLD_JUCEHEADER__ diff --git a/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp b/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp deleted file mode 100644 index 9488ef9b0..000000000 --- a/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -namespace -{ - static forcedinline void pushInterpolationSample (float* lastInputSamples, const float newValue) noexcept - { - lastInputSamples[4] = lastInputSamples[3]; - lastInputSamples[3] = lastInputSamples[2]; - lastInputSamples[2] = lastInputSamples[1]; - lastInputSamples[1] = lastInputSamples[0]; - lastInputSamples[0] = newValue; - } - - static forcedinline void pushInterpolationSamples (float* lastInputSamples, const float* input, int numOut) noexcept - { - if (numOut >= 5) - { - for (int i = 0; i < 5; ++i) - lastInputSamples[i] = input[--numOut]; - } - else - { - for (int i = 0; i < numOut; ++i) - pushInterpolationSample (lastInputSamples, input[i]); - } - } - - template - static int interpolate (float* lastInputSamples, double& subSamplePos, double actualRatio, - const float* in, float* out, int numOut) noexcept - { - auto pos = subSamplePos; - - if (actualRatio == 1.0 && pos == 1.0) - { - memcpy (out, in, (size_t) numOut * sizeof (float)); - pushInterpolationSamples (lastInputSamples, in, numOut); - return numOut; - } - - int numUsed = 0; - - while (numOut > 0) - { - while (pos >= 1.0) - { - pushInterpolationSample (lastInputSamples, in[numUsed++]); - pos -= 1.0; - } - - *out++ = InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); - pos += actualRatio; - --numOut; - } - - subSamplePos = pos; - return numUsed; - } - - template - static int interpolateAdding (float* lastInputSamples, double& subSamplePos, double actualRatio, - const float* in, float* out, int numOut, const float gain) noexcept - { - auto pos = subSamplePos; - - if (actualRatio == 1.0 && pos == 1.0) - { - FloatVectorOperations::addWithMultiply (out, in, gain, numOut); - pushInterpolationSamples (lastInputSamples, in, numOut); - return numOut; - } - - int numUsed = 0; - - while (numOut > 0) - { - while (pos >= 1.0) - { - pushInterpolationSample (lastInputSamples, in[numUsed++]); - pos -= 1.0; - } - - *out++ += gain * InterpolatorType::valueAtOffset (lastInputSamples, (float) pos); - pos += actualRatio; - --numOut; - } - - subSamplePos = pos; - return numUsed; - } -} - -//============================================================================== -template -struct LagrangeResampleHelper -{ - static forcedinline void calc (float& a, float b) noexcept { a *= b * (1.0f / k); } -}; - -template<> -struct LagrangeResampleHelper<0> -{ - static forcedinline void calc (float&, float) noexcept {} -}; - -struct LagrangeAlgorithm -{ - static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept - { - return calcCoefficient<0> (inputs[4], offset) - + calcCoefficient<1> (inputs[3], offset) - + calcCoefficient<2> (inputs[2], offset) - + calcCoefficient<3> (inputs[1], offset) - + calcCoefficient<4> (inputs[0], offset); - } - - template - static forcedinline float calcCoefficient (float input, const float offset) noexcept - { - LagrangeResampleHelper<0 - k>::calc (input, -2.0f - offset); - LagrangeResampleHelper<1 - k>::calc (input, -1.0f - offset); - LagrangeResampleHelper<2 - k>::calc (input, 0.0f - offset); - LagrangeResampleHelper<3 - k>::calc (input, 1.0f - offset); - LagrangeResampleHelper<4 - k>::calc (input, 2.0f - offset); - return input; - } -}; - -LagrangeInterpolator::LagrangeInterpolator() noexcept { reset(); } -LagrangeInterpolator::~LagrangeInterpolator() noexcept {} - -void LagrangeInterpolator::reset() noexcept -{ - subSamplePos = 1.0; - - for (auto& s : lastInputSamples) - s = 0; -} - -int LagrangeInterpolator::process (double actualRatio, const float* in, float* out, int numOut) noexcept -{ - return interpolate (lastInputSamples, subSamplePos, actualRatio, in, out, numOut); -} - -int LagrangeInterpolator::processAdding (double actualRatio, const float* in, float* out, int numOut, float gain) noexcept -{ - return interpolateAdding (lastInputSamples, subSamplePos, actualRatio, in, out, numOut, gain); -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h b/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h deleted file mode 100644 index d52757451..000000000 --- a/source/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -/** - Interpolator for resampling a stream of floats using 4-point lagrange interpolation. - - Note that the resampler is stateful, so when there's a break in the continuity - of the input stream you're feeding it, you should call reset() before feeding - it any new data. And like with any other stateful filter, if you're resampling - multiple channels, make sure each one uses its own LagrangeInterpolator - object. - - @see CatmullRomInterpolator -*/ -class JUCE_API LagrangeInterpolator -{ -public: - LagrangeInterpolator() noexcept; - ~LagrangeInterpolator() noexcept; - - /** Resets the state of the interpolator. - Call this when there's a break in the continuity of the input data stream. - */ - void reset() noexcept; - - /** Resamples a stream of samples. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results into - @param numOutputSamplesToProduce the number of output samples that should be created - - @returns the actual number of input samples that were used - */ - int process (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce) noexcept; - - /** Resamples a stream of samples, adding the results to the output data - with a gain. - - @param speedRatio the number of input samples to use for each output sample - @param inputSamples the source data to read from. This must contain at - least (speedRatio * numOutputSamplesToProduce) samples. - @param outputSamples the buffer to write the results to - the result values will be added - to any pre-existing data in this buffer after being multiplied by - the gain factor - @param numOutputSamplesToProduce the number of output samples that should be created - @param gain a gain factor to multiply the resulting samples by before - adding them to the destination buffer - - @returns the actual number of input samples that were used - */ - int processAdding (double speedRatio, - const float* inputSamples, - float* outputSamples, - int numOutputSamplesToProduce, - float gain) noexcept; - -private: - float lastInputSamples[5]; - double subSamplePos; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h b/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h deleted file mode 100644 index 3dc342c41..000000000 --- a/source/modules/juce_audio_basics/effects/juce_LinearSmoothedValue.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Utility class for linearly smoothed values like volume etc. that should - not change abruptly but as a linear ramp, to avoid audio glitches. -*/ - -//============================================================================== -template -class LinearSmoothedValue -{ -public: - /** Constructor. */ - LinearSmoothedValue() noexcept - { - } - - /** Constructor. */ - LinearSmoothedValue (FloatType initialValue) noexcept - : currentValue (initialValue), target (initialValue) - { - } - - //============================================================================== - /** Reset to a new sample rate and ramp length. - @param sampleRate The sampling rate - @param rampLengthInSeconds The duration of the ramp in seconds - */ - 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. - @param newValue 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. - @returns Smoothed value - */ - FloatType getNextValue() noexcept - { - if (countdown <= 0) - return target; - - --countdown; - currentValue += step; - return currentValue; - } - - /** Returns true if the current value is currently being interpolated. */ - bool isSmoothing() const noexcept - { - return countdown > 0; - } - - /** Returns the target value towards which the smoothed value is currently moving. */ - FloatType getTargetValue() const noexcept - { - return target; - } - - //============================================================================== - /** Applies a linear smoothed gain to a stream of samples - S[i] *= gain - @param samples Pointer to a raw array of samples - @param numSamples Length of array of samples - */ - void applyGain (FloatType* samples, int numSamples) noexcept - { - jassert(numSamples >= 0); - - if (isSmoothing()) - { - for (int i = 0; i < numSamples; i++) - samples[i] *= getNextValue(); - } - else - { - FloatVectorOperations::multiply (samples, target, numSamples); - } - } - - //============================================================================== - /** Computes output as linear smoothed gain applied to a stream of samples. - Sout[i] = Sin[i] * gain - @param samplesOut A pointer to a raw array of output samples - @param samplesIn A pointer to a raw array of input samples - @param numSamples The length of the array of samples - */ - void applyGain (FloatType* samplesOut, const FloatType* samplesIn, int numSamples) noexcept - { - jassert (numSamples >= 0); - - if (isSmoothing()) - { - for (int i = 0; i < numSamples; i++) - samplesOut[i] = samplesIn[i] * getNextValue(); - } - else - { - FloatVectorOperations::multiply (samplesOut, samplesIn, target, numSamples); - } - } - - //============================================================================== - /** Applies a linear smoothed gain to a buffer */ - void applyGain (AudioBuffer& buffer, int numSamples) noexcept - { - jassert (numSamples >= 0); - - if (isSmoothing()) - { - if (buffer.getNumChannels() == 1) - { - FloatType* samples = buffer.getWritePointer(0); - - for (int i = 0; i < numSamples; i++) - samples[i] *= getNextValue(); - } - else - { - for (int i = 0; i < numSamples; i++) - { - const FloatType gain = getNextValue(); - - for (int channel = 0; channel < buffer.getNumChannels(); channel++) - buffer.setSample (channel, i, buffer.getSample (channel, i) * gain); - } - } - } - else - { - buffer.applyGain (0, numSamples, target); - } - } - -private: - //============================================================================== - FloatType currentValue = 0, target = 0, step = 0; - int countdown = 0, stepsToTarget = 0; -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/effects/juce_Reverb.h b/source/modules/juce_audio_basics/effects/juce_Reverb.h deleted file mode 100644 index 02b95b3c3..000000000 --- a/source/modules/juce_audio_basics/effects/juce_Reverb.h +++ /dev/null @@ -1,320 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Performs a simple reverb effect on a stream of audio data. - - This is a simple stereo reverb, based on the technique and tunings used in FreeVerb. - Use setSampleRate() to prepare it, and then call processStereo() or processMono() to - apply the reverb to your audio data. - - @see ReverbAudioSource -*/ -class Reverb -{ -public: - //============================================================================== - Reverb() - { - setParameters (Parameters()); - setSampleRate (44100.0); - } - - //============================================================================== - /** Holds the parameters being used by a Reverb object. */ - struct Parameters - { - Parameters() noexcept - : roomSize (0.5f), - damping (0.5f), - wetLevel (0.33f), - dryLevel (0.4f), - width (1.0f), - freezeMode (0) - {} - - float roomSize; /**< Room size, 0 to 1.0, where 1.0 is big, 0 is small. */ - float damping; /**< Damping, 0 to 1.0, where 0 is not damped, 1.0 is fully damped. */ - float wetLevel; /**< Wet level, 0 to 1.0 */ - float dryLevel; /**< Dry level, 0 to 1.0 */ - float width; /**< Reverb width, 0 to 1.0, where 1.0 is very wide. */ - float freezeMode; /**< Freeze mode - values < 0.5 are "normal" mode, values > 0.5 - put the reverb into a continuous feedback loop. */ - }; - - //============================================================================== - /** Returns the reverb's current parameters. */ - const Parameters& getParameters() const noexcept { return parameters; } - - /** Applies a new set of parameters to the reverb. - Note that this doesn't attempt to lock the reverb, so if you call this in parallel with - the process method, you may get artifacts. - */ - void setParameters (const Parameters& newParams) - { - const float wetScaleFactor = 3.0f; - const float dryScaleFactor = 2.0f; - - const float wet = newParams.wetLevel * wetScaleFactor; - dryGain.setValue (newParams.dryLevel * dryScaleFactor); - wetGain1.setValue (0.5f * wet * (1.0f + newParams.width)); - wetGain2.setValue (0.5f * wet * (1.0f - newParams.width)); - - gain = isFrozen (newParams.freezeMode) ? 0.0f : 0.015f; - parameters = newParams; - updateDamping(); - } - - //============================================================================== - /** Sets the sample rate that will be used for the reverb. - You must call this before the process methods, in order to tell it the correct sample rate. - */ - void setSampleRate (const double sampleRate) - { - jassert (sampleRate > 0); - - static const short combTunings[] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 }; // (at 44100Hz) - static const short allPassTunings[] = { 556, 441, 341, 225 }; - const int stereoSpread = 23; - const int intSampleRate = (int) sampleRate; - - for (int i = 0; i < numCombs; ++i) - { - comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100); - comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100); - } - - for (int i = 0; i < numAllPasses; ++i) - { - allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100); - allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100); - } - - const double smoothTime = 0.01; - damping .reset (sampleRate, smoothTime); - feedback.reset (sampleRate, smoothTime); - dryGain .reset (sampleRate, smoothTime); - wetGain1.reset (sampleRate, smoothTime); - wetGain2.reset (sampleRate, smoothTime); - } - - /** Clears the reverb's buffers. */ - void reset() - { - for (int j = 0; j < numChannels; ++j) - { - for (int i = 0; i < numCombs; ++i) - comb[j][i].clear(); - - for (int i = 0; i < numAllPasses; ++i) - allPass[j][i].clear(); - } - } - - //============================================================================== - /** Applies the reverb to two stereo channels of audio data. */ - void processStereo (float* const left, float* const right, const int numSamples) noexcept - { - jassert (left != nullptr && right != nullptr); - - for (int i = 0; i < numSamples; ++i) - { - const float input = (left[i] + right[i]) * gain; - float outL = 0, outR = 0; - - const float damp = damping.getNextValue(); - const float feedbck = feedback.getNextValue(); - - for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel - { - outL += comb[0][j].process (input, damp, feedbck); - outR += comb[1][j].process (input, damp, feedbck); - } - - for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series - { - outL = allPass[0][j].process (outL); - outR = allPass[1][j].process (outR); - } - - const float dry = dryGain.getNextValue(); - const float wet1 = wetGain1.getNextValue(); - const float wet2 = wetGain2.getNextValue(); - - left[i] = outL * wet1 + outR * wet2 + left[i] * dry; - right[i] = outR * wet1 + outL * wet2 + right[i] * dry; - } - } - - /** Applies the reverb to a single mono channel of audio data. */ - void processMono (float* const samples, const int numSamples) noexcept - { - jassert (samples != nullptr); - - for (int i = 0; i < numSamples; ++i) - { - const float input = samples[i] * gain; - float output = 0; - - const float damp = damping.getNextValue(); - const float feedbck = feedback.getNextValue(); - - for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel - output += comb[0][j].process (input, damp, feedbck); - - for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series - output = allPass[0][j].process (output); - - const float dry = dryGain.getNextValue(); - const float wet1 = wetGain1.getNextValue(); - - samples[i] = output * wet1 + samples[i] * dry; - } - } - -private: - //============================================================================== - static bool isFrozen (const float freezeMode) noexcept { return freezeMode >= 0.5f; } - - void updateDamping() noexcept - { - const float roomScaleFactor = 0.28f; - const float roomOffset = 0.7f; - const float dampScaleFactor = 0.4f; - - if (isFrozen (parameters.freezeMode)) - setDamping (0.0f, 1.0f); - else - setDamping (parameters.damping * dampScaleFactor, - parameters.roomSize * roomScaleFactor + roomOffset); - } - - void setDamping (const float dampingToUse, const float roomSizeToUse) noexcept - { - damping.setValue (dampingToUse); - feedback.setValue (roomSizeToUse); - } - - //============================================================================== - class CombFilter - { - public: - CombFilter() noexcept : bufferSize (0), bufferIndex (0), last (0) {} - - void setSize (const int size) - { - if (size != bufferSize) - { - bufferIndex = 0; - buffer.malloc ((size_t) size); - bufferSize = size; - } - - clear(); - } - - void clear() noexcept - { - last = 0; - buffer.clear ((size_t) bufferSize); - } - - float process (const float input, const float damp, const float feedbackLevel) noexcept - { - const float output = buffer[bufferIndex]; - last = (output * (1.0f - damp)) + (last * damp); - JUCE_UNDENORMALISE (last); - - float temp = input + (last * feedbackLevel); - JUCE_UNDENORMALISE (temp); - buffer[bufferIndex] = temp; - bufferIndex = (bufferIndex + 1) % bufferSize; - return output; - } - - private: - HeapBlock buffer; - int bufferSize, bufferIndex; - float last; - - JUCE_DECLARE_NON_COPYABLE (CombFilter) - }; - - //============================================================================== - class AllPassFilter - { - public: - AllPassFilter() noexcept : bufferSize (0), bufferIndex (0) {} - - void setSize (const int size) - { - if (size != bufferSize) - { - bufferIndex = 0; - buffer.malloc ((size_t) size); - bufferSize = size; - } - - clear(); - } - - void clear() noexcept - { - buffer.clear ((size_t) bufferSize); - } - - float process (const float input) noexcept - { - const float bufferedValue = buffer [bufferIndex]; - float temp = input + (bufferedValue * 0.5f); - JUCE_UNDENORMALISE (temp); - buffer [bufferIndex] = temp; - bufferIndex = (bufferIndex + 1) % bufferSize; - return bufferedValue - input; - } - - private: - HeapBlock buffer; - int bufferSize, bufferIndex; - - JUCE_DECLARE_NON_COPYABLE (AllPassFilter) - }; - - //============================================================================== - enum { numCombs = 8, numAllPasses = 4, numChannels = 2 }; - - Parameters parameters; - float gain; - - CombFilter comb [numChannels][numCombs]; - AllPassFilter allPass [numChannels][numAllPasses]; - - LinearSmoothedValue damping, feedback, dryGain, wetGain1, wetGain2; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp deleted file mode 100644 index 9ee676fb3..000000000 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -#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 - header files that the compiler may be using. - */ - #error "Incorrect use of JUCE cpp file" -#endif - -#include "juce_audio_basics.h" - -#if JUCE_MINGW - #define JUCE_USE_SSE_INTRINSICS 0 -#endif - -#if JUCE_MINGW && ! defined (alloca) - #define alloca __builtin_alloca -#endif - -#ifndef JUCE_USE_SSE_INTRINSICS - #define JUCE_USE_SSE_INTRINSICS 1 -#endif - -#if ! JUCE_INTEL - #undef JUCE_USE_SSE_INTRINSICS -#endif - -#if JUCE_USE_SSE_INTRINSICS - #include -#endif - -#ifndef JUCE_USE_VDSP_FRAMEWORK - #define JUCE_USE_VDSP_FRAMEWORK 1 -#endif - -#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK - #include -#else - #undef JUCE_USE_VDSP_FRAMEWORK -#endif - -#if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) - #define JUCE_USE_ARM_NEON 1 -#endif - -#if TARGET_IPHONE_SIMULATOR - #ifdef JUCE_USE_ARM_NEON - #undef JUCE_USE_ARM_NEON - #endif - #define JUCE_USE_ARM_NEON 0 -#endif - -#if JUCE_USE_ARM_NEON - #include -#endif - -#include "buffers/juce_AudioDataConverters.cpp" -#include "buffers/juce_FloatVectorOperations.cpp" -#include "buffers/juce_AudioChannelSet.cpp" -#include "effects/juce_IIRFilter.cpp" -#include "effects/juce_IIRFilterOld.cpp" -#include "effects/juce_LagrangeInterpolator.cpp" -#include "effects/juce_CatmullRomInterpolator.cpp" -#include "midi/juce_MidiBuffer.cpp" -#include "midi/juce_MidiFile.cpp" -#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" -#include "sources/juce_MemoryAudioSource.cpp" -#include "sources/juce_MixerAudioSource.cpp" -#include "sources/juce_ResamplingAudioSource.cpp" -#include "sources/juce_ReverbAudioSource.cpp" -#include "sources/juce_ToneGeneratorAudioSource.cpp" -#include "synthesisers/juce_Synthesiser.cpp" diff --git a/source/modules/juce_audio_basics/juce_audio_basics.h b/source/modules/juce_audio_basics/juce_audio_basics.h deleted file mode 100644 index bcce98b41..000000000 --- a/source/modules/juce_audio_basics/juce_audio_basics.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -/******************************************************************************* - The block below describes the properties of this module, and is read by - the Projucer to automatically generate project code that uses it. - For details about the syntax and how to create or use a module, see the - JUCE Module Format.txt file. - - - BEGIN_JUCE_MODULE_DECLARATION - - ID: juce_audio_basics - vendor: juce - version: 5.1.2 - name: JUCE audio and MIDI data classes - description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. - website: http://www.juce.com/juce - license: ISC - - dependencies: juce_core - OSXFrameworks: Accelerate - iOSFrameworks: Accelerate - - END_JUCE_MODULE_DECLARATION - -*******************************************************************************/ - - -#pragma once -#define JUCE_AUDIO_BASICS_H_INCLUDED - -#include - -//============================================================================== -#undef Complex // apparently some C libraries actually define these symbols (!) -#undef Factor - -#include "buffers/juce_AudioDataConverters.h" -#include "buffers/juce_FloatVectorOperations.h" -#include "buffers/juce_AudioSampleBuffer.h" -#include "buffers/juce_AudioChannelSet.h" -#include "effects/juce_Decibels.h" -#include "effects/juce_IIRFilter.h" -#include "effects/juce_IIRFilterOld.h" -#include "effects/juce_LagrangeInterpolator.h" -#include "effects/juce_CatmullRomInterpolator.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" -#include "sources/juce_ChannelRemappingAudioSource.h" -#include "sources/juce_IIRFilterAudioSource.h" -#include "sources/juce_MemoryAudioSource.h" -#include "sources/juce_MixerAudioSource.h" -#include "sources/juce_ResamplingAudioSource.h" -#include "sources/juce_ReverbAudioSource.h" -#include "sources/juce_ToneGeneratorAudioSource.h" -#include "synthesisers/juce_Synthesiser.h" -#include "audio_play_head/juce_AudioPlayHead.h" diff --git a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp deleted file mode 100644 index 94d19898f..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -namespace MidiBufferHelpers -{ - inline int getEventTime (const void* const d) noexcept - { - return readUnaligned (d); - } - - inline uint16 getEventDataSize (const void* const d) noexcept - { - return readUnaligned (static_cast (d) + sizeof (int32)); - } - - inline uint16 getEventTotalSize (const void* const d) noexcept - { - return (uint16) (getEventDataSize (d) + sizeof (int32) + sizeof (uint16)); - } - - static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept - { - unsigned int byte = (unsigned int) *data; - int size = 0; - - if (byte == 0xf0 || byte == 0xf7) - { - const uint8* d = data + 1; - - while (d < data + maxBytes) - if (*d++ == 0xf7) - break; - - size = (int) (d - data); - } - else if (byte == 0xff) - { - int n; - const int bytesLeft = MidiMessage::readVariableLengthVal (data + 1, n); - size = jmin (maxBytes, n + 2 + bytesLeft); - } - else if (byte >= 0x80) - { - size = jmin (maxBytes, MidiMessage::getMessageLengthFromFirstByte ((uint8) byte)); - } - - return size; - } - - static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept - { - while (d < endData && getEventTime (d) <= samplePosition) - d += getEventTotalSize (d); - - return d; - } -} - -//============================================================================== -MidiBuffer::MidiBuffer() noexcept {} -MidiBuffer::~MidiBuffer() {} - -MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {} - -MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept -{ - data = other.data; - return *this; -} - -MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept -{ - addEvent (message, 0); -} - -void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); } -void MidiBuffer::clear() noexcept { data.clearQuick(); } -void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); } -bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; } - -void MidiBuffer::clear (const int startSample, const int numSamples) -{ - uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); - uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); - - data.removeRange ((int) (start - data.begin()), (int) (end - data.begin())); -} - -void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) -{ - addEvent (m.getRawData(), m.getRawDataSize(), sampleNumber); -} - -void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber) -{ - const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); - - if (numBytes > 0) - { - const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); - const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); - - data.insertMultiple (offset, 0, (int) newItemSize); - - uint8* const d = data.begin() + offset; - writeUnaligned (d, sampleNumber); - writeUnaligned (d + 4, static_cast (numBytes)); - memcpy (d + 6, newData, (size_t) numBytes); - } -} - -void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, - const int startSample, - const int numSamples, - const int sampleDeltaToAdd) -{ - Iterator i (otherBuffer); - i.setNextSamplePosition (startSample); - - const uint8* eventData; - int eventSize, position; - - while (i.getNextEvent (eventData, eventSize, position) - && (position < startSample + numSamples || numSamples < 0)) - { - addEvent (eventData, eventSize, position + sampleDeltaToAdd); - } -} - -int MidiBuffer::getNumEvents() const noexcept -{ - int n = 0; - const uint8* const end = data.end(); - - for (const uint8* d = data.begin(); d < end; ++n) - d += MidiBufferHelpers::getEventTotalSize (d); - - return n; -} - -int MidiBuffer::getFirstEventTime() const noexcept -{ - return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0; -} - -int MidiBuffer::getLastEventTime() const noexcept -{ - if (data.size() == 0) - return 0; - - const uint8* const endData = data.end(); - - for (const uint8* d = data.begin();;) - { - const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d); - - if (nextOne >= endData) - return MidiBufferHelpers::getEventTime (d); - - d = nextOne; - } -} - -//============================================================================== -MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept - : buffer (b), data (b.data.begin()) -{ -} - -MidiBuffer::Iterator::~Iterator() noexcept -{ -} - -void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept -{ - data = buffer.data.begin(); - const uint8* const dataEnd = buffer.data.end(); - - while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition) - data += MidiBufferHelpers::getEventTotalSize (data); -} - -bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept -{ - if (data >= buffer.data.end()) - return false; - - samplePosition = MidiBufferHelpers::getEventTime (data); - const int itemSize = MidiBufferHelpers::getEventDataSize (data); - numBytes = itemSize; - midiData = data + sizeof (int32) + sizeof (uint16); - data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; - - return true; -} - -bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept -{ - if (data >= buffer.data.end()) - return false; - - samplePosition = MidiBufferHelpers::getEventTime (data); - const int itemSize = MidiBufferHelpers::getEventDataSize (data); - result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition); - data += sizeof (int32) + sizeof (uint16) + (size_t) itemSize; - - return true; -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h deleted file mode 100644 index e856bfbd2..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Holds a sequence of time-stamped midi events. - - Analogous to the AudioSampleBuffer, this holds a set of midi events with - integer time-stamps. The buffer is kept sorted in order of the time-stamps. - - If you're working with a sequence of midi events that may need to be manipulated - or read/written to a midi file, then MidiMessageSequence is probably a more - appropriate container. MidiBuffer is designed for lower-level streams of raw - midi data. - - @see MidiMessage -*/ -class JUCE_API MidiBuffer -{ -public: - //============================================================================== - /** Creates an empty MidiBuffer. */ - MidiBuffer() noexcept; - - /** Creates a MidiBuffer containing a single midi message. */ - explicit MidiBuffer (const MidiMessage& message) noexcept; - - /** Creates a copy of another MidiBuffer. */ - MidiBuffer (const MidiBuffer&) noexcept; - - /** Makes a copy of another MidiBuffer. */ - MidiBuffer& operator= (const MidiBuffer&) noexcept; - - /** Destructor */ - ~MidiBuffer(); - - //============================================================================== - /** Removes all events from the buffer. */ - void clear() noexcept; - - /** Removes all events between two times from the buffer. - - All events for which (start <= event position < start + numSamples) will - be removed. - */ - void clear (int start, int numSamples); - - /** Returns true if the buffer is empty. - To actually retrieve the events, use a MidiBuffer::Iterator object - */ - bool isEmpty() const noexcept; - - /** Counts the number of events in the buffer. - - This is actually quite a slow operation, as it has to iterate through all - the events, so you might prefer to call isEmpty() if that's all you need - to know. - */ - int getNumEvents() const noexcept; - - /** Adds an event to the buffer. - - The sample number will be used to determine the position of the event in - the buffer, which is always kept sorted. The MidiMessage's timestamp is - ignored. - - If an event is added whose sample position is the same as one or more events - already in the buffer, the new event will be placed after the existing ones. - - To retrieve events, use a MidiBuffer::Iterator object - */ - void addEvent (const MidiMessage& midiMessage, int sampleNumber); - - /** Adds an event to the buffer from raw midi data. - - The sample number will be used to determine the position of the event in - the buffer, which is always kept sorted. - - If an event is added whose sample position is the same as one or more events - already in the buffer, the new event will be placed after the existing ones. - - The event data will be inspected to calculate the number of bytes in length that - the midi event really takes up, so maxBytesOfMidiData may be longer than the data - that actually gets stored. E.g. if you pass in a note-on and a length of 4 bytes, - it'll actually only store 3 bytes. If the midi data is invalid, it might not - add an event at all. - - To retrieve events, use a MidiBuffer::Iterator object - */ - void addEvent (const void* rawMidiData, - int maxBytesOfMidiData, - int sampleNumber); - - /** Adds some events from another buffer to this one. - - @param otherBuffer the buffer containing the events you want to add - @param startSample the lowest sample number in the source buffer for which - events should be added. Any source events whose timestamp is - less than this will be ignored - @param numSamples the valid range of samples from the source buffer for which - events should be added - i.e. events in the source buffer whose - timestamp is greater than or equal to (startSample + numSamples) - will be ignored. If this value is less than 0, all events after - startSample will be taken. - @param sampleDeltaToAdd a value which will be added to the source timestamps of the events - that are added to this buffer - */ - void addEvents (const MidiBuffer& otherBuffer, - int startSample, - int numSamples, - int sampleDeltaToAdd); - - /** Returns the sample number of the first event in the buffer. - If the buffer's empty, this will just return 0. - */ - int getFirstEventTime() const noexcept; - - /** Returns the sample number of the last event in the buffer. - If the buffer's empty, this will just return 0. - */ - int getLastEventTime() const noexcept; - - //============================================================================== - /** Exchanges the contents of this buffer with another one. - - This is a quick operation, because no memory allocating or copying is done, it - just swaps the internal state of the two buffers. - */ - void swapWith (MidiBuffer&) noexcept; - - /** Preallocates some memory for the buffer to use. - This helps to avoid needing to reallocate space when the buffer has messages - added to it. - */ - void ensureSize (size_t minimumNumBytes); - - //============================================================================== - /** - Used to iterate through the events in a MidiBuffer. - - Note that altering the buffer while an iterator is using it will produce - undefined behaviour. - - @see MidiBuffer - */ - class JUCE_API Iterator - { - public: - //============================================================================== - /** Creates an Iterator for this MidiBuffer. */ - Iterator (const MidiBuffer&) noexcept; - - /** Creates a copy of an iterator. */ - Iterator (const Iterator&) noexcept = default; - - /** Destructor. */ - ~Iterator() noexcept; - - //============================================================================== - /** Repositions the iterator so that the next event retrieved will be the first - one whose sample position is at greater than or equal to the given position. - */ - void setNextSamplePosition (int samplePosition) noexcept; - - /** Retrieves a copy of the next event from the buffer. - - @param result on return, this will be the message. The MidiMessage's timestamp - is set to the same value as samplePosition. - @param samplePosition on return, this will be the position of the event, as a - sample index in the buffer - @returns true if an event was found, or false if the iterator has reached - the end of the buffer - */ - bool getNextEvent (MidiMessage& result, - int& samplePosition) noexcept; - - /** Retrieves the next event from the buffer. - - @param midiData on return, this pointer will be set to a block of data containing - the midi message. Note that to make it fast, this is a pointer - directly into the MidiBuffer's internal data, so is only valid - temporarily until the MidiBuffer is altered. - @param numBytesOfMidiData on return, this is the number of bytes of data used by the - midi message - @param samplePosition on return, this will be the position of the event, as a - sample index in the buffer - @returns true if an event was found, or false if the iterator has reached - the end of the buffer - */ - bool getNextEvent (const uint8* &midiData, - int& numBytesOfMidiData, - int& samplePosition) noexcept; - - private: - //============================================================================== - const MidiBuffer& buffer; - const uint8* data; - }; - - /** The raw data holding this buffer. - Obviously access to this data is provided at your own risk. Its internal format could - change in future, so don't write code that relies on it! - */ - Array data; - -private: - JUCE_LEAK_DETECTOR (MidiBuffer) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp deleted file mode 100644 index 6a67a0890..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -namespace MidiFileHelpers -{ - static void writeVariableLengthInt (OutputStream& out, unsigned int v) - { - unsigned int buffer = v & 0x7f; - - while ((v >>= 7) != 0) - { - buffer <<= 8; - buffer |= ((v & 0x7f) | 0x80); - } - - for (;;) - { - out.writeByte ((char) buffer); - - if (buffer & 0x80) - buffer >>= 8; - else - break; - } - } - - static bool parseMidiHeader (const uint8* &data, short& timeFormat, short& fileType, short& numberOfTracks) noexcept - { - unsigned int ch = ByteOrder::bigEndianInt (data); - data += 4; - - if (ch != ByteOrder::bigEndianInt ("MThd")) - { - bool ok = false; - - if (ch == ByteOrder::bigEndianInt ("RIFF")) - { - for (int i = 0; i < 8; ++i) - { - ch = ByteOrder::bigEndianInt (data); - data += 4; - - if (ch == ByteOrder::bigEndianInt ("MThd")) - { - ok = true; - break; - } - } - } - - if (! ok) - return false; - } - - unsigned int bytesRemaining = ByteOrder::bigEndianInt (data); - data += 4; - fileType = (short) ByteOrder::bigEndianShort (data); - data += 2; - numberOfTracks = (short) ByteOrder::bigEndianShort (data); - data += 2; - timeFormat = (short) ByteOrder::bigEndianShort (data); - data += 2; - bytesRemaining -= 6; - data += bytesRemaining; - - return true; - } - - static double convertTicksToSeconds (const double time, - const MidiMessageSequence& tempoEvents, - const int timeFormat) - { - if (timeFormat < 0) - return time / (-(timeFormat >> 8) * (timeFormat & 0xff)); - - double lastTime = 0.0, correctedTime = 0.0; - const double tickLen = 1.0 / (timeFormat & 0x7fff); - double secsPerTick = 0.5 * tickLen; - const int numEvents = tempoEvents.getNumEvents(); - - for (int i = 0; i < numEvents; ++i) - { - const MidiMessage& m = tempoEvents.getEventPointer(i)->message; - const double eventTime = m.getTimeStamp(); - - if (eventTime >= time) - break; - - correctedTime += (eventTime - lastTime) * secsPerTick; - lastTime = eventTime; - - if (m.isTempoMetaEvent()) - secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote(); - - while (i + 1 < numEvents) - { - const MidiMessage& m2 = tempoEvents.getEventPointer(i + 1)->message; - - if (m2.getTimeStamp() != eventTime) - break; - - if (m2.isTempoMetaEvent()) - secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote(); - - ++i; - } - } - - return correctedTime + (time - lastTime) * secsPerTick; - } - - // a comparator that puts all the note-offs before note-ons that have the same time - struct Sorter - { - static int compareElements (const MidiMessageSequence::MidiEventHolder* const first, - const MidiMessageSequence::MidiEventHolder* const second) noexcept - { - const double diff = (first->message.getTimeStamp() - second->message.getTimeStamp()); - - if (diff > 0) return 1; - if (diff < 0) return -1; - if (first->message.isNoteOff() && second->message.isNoteOn()) return -1; - if (first->message.isNoteOn() && second->message.isNoteOff()) return 1; - - return 0; - } - }; - - template - static void findAllMatchingEvents (const OwnedArray& tracks, - MidiMessageSequence& results, - MethodType method) - { - for (int i = 0; i < tracks.size(); ++i) - { - const MidiMessageSequence& track = *tracks.getUnchecked(i); - const int numEvents = track.getNumEvents(); - - for (int j = 0; j < numEvents; ++j) - { - const MidiMessage& m = track.getEventPointer(j)->message; - - if ((m.*method)()) - results.addEvent (m); - } - } - } -} - -//============================================================================== -MidiFile::MidiFile() - : timeFormat ((short) (unsigned short) 0xe728) -{ -} - -MidiFile::~MidiFile() -{ -} - -MidiFile::MidiFile (const MidiFile& other) - : timeFormat (other.timeFormat) -{ - tracks.addCopiesOf (other.tracks); -} - -MidiFile& MidiFile::operator= (const MidiFile& other) -{ - timeFormat = other.timeFormat; - tracks.clear(); - tracks.addCopiesOf (other.tracks); - - return *this; -} - -void MidiFile::clear() -{ - tracks.clear(); -} - -//============================================================================== -int MidiFile::getNumTracks() const noexcept -{ - return tracks.size(); -} - -const MidiMessageSequence* MidiFile::getTrack (const int index) const noexcept -{ - return tracks [index]; -} - -void MidiFile::addTrack (const MidiMessageSequence& trackSequence) -{ - tracks.add (new MidiMessageSequence (trackSequence)); -} - -//============================================================================== -short MidiFile::getTimeFormat() const noexcept -{ - return timeFormat; -} - -void MidiFile::setTicksPerQuarterNote (const int ticks) noexcept -{ - timeFormat = (short) ticks; -} - -void MidiFile::setSmpteTimeFormat (const int framesPerSecond, - const int subframeResolution) noexcept -{ - timeFormat = (short) (((-framesPerSecond) << 8) | subframeResolution); -} - -//============================================================================== -void MidiFile::findAllTempoEvents (MidiMessageSequence& results) const -{ - MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTempoMetaEvent); -} - -void MidiFile::findAllTimeSigEvents (MidiMessageSequence& results) const -{ - MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTimeSignatureMetaEvent); -} - -void MidiFile::findAllKeySigEvents (MidiMessageSequence& results) const -{ - MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isKeySignatureMetaEvent); -} - -double MidiFile::getLastTimestamp() const -{ - double t = 0.0; - - for (int i = tracks.size(); --i >= 0;) - t = jmax (t, tracks.getUnchecked(i)->getEndTime()); - - return t; -} - -//============================================================================== -bool MidiFile::readFrom (InputStream& sourceStream) -{ - clear(); - MemoryBlock data; - - const int maxSensibleMidiFileSize = 200 * 1024 * 1024; - - // (put a sanity-check on the file size, as midi files are generally small) - if (sourceStream.readIntoMemoryBlock (data, maxSensibleMidiFileSize)) - { - size_t size = data.getSize(); - const uint8* d = static_cast (data.getData()); - short fileType, expectedTracks; - - if (size > 16 && MidiFileHelpers::parseMidiHeader (d, timeFormat, fileType, expectedTracks)) - { - size -= (size_t) (d - static_cast (data.getData())); - - int track = 0; - - while (size > 0 && track < expectedTracks) - { - const int chunkType = (int) ByteOrder::bigEndianInt (d); - d += 4; - const int chunkSize = (int) ByteOrder::bigEndianInt (d); - d += 4; - - if (chunkSize <= 0) - break; - - if (chunkType == (int) ByteOrder::bigEndianInt ("MTrk")) - readNextTrack (d, chunkSize); - - size -= (size_t) chunkSize + 8; - d += chunkSize; - ++track; - } - - return true; - } - } - - return false; -} - -void MidiFile::readNextTrack (const uint8* data, int size) -{ - double time = 0; - uint8 lastStatusByte = 0; - - MidiMessageSequence result; - - while (size > 0) - { - int bytesUsed; - const int delay = MidiMessage::readVariableLengthVal (data, bytesUsed); - data += bytesUsed; - size -= bytesUsed; - time += delay; - - int messSize = 0; - const MidiMessage mm (data, size, messSize, lastStatusByte, time); - - if (messSize <= 0) - break; - - size -= messSize; - data += messSize; - - result.addEvent (mm); - - const uint8 firstByte = *(mm.getRawData()); - if ((firstByte & 0xf0) != 0xf0) - lastStatusByte = firstByte; - } - - // use a sort that puts all the note-offs before note-ons that have the same time - MidiFileHelpers::Sorter sorter; - result.list.sort (sorter, true); - - addTrack (result); - tracks.getLast()->updateMatchedPairs(); -} - -//============================================================================== -void MidiFile::convertTimestampTicksToSeconds() -{ - MidiMessageSequence tempoEvents; - findAllTempoEvents (tempoEvents); - findAllTimeSigEvents (tempoEvents); - - if (timeFormat != 0) - { - for (int i = 0; i < tracks.size(); ++i) - { - const MidiMessageSequence& ms = *tracks.getUnchecked(i); - - for (int j = ms.getNumEvents(); --j >= 0;) - { - MidiMessage& m = ms.getEventPointer(j)->message; - m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat)); - } - } - } -} - -//============================================================================== -bool MidiFile::writeTo (OutputStream& out, int midiFileType) -{ - jassert (midiFileType >= 0 && midiFileType <= 2); - - if (! out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd"))) return false; - if (! out.writeIntBigEndian (6)) return false; - if (! out.writeShortBigEndian ((short) midiFileType)) return false; - if (! out.writeShortBigEndian ((short) tracks.size())) return false; - if (! out.writeShortBigEndian (timeFormat)) return false; - - for (int i = 0; i < tracks.size(); ++i) - if (! writeTrack (out, i)) - return false; - - out.flush(); - return true; -} - -bool MidiFile::writeTrack (OutputStream& mainOut, const int trackNum) -{ - MemoryOutputStream out; - const MidiMessageSequence& ms = *tracks.getUnchecked (trackNum); - - int lastTick = 0; - uint8 lastStatusByte = 0; - bool endOfTrackEventWritten = false; - - for (int i = 0; i < ms.getNumEvents(); ++i) - { - const MidiMessage& mm = ms.getEventPointer(i)->message; - - if (mm.isEndOfTrackMetaEvent()) - endOfTrackEventWritten = true; - - const int tick = roundToInt (mm.getTimeStamp()); - const int delta = jmax (0, tick - lastTick); - MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta); - lastTick = tick; - - const uint8* data = mm.getRawData(); - int dataSize = mm.getRawDataSize(); - - const uint8 statusByte = data[0]; - - if (statusByte == lastStatusByte - && (statusByte & 0xf0) != 0xf0 - && dataSize > 1 - && i > 0) - { - ++data; - --dataSize; - } - else if (statusByte == 0xf0) // Write sysex message with length bytes. - { - out.writeByte ((char) statusByte); - - ++data; - --dataSize; - - MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize); - } - - out.write (data, (size_t) dataSize); - lastStatusByte = statusByte; - } - - if (! endOfTrackEventWritten) - { - out.writeByte (0); // (tick delta) - const MidiMessage m (MidiMessage::endOfTrack()); - out.write (m.getRawData(), (size_t) m.getRawDataSize()); - } - - if (! mainOut.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MTrk"))) return false; - if (! mainOut.writeIntBigEndian ((int) out.getDataSize())) return false; - - mainOut << out; - - return true; -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiFile.h b/source/modules/juce_audio_basics/midi/juce_MidiFile.h deleted file mode 100644 index b06589d29..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiFile.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Reads/writes standard midi format files. - - To read a midi file, create a MidiFile object and call its readFrom() method. You - can then get the individual midi tracks from it using the getTrack() method. - - To write a file, create a MidiFile object, add some MidiMessageSequence objects - to it using the addTrack() method, and then call its writeTo() method to stream - it out. - - @see MidiMessageSequence -*/ -class JUCE_API MidiFile -{ -public: - //============================================================================== - /** Creates an empty MidiFile object. - */ - MidiFile(); - - /** Destructor. */ - ~MidiFile(); - - /** Creates a copy of another MidiFile. */ - MidiFile (const MidiFile& other); - - /** Copies from another MidiFile object */ - MidiFile& operator= (const MidiFile& other); - - //============================================================================== - /** Returns the number of tracks in the file. - @see getTrack, addTrack - */ - int getNumTracks() const noexcept; - - /** Returns a pointer to one of the tracks in the file. - @returns a pointer to the track, or nullptr if the index is out-of-range - @see getNumTracks, addTrack - */ - const MidiMessageSequence* getTrack (int index) const noexcept; - - /** Adds a midi track to the file. - This will make its own internal copy of the sequence that is passed-in. - @see getNumTracks, getTrack - */ - void addTrack (const MidiMessageSequence& trackSequence); - - /** Removes all midi tracks from the file. - @see getNumTracks - */ - void clear(); - - /** Returns the raw time format code that will be written to a stream. - - After reading a midi file, this method will return the time-format that - was read from the file's header. It can be changed using the setTicksPerQuarterNote() - or setSmpteTimeFormat() methods. - - If the value returned is positive, it indicates the number of midi ticks - per quarter-note - see setTicksPerQuarterNote(). - - It it's negative, the upper byte indicates the frames-per-second (but negative), and - the lower byte is the number of ticks per frame - see setSmpteTimeFormat(). - */ - short getTimeFormat() const noexcept; - - /** Sets the time format to use when this file is written to a stream. - - If this is called, the file will be written as bars/beats using the - specified resolution, rather than SMPTE absolute times, as would be - used if setSmpteTimeFormat() had been called instead. - - @param ticksPerQuarterNote e.g. 96, 960 - @see setSmpteTimeFormat - */ - void setTicksPerQuarterNote (int ticksPerQuarterNote) noexcept; - - /** Sets the time format to use when this file is written to a stream. - - If this is called, the file will be written using absolute times, rather - than bars/beats as would be the case if setTicksPerBeat() had been called - instead. - - @param framesPerSecond must be 24, 25, 29 or 30 - @param subframeResolution the sub-second resolution, e.g. 4 (midi time code), - 8, 10, 80 (SMPTE bit resolution), or 100. For millisecond - timing, setSmpteTimeFormat (25, 40) - @see setTicksPerBeat - */ - void setSmpteTimeFormat (int framesPerSecond, - int subframeResolution) noexcept; - - //============================================================================== - /** Makes a list of all the tempo-change meta-events from all tracks in the midi file. - Useful for finding the positions of all the tempo changes in a file. - @param tempoChangeEvents a list to which all the events will be added - */ - void findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const; - - /** Makes a list of all the time-signature meta-events from all tracks in the midi file. - Useful for finding the positions of all the tempo changes in a file. - @param timeSigEvents a list to which all the events will be added - */ - void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const; - - /** Makes a list of all the time-signature meta-events from all tracks in the midi file. - @param keySigEvents a list to which all the events will be added - */ - void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const; - - /** Returns the latest timestamp in any of the tracks. - (Useful for finding the length of the file). - */ - double getLastTimestamp() const; - - //============================================================================== - /** Reads a midi file format stream. - - After calling this, you can get the tracks that were read from the file by using the - getNumTracks() and getTrack() methods. - - The timestamps of the midi events in the tracks will represent their positions in - terms of midi ticks. To convert them to seconds, use the convertTimestampTicksToSeconds() - method. - - @returns true if the stream was read successfully - */ - bool readFrom (InputStream& sourceStream); - - /** Writes the midi tracks as a standard midi file. - The midiFileType value is written as the file's format type, which can be 0, 1 - or 2 - see the midi file spec for more info about that. - @returns true if the operation succeeded. - */ - bool writeTo (OutputStream& destStream, int midiFileType = 1); - - /** Converts the timestamp of all the midi events from midi ticks to seconds. - - This will use the midi time format and tempo/time signature info in the - tracks to convert all the timestamps to absolute values in seconds. - */ - void convertTimestampTicksToSeconds(); - - -private: - //============================================================================== - OwnedArray tracks; - short timeFormat; - - void readNextTrack (const uint8*, int size); - bool writeTrack (OutputStream&, int trackNum); - - JUCE_LEAK_DETECTOR (MidiFile) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp b/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp deleted file mode 100644 index 68b8db06b..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -MidiKeyboardState::MidiKeyboardState() -{ - zerostruct (noteStates); -} - -MidiKeyboardState::~MidiKeyboardState() -{ -} - -//============================================================================== -void MidiKeyboardState::reset() -{ - const ScopedLock sl (lock); - zerostruct (noteStates); - eventsToAdd.clear(); -} - -bool MidiKeyboardState::isNoteOn (const int midiChannel, const int n) const noexcept -{ - jassert (midiChannel >= 0 && midiChannel <= 16); - - return isPositiveAndBelow (n, 128) - && (noteStates[n] & (1 << (midiChannel - 1))) != 0; -} - -bool MidiKeyboardState::isNoteOnForChannels (const int midiChannelMask, const int n) const noexcept -{ - return isPositiveAndBelow (n, 128) - && (noteStates[n] & midiChannelMask) != 0; -} - -void MidiKeyboardState::noteOn (const int midiChannel, const int midiNoteNumber, const float velocity) -{ - jassert (midiChannel >= 0 && midiChannel <= 16); - jassert (isPositiveAndBelow (midiNoteNumber, 128)); - - const ScopedLock sl (lock); - - if (isPositiveAndBelow (midiNoteNumber, 128)) - { - const int timeNow = (int) Time::getMillisecondCounter(); - eventsToAdd.addEvent (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity), timeNow); - eventsToAdd.clear (0, timeNow - 500); - - noteOnInternal (midiChannel, midiNoteNumber, velocity); - } -} - -void MidiKeyboardState::noteOnInternal (const int midiChannel, const int midiNoteNumber, const float velocity) -{ - if (isPositiveAndBelow (midiNoteNumber, 128)) - { - noteStates [midiNoteNumber] |= (1 << (midiChannel - 1)); - - for (int i = listeners.size(); --i >= 0;) - listeners.getUnchecked(i)->handleNoteOn (this, midiChannel, midiNoteNumber, velocity); - } -} - -void MidiKeyboardState::noteOff (const int midiChannel, const int midiNoteNumber, const float velocity) -{ - const ScopedLock sl (lock); - - if (isNoteOn (midiChannel, midiNoteNumber)) - { - const int timeNow = (int) Time::getMillisecondCounter(); - eventsToAdd.addEvent (MidiMessage::noteOff (midiChannel, midiNoteNumber), timeNow); - eventsToAdd.clear (0, timeNow - 500); - - noteOffInternal (midiChannel, midiNoteNumber, velocity); - } -} - -void MidiKeyboardState::noteOffInternal (const int midiChannel, const int midiNoteNumber, const float velocity) -{ - if (isNoteOn (midiChannel, midiNoteNumber)) - { - noteStates [midiNoteNumber] &= ~(1 << (midiChannel - 1)); - - for (int i = listeners.size(); --i >= 0;) - listeners.getUnchecked(i)->handleNoteOff (this, midiChannel, midiNoteNumber, velocity); - } -} - -void MidiKeyboardState::allNotesOff (const int midiChannel) -{ - const ScopedLock sl (lock); - - if (midiChannel <= 0) - { - for (int i = 1; i <= 16; ++i) - allNotesOff (i); - } - else - { - for (int i = 0; i < 128; ++i) - noteOff (midiChannel, i, 0.0f); - } -} - -void MidiKeyboardState::processNextMidiEvent (const MidiMessage& message) -{ - if (message.isNoteOn()) - { - noteOnInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity()); - } - else if (message.isNoteOff()) - { - noteOffInternal (message.getChannel(), message.getNoteNumber(), message.getFloatVelocity()); - } - else if (message.isAllNotesOff()) - { - for (int i = 0; i < 128; ++i) - noteOffInternal (message.getChannel(), i, 0.0f); - } -} - -void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, - const int startSample, - const int numSamples, - const bool injectIndirectEvents) -{ - MidiBuffer::Iterator i (buffer); - MidiMessage message; - int time; - - const ScopedLock sl (lock); - - while (i.getNextEvent (message, time)) - processNextMidiEvent (message); - - if (injectIndirectEvents) - { - MidiBuffer::Iterator i2 (eventsToAdd); - const int firstEventToAdd = eventsToAdd.getFirstEventTime(); - const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd); - - while (i2.getNextEvent (message, time)) - { - const int pos = jlimit (0, numSamples - 1, roundToInt ((time - firstEventToAdd) * scaleFactor)); - buffer.addEvent (message, startSample + pos); - } - } - - eventsToAdd.clear(); -} - -//============================================================================== -void MidiKeyboardState::addListener (MidiKeyboardStateListener* const listener) -{ - const ScopedLock sl (lock); - listeners.addIfNotAlreadyThere (listener); -} - -void MidiKeyboardState::removeListener (MidiKeyboardStateListener* const listener) -{ - const ScopedLock sl (lock); - listeners.removeFirstMatchingValue (listener); -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h b/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h deleted file mode 100644 index b3fc4e61e..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -class MidiKeyboardState; - - -//============================================================================== -/** - Receives events from a MidiKeyboardState object. - - @see MidiKeyboardState -*/ -class JUCE_API MidiKeyboardStateListener -{ -public: - //============================================================================== - MidiKeyboardStateListener() noexcept {} - virtual ~MidiKeyboardStateListener() {} - - //============================================================================== - /** Called when one of the MidiKeyboardState's keys is pressed. - - This will be called synchronously when the state is either processing a - buffer in its MidiKeyboardState::processNextMidiBuffer() method, or - when a note is being played with its MidiKeyboardState::noteOn() method. - - Note that this callback could happen from an audio callback thread, so be - careful not to block, and avoid any UI activity in the callback. - */ - virtual void handleNoteOn (MidiKeyboardState* source, - int midiChannel, int midiNoteNumber, float velocity) = 0; - - /** Called when one of the MidiKeyboardState's keys is released. - - This will be called synchronously when the state is either processing a - buffer in its MidiKeyboardState::processNextMidiBuffer() method, or - when a note is being played with its MidiKeyboardState::noteOff() method. - - Note that this callback could happen from an audio callback thread, so be - careful not to block, and avoid any UI activity in the callback. - */ - virtual void handleNoteOff (MidiKeyboardState* source, - int midiChannel, int midiNoteNumber, float velocity) = 0; -}; - - -//============================================================================== -/** - Represents a piano keyboard, keeping track of which keys are currently pressed. - - This object can parse a stream of midi events, using them to update its idea - of which keys are pressed for each individiual midi channel. - - When keys go up or down, it can broadcast these events to listener objects. - - It also allows key up/down events to be triggered with its noteOn() and noteOff() - methods, and midi messages for these events will be merged into the - midi stream that gets processed by processNextMidiBuffer(). -*/ -class JUCE_API MidiKeyboardState -{ -public: - //============================================================================== - MidiKeyboardState(); - ~MidiKeyboardState(); - - //============================================================================== - /** Resets the state of the object. - - All internal data for all the channels is reset, but no events are sent as a - result. - - If you want to release any keys that are currently down, and to send out note-up - midi messages for this, use the allNotesOff() method instead. - */ - void reset(); - - /** Returns true if the given midi key is currently held down for the given midi channel. - - The channel number must be between 1 and 16. If you want to see if any notes are - on for a range of channels, use the isNoteOnForChannels() method. - */ - bool isNoteOn (int midiChannel, int midiNoteNumber) const noexcept; - - /** Returns true if the given midi key is currently held down on any of a set of midi channels. - - The channel mask has a bit set for each midi channel you want to test for - bit - 0 = midi channel 1, bit 1 = midi channel 2, etc. - - If a note is on for at least one of the specified channels, this returns true. - */ - bool isNoteOnForChannels (int midiChannelMask, int midiNoteNumber) const noexcept; - - /** Turns a specified note on. - - This will cause a suitable midi note-on event to be injected into the midi buffer during the - next call to processNextMidiBuffer(). - - It will also trigger a synchronous callback to the listeners to tell them that the key has - gone down. - */ - void noteOn (int midiChannel, int midiNoteNumber, float velocity); - - /** Turns a specified note off. - - This will cause a suitable midi note-off event to be injected into the midi buffer during the - next call to processNextMidiBuffer(). - - It will also trigger a synchronous callback to the listeners to tell them that the key has - gone up. - - But if the note isn't acutally down for the given channel, this method will in fact do nothing. - */ - void noteOff (int midiChannel, int midiNoteNumber, float velocity); - - /** This will turn off any currently-down notes for the given midi channel. - - If you pass 0 for the midi channel, it will in fact turn off all notes on all channels. - - Calling this method will make calls to noteOff(), so can trigger synchronous callbacks - and events being added to the midi stream. - */ - void allNotesOff (int midiChannel); - - //============================================================================== - /** Looks at a key-up/down event and uses it to update the state of this object. - - To process a buffer full of midi messages, use the processNextMidiBuffer() method - instead. - */ - void processNextMidiEvent (const MidiMessage& message); - - /** Scans a midi stream for up/down events and adds its own events to it. - - This will look for any up/down events and use them to update the internal state, - synchronously making suitable callbacks to the listeners. - - If injectIndirectEvents is true, then midi events to produce the recent noteOn() - and noteOff() calls will be added into the buffer. - - Only the section of the buffer whose timestamps are between startSample and - (startSample + numSamples) will be affected, and any events added will be placed - between these times. - - If you're going to use this method, you'll need to keep calling it regularly for - it to work satisfactorily. - - To process a single midi event at a time, use the processNextMidiEvent() method - instead. - */ - void processNextMidiBuffer (MidiBuffer& buffer, - int startSample, - int numSamples, - bool injectIndirectEvents); - - //============================================================================== - /** Registers a listener for callbacks when keys go up or down. - @see removeListener - */ - void addListener (MidiKeyboardStateListener* listener); - - /** Deregisters a listener. - @see addListener - */ - void removeListener (MidiKeyboardStateListener* listener); - -private: - //============================================================================== - CriticalSection lock; - uint16 noteStates [128]; - MidiBuffer eventsToAdd; - Array listeners; - - void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity); - void noteOffInternal (int midiChannel, int midiNoteNumber, float velocity); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp deleted file mode 100644 index 054981cae..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ /dev/null @@ -1,1126 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -namespace MidiHelpers -{ - inline uint8 initialByte (const int type, const int channel) noexcept - { - return (uint8) (type | jlimit (0, 15, channel - 1)); - } - - inline uint8 validVelocity (const int v) noexcept - { - return (uint8) jlimit (0, 127, v); - } -} - -//============================================================================== -uint8 MidiMessage::floatValueToMidiByte (const float v) noexcept -{ - jassert (v >= 0 && v <= 1.0f); // if your value is > 1, maybe you're passing an - // integer value to a float method by mistake? - - return MidiHelpers::validVelocity (roundToInt (v * 127.0f)); -} - -uint16 MidiMessage::pitchbendToPitchwheelPos (const float pitchbend, - const float pitchbendRange) noexcept -{ - // can't translate a pitchbend value that is outside of the given range! - jassert (std::abs (pitchbend) <= pitchbendRange); - - return static_cast (pitchbend > 0.0f - ? jmap (pitchbend, 0.0f, pitchbendRange, 8192.0f, 16383.0f) - : jmap (pitchbend, -pitchbendRange, 0.0f, 0.0f, 8192.0f)); -} - -//============================================================================== -int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept -{ - numBytesUsed = 0; - int v = 0, i; - - do - { - i = (int) *data++; - - if (++numBytesUsed > 6) - break; - - v = (v << 7) + (i & 0x7f); - - } while (i & 0x80); - - return v; -} - -int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept -{ - // this method only works for valid starting bytes of a short midi message - jassert (firstByte >= 0x80 && firstByte != 0xf0 && firstByte != 0xf7); - - static const char messageLengths[] = - { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 1, 2, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 - }; - - return messageLengths [firstByte & 0x7f]; -} - -//============================================================================== -MidiMessage::MidiMessage() noexcept - : size (2) -{ - packedData.asBytes[0] = 0xf0; - packedData.asBytes[1] = 0xf7; -} - -MidiMessage::MidiMessage (const void* const d, const int dataSize, const double t) - : timeStamp (t), size (dataSize) -{ - jassert (dataSize > 0); - // this checks that the length matches the data.. - jassert (dataSize > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size); - - memcpy (allocateSpace (dataSize), d, (size_t) dataSize); -} - -MidiMessage::MidiMessage (const int byte1, const double t) noexcept - : timeStamp (t), size (1) -{ - packedData.asBytes[0] = (uint8) byte1; - - // check that the length matches the data.. - jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 1); -} - -MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept - : timeStamp (t), size (2) -{ - packedData.asBytes[0] = (uint8) byte1; - packedData.asBytes[1] = (uint8) byte2; - - // check that the length matches the data.. - jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 2); -} - -MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept - : timeStamp (t), size (3) -{ - packedData.asBytes[0] = (uint8) byte1; - packedData.asBytes[1] = (uint8) byte2; - packedData.asBytes[2] = (uint8) byte3; - - // check that the length matches the data.. - jassert (byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == 3); -} - -MidiMessage::MidiMessage (const MidiMessage& other) - : timeStamp (other.timeStamp), size (other.size) -{ - if (isHeapAllocated()) - memcpy (allocateSpace (size), other.getData(), (size_t) size); - else - packedData.allocatedData = other.packedData.allocatedData; -} - -MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) - : timeStamp (newTimeStamp), size (other.size) -{ - if (isHeapAllocated()) - memcpy (allocateSpace (size), other.getData(), (size_t) size); - else - packedData.allocatedData = other.packedData.allocatedData; -} - -MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, - double t, bool sysexHasEmbeddedLength) - : timeStamp (t) -{ - auto src = static_cast (srcData); - auto byte = (unsigned int) *src; - - if (byte < 0x80) - { - byte = (unsigned int) lastStatusByte; - numBytesUsed = -1; - } - else - { - numBytesUsed = 0; - --sz; - ++src; - } - - if (byte >= 0x80) - { - if (byte == 0xf0) - { - auto d = src; - bool haveReadAllLengthBytes = ! sysexHasEmbeddedLength; - int numVariableLengthSysexBytes = 0; - - while (d < src + sz) - { - if (*d >= 0x80) - { - if (*d == 0xf7) - { - ++d; // include the trailing 0xf7 when we hit it - break; - } - - if (haveReadAllLengthBytes) // if we see a 0x80 bit set after the initial data length - break; // bytes, assume it's the end of the sysex - - ++numVariableLengthSysexBytes; - } - else if (! haveReadAllLengthBytes) - { - haveReadAllLengthBytes = true; - ++numVariableLengthSysexBytes; - } - - ++d; - } - - src += numVariableLengthSysexBytes; - size = 1 + (int) (d - src); - - auto dest = allocateSpace (size); - *dest = (uint8) byte; - memcpy (dest + 1, src, (size_t) (size - 1)); - - numBytesUsed += (numVariableLengthSysexBytes + size); // (these aren't counted in the size) - } - else if (byte == 0xff) - { - int n; - const int bytesLeft = readVariableLengthVal (src + 1, n); - size = jmin (sz + 1, n + 2 + bytesLeft); - - auto dest = allocateSpace (size); - *dest = (uint8) byte; - memcpy (dest + 1, src, (size_t) size - 1); - - numBytesUsed += size; - } - else - { - size = getMessageLengthFromFirstByte ((uint8) byte); - packedData.asBytes[0] = (uint8) byte; - - if (size > 1) - { - packedData.asBytes[1] = (sz > 0 ? src[0] : 0); - - if (size > 2) - packedData.asBytes[2] = (sz > 1 ? src[1] : 0); - } - - numBytesUsed += jmin (size, sz + 1); - } - } - else - { - packedData.allocatedData = nullptr; - size = 0; - } -} - -MidiMessage& MidiMessage::operator= (const MidiMessage& other) -{ - if (this != &other) - { - if (other.isHeapAllocated()) - { - if (isHeapAllocated()) - packedData.allocatedData = static_cast (std::realloc (packedData.allocatedData, (size_t) other.size)); - else - packedData.allocatedData = static_cast (std::malloc ((size_t) other.size)); - - memcpy (packedData.allocatedData, other.packedData.allocatedData, (size_t) other.size); - } - else - { - if (isHeapAllocated()) - std::free (packedData.allocatedData); - - packedData.allocatedData = other.packedData.allocatedData; - } - - timeStamp = other.timeStamp; - size = other.size; - } - - return *this; -} - -MidiMessage::MidiMessage (MidiMessage&& other) noexcept - : timeStamp (other.timeStamp), size (other.size) -{ - packedData.allocatedData = other.packedData.allocatedData; - other.size = 0; -} - -MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept -{ - packedData.allocatedData = other.packedData.allocatedData; - timeStamp = other.timeStamp; - size = other.size; - other.size = 0; - return *this; -} - -MidiMessage::~MidiMessage() noexcept -{ - if (isHeapAllocated()) - std::free (packedData.allocatedData); -} - -uint8* MidiMessage::allocateSpace (int bytes) -{ - if (bytes > (int) sizeof (packedData)) - { - auto d = static_cast (std::malloc ((size_t) bytes)); - packedData.allocatedData = d; - return d; - } - - return packedData.asBytes; -} - -String MidiMessage::getDescription() const -{ - if (isNoteOn()) return "Note on " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel()); - if (isNoteOff()) return "Note off " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + " Velocity " + String (getVelocity()) + " Channel " + String (getChannel()); - if (isProgramChange()) return "Program change " + String (getProgramChangeNumber()) + " Channel " + String (getChannel()); - if (isPitchWheel()) return "Pitch wheel " + String (getPitchWheelValue()) + " Channel " + String (getChannel()); - if (isAftertouch()) return "Aftertouch " + MidiMessage::getMidiNoteName (getNoteNumber(), true, true, 3) + ": " + String (getAfterTouchValue()) + " Channel " + String (getChannel()); - if (isChannelPressure()) return "Channel pressure " + String (getChannelPressureValue()) + " Channel " + String (getChannel()); - if (isAllNotesOff()) return "All notes off Channel " + String (getChannel()); - if (isAllSoundOff()) return "All sound off Channel " + String (getChannel()); - if (isMetaEvent()) return "Meta event"; - - if (isController()) - { - String name (MidiMessage::getControllerName (getControllerNumber())); - - if (name.isEmpty()) - name = String (getControllerNumber()); - - return "Controller " + name + ": " + String (getControllerValue()) + " Channel " + String (getChannel()); - } - - return String::toHexString (getRawData(), getRawDataSize()); -} - -int MidiMessage::getChannel() const noexcept -{ - auto data = getRawData(); - - if ((data[0] & 0xf0) != 0xf0) - return (data[0] & 0xf) + 1; - - return 0; -} - -bool MidiMessage::isForChannel (const int channel) const noexcept -{ - jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 - - auto data = getRawData(); - - return ((data[0] & 0xf) == channel - 1) - && ((data[0] & 0xf0) != 0xf0); -} - -void MidiMessage::setChannel (const int channel) noexcept -{ - jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 - - auto data = getData(); - - if ((data[0] & 0xf0) != (uint8) 0xf0) - data[0] = (uint8) ((data[0] & (uint8) 0xf0) - | (uint8)(channel - 1)); -} - -bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept -{ - auto data = getRawData(); - - return ((data[0] & 0xf0) == 0x90) - && (returnTrueForVelocity0 || data[2] != 0); -} - -bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept -{ - auto data = getRawData(); - - return ((data[0] & 0xf0) == 0x80) - || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90)); -} - -bool MidiMessage::isNoteOnOrOff() const noexcept -{ - auto d = getRawData()[0] & 0xf0; - return (d == 0x90) || (d == 0x80); -} - -int MidiMessage::getNoteNumber() const noexcept -{ - return getRawData()[1]; -} - -void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept -{ - if (isNoteOnOrOff() || isAftertouch()) - getData()[1] = (uint8) (newNoteNumber & 127); -} - -uint8 MidiMessage::getVelocity() const noexcept -{ - if (isNoteOnOrOff()) - return getRawData()[2]; - - return 0; -} - -float MidiMessage::getFloatVelocity() const noexcept -{ - return getVelocity() * (1.0f / 127.0f); -} - -void MidiMessage::setVelocity (const float newVelocity) noexcept -{ - if (isNoteOnOrOff()) - getData()[2] = floatValueToMidiByte (newVelocity); -} - -void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept -{ - if (isNoteOnOrOff()) - { - auto data = getData(); - data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2])); - } -} - -bool MidiMessage::isAftertouch() const noexcept -{ - return (getRawData()[0] & 0xf0) == 0xa0; -} - -int MidiMessage::getAfterTouchValue() const noexcept -{ - jassert (isAftertouch()); - return getRawData()[2]; -} - -MidiMessage MidiMessage::aftertouchChange (const int channel, - const int noteNum, - const int aftertouchValue) noexcept -{ - jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 - jassert (isPositiveAndBelow (noteNum, 128)); - jassert (isPositiveAndBelow (aftertouchValue, 128)); - - return MidiMessage (MidiHelpers::initialByte (0xa0, channel), - noteNum & 0x7f, - aftertouchValue & 0x7f); -} - -bool MidiMessage::isChannelPressure() const noexcept -{ - return (getRawData()[0] & 0xf0) == 0xd0; -} - -int MidiMessage::getChannelPressureValue() const noexcept -{ - jassert (isChannelPressure()); - return getRawData()[1]; -} - -MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept -{ - jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 - jassert (isPositiveAndBelow (pressure, 128)); - - return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f); -} - -bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && getRawData()[2] >= 64; } -bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && getRawData()[2] < 64; } - -bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && getRawData()[2] >= 64; } -bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && getRawData()[2] < 64; } - -bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && getRawData()[2] >= 64; } -bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && getRawData()[2] < 64; } - - -bool MidiMessage::isProgramChange() const noexcept -{ - return (getRawData()[0] & 0xf0) == 0xc0; -} - -int MidiMessage::getProgramChangeNumber() const noexcept -{ - jassert (isProgramChange()); - return getRawData()[1]; -} - -MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept -{ - jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 - - return MidiMessage (MidiHelpers::initialByte (0xc0, channel), programNumber & 0x7f); -} - -bool MidiMessage::isPitchWheel() const noexcept -{ - return (getRawData()[0] & 0xf0) == 0xe0; -} - -int MidiMessage::getPitchWheelValue() const noexcept -{ - jassert (isPitchWheel()); - auto data = getRawData(); - return data[1] | (data[2] << 7); -} - -MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noexcept -{ - jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 - jassert (isPositiveAndBelow (position, 0x4000)); - - return MidiMessage (MidiHelpers::initialByte (0xe0, channel), - position & 127, (position >> 7) & 127); -} - -bool MidiMessage::isController() const noexcept -{ - return (getRawData()[0] & 0xf0) == 0xb0; -} - -bool MidiMessage::isControllerOfType (const int controllerType) const noexcept -{ - auto data = getRawData(); - return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType; -} - -int MidiMessage::getControllerNumber() const noexcept -{ - jassert (isController()); - return getRawData()[1]; -} - -int MidiMessage::getControllerValue() const noexcept -{ - jassert (isController()); - return getRawData()[2]; -} - -MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept -{ - // the channel must be between 1 and 16 inclusive - jassert (channel > 0 && channel <= 16); - - return MidiMessage (MidiHelpers::initialByte (0xb0, channel), - controllerType & 127, value & 127); -} - -MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const uint8 velocity) noexcept -{ - jassert (channel > 0 && channel <= 16); - jassert (isPositiveAndBelow (noteNumber, 128)); - - return MidiMessage (MidiHelpers::initialByte (0x90, channel), - noteNumber & 127, MidiHelpers::validVelocity (velocity)); -} - -MidiMessage MidiMessage::noteOn (const int channel, const int noteNumber, const float velocity) noexcept -{ - return noteOn (channel, noteNumber, floatValueToMidiByte (velocity)); -} - -MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, uint8 velocity) noexcept -{ - jassert (channel > 0 && channel <= 16); - jassert (isPositiveAndBelow (noteNumber, 128)); - - return MidiMessage (MidiHelpers::initialByte (0x80, channel), - noteNumber & 127, MidiHelpers::validVelocity (velocity)); -} - -MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber, float velocity) noexcept -{ - return noteOff (channel, noteNumber, floatValueToMidiByte (velocity)); -} - -MidiMessage MidiMessage::noteOff (const int channel, const int noteNumber) noexcept -{ - jassert (channel > 0 && channel <= 16); - jassert (isPositiveAndBelow (noteNumber, 128)); - - return MidiMessage (MidiHelpers::initialByte (0x80, channel), noteNumber & 127, 0); -} - -MidiMessage MidiMessage::allNotesOff (const int channel) noexcept -{ - return controllerEvent (channel, 123, 0); -} - -bool MidiMessage::isAllNotesOff() const noexcept -{ - auto data = getRawData(); - return (data[0] & 0xf0) == 0xb0 && data[1] == 123; -} - -MidiMessage MidiMessage::allSoundOff (const int channel) noexcept -{ - return controllerEvent (channel, 120, 0); -} - -bool MidiMessage::isAllSoundOff() const noexcept -{ - auto data = getRawData(); - return data[1] == 120 && (data[0] & 0xf0) == 0xb0; -} - -MidiMessage MidiMessage::allControllersOff (const int channel) noexcept -{ - return controllerEvent (channel, 121, 0); -} - -MidiMessage MidiMessage::masterVolume (const float volume) -{ - auto vol = jlimit (0, 0x3fff, roundToInt (volume * 0x4000)); - - return { 0xf0, 0x7f, 0x7f, 0x04, 0x01, vol & 0x7f, vol >> 7, 0xf7 }; -} - -//============================================================================== -bool MidiMessage::isSysEx() const noexcept -{ - return *getRawData() == 0xf0; -} - -MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize) -{ - HeapBlock m ((size_t) dataSize + 2); - - m[0] = 0xf0; - memcpy (m + 1, sysexData, (size_t) dataSize); - m[dataSize + 1] = 0xf7; - - return MidiMessage (m, dataSize + 2); -} - -const uint8* MidiMessage::getSysExData() const noexcept -{ - return isSysEx() ? getRawData() + 1 : nullptr; -} - -int MidiMessage::getSysExDataSize() const noexcept -{ - return isSysEx() ? size - 2 : 0; -} - -//============================================================================== -bool MidiMessage::isMetaEvent() const noexcept { return *getRawData() == 0xff; } -bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0xfe; } - -int MidiMessage::getMetaEventType() const noexcept -{ - auto data = getRawData(); - return *data != 0xff ? -1 : data[1]; -} - -int MidiMessage::getMetaEventLength() const noexcept -{ - auto data = getRawData(); - - if (*data == 0xff) - { - int n; - return jmin (size - 2, readVariableLengthVal (data + 2, n)); - } - - return 0; -} - -const uint8* MidiMessage::getMetaEventData() const noexcept -{ - jassert (isMetaEvent()); - - int n; - auto d = getRawData() + 2; - readVariableLengthVal (d, n); - return d + n; -} - -bool MidiMessage::isTrackMetaEvent() const noexcept { return getMetaEventType() == 0; } -bool MidiMessage::isEndOfTrackMetaEvent() const noexcept { return getMetaEventType() == 47; } - -bool MidiMessage::isTextMetaEvent() const noexcept -{ - auto t = getMetaEventType(); - return t > 0 && t < 16; -} - -String MidiMessage::getTextFromTextMetaEvent() const -{ - auto textData = reinterpret_cast (getMetaEventData()); - - return String (CharPointer_UTF8 (textData), - CharPointer_UTF8 (textData + getMetaEventLength())); -} - -MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) -{ - jassert (type > 0 && type < 16); - - MidiMessage result; - - const size_t textSize = text.text.sizeInBytes() - 1; - - uint8 header[8]; - size_t n = sizeof (header); - - header[--n] = (uint8) (textSize & 0x7f); - - for (size_t i = textSize; (i >>= 7) != 0;) - header[--n] = (uint8) ((i & 0x7f) | 0x80); - - header[--n] = (uint8) type; - header[--n] = 0xff; - - const size_t headerLen = sizeof (header) - n; - const int totalSize = (int) (headerLen + textSize); - - auto dest = result.allocateSpace (totalSize); - result.size = totalSize; - - memcpy (dest, header + n, headerLen); - memcpy (dest + headerLen, text.text.getAddress(), textSize); - - return result; -} - -bool MidiMessage::isTrackNameEvent() const noexcept { auto data = getRawData(); return (data[1] == 3) && (*data == 0xff); } -bool MidiMessage::isTempoMetaEvent() const noexcept { auto data = getRawData(); return (data[1] == 81) && (*data == 0xff); } -bool MidiMessage::isMidiChannelMetaEvent() const noexcept { auto data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } - -int MidiMessage::getMidiChannelMetaEventChannel() const noexcept -{ - jassert (isMidiChannelMetaEvent()); - return getRawData()[3] + 1; -} - -double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept -{ - if (! isTempoMetaEvent()) - return 0.0; - - auto d = getMetaEventData(); - - return (((unsigned int) d[0] << 16) - | ((unsigned int) d[1] << 8) - | d[2]) - / 1000000.0; -} - -double MidiMessage::getTempoMetaEventTickLength (const short timeFormat) const noexcept -{ - if (timeFormat > 0) - { - if (! isTempoMetaEvent()) - return 0.5 / timeFormat; - - return getTempoSecondsPerQuarterNote() / timeFormat; - } - - const int frameCode = (-timeFormat) >> 8; - double framesPerSecond; - - switch (frameCode) - { - case 24: framesPerSecond = 24.0; break; - case 25: framesPerSecond = 25.0; break; - case 29: framesPerSecond = 30.0 * 1000.0 / 1001.0; break; - case 30: framesPerSecond = 30.0; break; - default: framesPerSecond = 30.0; break; - } - - return (1.0 / framesPerSecond) / (timeFormat & 0xff); -} - -MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcept -{ - return { 0xff, 81, 3, - (uint8) (microsecondsPerQuarterNote >> 16), - (uint8) (microsecondsPerQuarterNote >> 8), - (uint8) microsecondsPerQuarterNote }; -} - -bool MidiMessage::isTimeSignatureMetaEvent() const noexcept -{ - auto data = getRawData(); - return (data[1] == 0x58) && (*data == (uint8) 0xff); -} - -void MidiMessage::getTimeSignatureInfo (int& numerator, int& denominator) const noexcept -{ - if (isTimeSignatureMetaEvent()) - { - auto d = getMetaEventData(); - numerator = d[0]; - denominator = 1 << d[1]; - } - else - { - numerator = 4; - denominator = 4; - } -} - -MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int denominator) -{ - int n = 1; - int powerOfTwo = 0; - - while (n < denominator) - { - n <<= 1; - ++powerOfTwo; - } - - return { 0xff, 0x58, 0x04, numerator, powerOfTwo, 1, 96 }; -} - -MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept -{ - return { 0xff, 0x20, 0x01, jlimit (0, 0xff, channel - 1) }; -} - -bool MidiMessage::isKeySignatureMetaEvent() const noexcept -{ - return getMetaEventType() == 0x59; -} - -int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept -{ - return (int) (int8) getMetaEventData()[0]; -} - -bool MidiMessage::isKeySignatureMajorKey() const noexcept -{ - return getMetaEventData()[1] == 0; -} - -MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey) -{ - jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7); - - return { 0xff, 0x59, 0x02, numberOfSharpsOrFlats, isMinorKey ? 1 : 0 }; -} - -MidiMessage MidiMessage::endOfTrack() noexcept -{ - return { 0xff, 0x2f, 0x00 }; -} - -//============================================================================== -bool MidiMessage::isSongPositionPointer() const noexcept { return *getRawData() == 0xf2; } -int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { auto data = getRawData(); return data[1] | (data[2] << 7); } - -MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept -{ - return { 0xf2, - positionInMidiBeats & 127, - (positionInMidiBeats >> 7) & 127 }; -} - -bool MidiMessage::isMidiStart() const noexcept { return *getRawData() == 0xfa; } -MidiMessage MidiMessage::midiStart() noexcept { return MidiMessage (0xfa); } - -bool MidiMessage::isMidiContinue() const noexcept { return *getRawData() == 0xfb; } -MidiMessage MidiMessage::midiContinue() noexcept { return MidiMessage (0xfb); } - -bool MidiMessage::isMidiStop() const noexcept { return *getRawData() == 0xfc; } -MidiMessage MidiMessage::midiStop() noexcept { return MidiMessage (0xfc); } - -bool MidiMessage::isMidiClock() const noexcept { return *getRawData() == 0xf8; } -MidiMessage MidiMessage::midiClock() noexcept { return MidiMessage (0xf8); } - -bool MidiMessage::isQuarterFrame() const noexcept { return *getRawData() == 0xf1; } -int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; } -int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) getRawData()[1]) & 0x0f; } - -MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept -{ - return MidiMessage (0xf1, (sequenceNumber << 4) | value); -} - -bool MidiMessage::isFullFrame() const noexcept -{ - auto data = getRawData(); - - return data[0] == 0xf0 - && data[1] == 0x7f - && size >= 10 - && data[3] == 0x01 - && data[4] == 0x01; -} - -void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds, int& frames, - MidiMessage::SmpteTimecodeType& timecodeType) const noexcept -{ - jassert (isFullFrame()); - - auto data = getRawData(); - timecodeType = (SmpteTimecodeType) (data[5] >> 5); - hours = data[5] & 0x1f; - minutes = data[6]; - seconds = data[7]; - frames = data[8]; -} - -MidiMessage MidiMessage::fullFrame (int hours, int minutes, int seconds, int frames, - MidiMessage::SmpteTimecodeType timecodeType) -{ - return { 0xf0, 0x7f, 0x7f, 0x01, 0x01, - (hours & 0x01f) | (timecodeType << 5), - minutes, seconds, frames, - 0xf7 }; -} - -bool MidiMessage::isMidiMachineControlMessage() const noexcept -{ - auto data = getRawData(); - - return data[0] == 0xf0 - && data[1] == 0x7f - && data[3] == 0x06 - && size > 5; -} - -MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand() const noexcept -{ - jassert (isMidiMachineControlMessage()); - - return (MidiMachineControlCommand) getRawData()[4]; -} - -MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command) -{ - return { 0xf0, 0x7f, 0, 6, command, 0xf7 }; -} - -//============================================================================== -bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept -{ - auto data = getRawData(); - - if (size >= 12 - && data[0] == 0xf0 - && data[1] == 0x7f - && data[3] == 0x06 - && data[4] == 0x44 - && data[5] == 0x06 - && data[6] == 0x01) - { - hours = data[7] % 24; // (that some machines send out hours > 24) - minutes = data[8]; - seconds = data[9]; - frames = data[10]; - - return true; - } - - return false; -} - -MidiMessage MidiMessage::midiMachineControlGoto (int hours, int minutes, int seconds, int frames) -{ - return { 0xf0, 0x7f, 0, 6, 0x44, 6, 1, hours, minutes, seconds, frames, 0xf7 }; -} - -//============================================================================== -String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctaveNumber, int octaveNumForMiddleC) -{ - static const char* const sharpNoteNames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }; - static const char* const flatNoteNames[] = { "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B" }; - - if (isPositiveAndBelow (note, 128)) - { - String s (useSharps ? sharpNoteNames [note % 12] - : flatNoteNames [note % 12]); - - if (includeOctaveNumber) - s << (note / 12 + (octaveNumForMiddleC - 5)); - - return s; - } - - return {}; -} - -double MidiMessage::getMidiNoteInHertz (const int noteNumber, const double frequencyOfA) noexcept -{ - return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0); -} - -bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept -{ - return ((1 << (noteNumber % 12)) & 0x054a) != 0; -} - -const char* MidiMessage::getGMInstrumentName (const int n) -{ - static const char* names[] = - { - NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"), - NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"), - NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"), - NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"), - NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"), - NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"), - NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"), - NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"), - NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"), - NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"), - NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"), - NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"), - NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"), - NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"), - NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"), - NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"), - NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"), - NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"), - NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"), - NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"), - NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"), - NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"), - NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"), - NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"), - NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"), - NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"), - NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"), - NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"), - NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"), - NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"), - NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"), - NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot") - }; - - return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; -} - -const char* MidiMessage::getGMInstrumentBankName (const int n) -{ - static const char* names[] = - { - NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"), - NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"), - NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"), - NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects") - }; - - return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; -} - -const char* MidiMessage::getRhythmInstrumentName (const int n) -{ - static const char* names[] = - { - NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"), - NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"), - NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"), - NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"), - NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"), - NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"), - NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"), - NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"), - NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"), - NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"), - NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"), - NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle") - }; - - return (n >= 35 && n <= 81) ? names [n - 35] : nullptr; -} - -const char* MidiMessage::getControllerName (const int n) -{ - static const char* names[] = - { - NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"), - nullptr, - NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"), - NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"), - nullptr, - NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"), - NEEDS_TRANS("Effect Control 2 (coarse)"), - nullptr, nullptr, - NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"), - NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"), - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"), - nullptr, - NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"), - NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"), - NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"), - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"), - NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"), - NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"), - NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"), - NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"), - NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"), - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"), - NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"), - NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"), - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"), - NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation") - }; - - return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h b/source/modules/juce_audio_basics/midi/juce_MidiMessage.h deleted file mode 100644 index 7fcefe2a2..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ /dev/null @@ -1,940 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Encapsulates a MIDI message. - - @see MidiMessageSequence, MidiOutput, MidiInput -*/ -class JUCE_API MidiMessage -{ -public: - //============================================================================== - /** Creates a 3-byte short midi message. - - @param byte1 message byte 1 - @param byte2 message byte 2 - @param byte3 message byte 3 - @param timeStamp the time to give the midi message - this value doesn't - use any particular units, so will be application-specific - */ - MidiMessage (int byte1, int byte2, int byte3, double timeStamp = 0) noexcept; - - /** Creates a 2-byte short midi message. - - @param byte1 message byte 1 - @param byte2 message byte 2 - @param timeStamp the time to give the midi message - this value doesn't - use any particular units, so will be application-specific - */ - MidiMessage (int byte1, int byte2, double timeStamp = 0) noexcept; - - /** Creates a 1-byte short midi message. - - @param byte1 message byte 1 - @param timeStamp the time to give the midi message - this value doesn't - use any particular units, so will be application-specific - */ - MidiMessage (int byte1, double timeStamp = 0) noexcept; - - /** Creates a midi message from a list of bytes. */ - template - MidiMessage (int byte1, int byte2, int byte3, Data... otherBytes) : size (3 + sizeof... (otherBytes)) - { - // this checks that the length matches the data.. - jassert (size > 3 || byte1 >= 0xf0 || getMessageLengthFromFirstByte ((uint8) byte1) == size); - - const uint8 data[] = { (uint8) byte1, (uint8) byte2, (uint8) byte3, static_cast (otherBytes)... }; - memcpy (allocateSpace (size), data, (size_t) size); - } - - - /** Creates a midi message from a block of data. */ - MidiMessage (const void* data, int numBytes, double timeStamp = 0); - - /** Reads the next midi message from some data. - - This will read as many bytes from a data stream as it needs to make a - complete message, and will return the number of bytes it used. This lets - you read a sequence of midi messages from a file or stream. - - @param data the data to read from - @param maxBytesToUse the maximum number of bytes it's allowed to read - @param numBytesUsed returns the number of bytes that were actually needed - @param lastStatusByte in a sequence of midi messages, the initial byte - can be dropped from a message if it's the same as the - first byte of the previous message, so this lets you - supply the byte to use if the first byte of the message - has in fact been dropped. - @param timeStamp the time to give the midi message - this value doesn't - use any particular units, so will be application-specific - @param sysexHasEmbeddedLength when reading sysexes, this flag indicates whether - to expect the data to begin with a variable-length field - indicating its size - */ - MidiMessage (const void* data, int maxBytesToUse, - int& numBytesUsed, uint8 lastStatusByte, - double timeStamp = 0, - bool sysexHasEmbeddedLength = true); - - /** Creates an active-sense message. - Since the MidiMessage has to contain a valid message, this default constructor - just initialises it with an empty sysex message. - */ - MidiMessage() noexcept; - - /** Creates a copy of another midi message. */ - MidiMessage (const MidiMessage&); - - /** Creates a copy of another midi message, with a different timestamp. */ - MidiMessage (const MidiMessage&, double newTimeStamp); - - /** Destructor. */ - ~MidiMessage() noexcept; - - /** Copies this message from another one. */ - MidiMessage& operator= (const MidiMessage& other); - - /** Move constructor */ - MidiMessage (MidiMessage&&) noexcept; - - /** Move assignment operator */ - MidiMessage& operator= (MidiMessage&&) noexcept; - - //============================================================================== - /** Returns a pointer to the raw midi data. - @see getRawDataSize - */ - const uint8* getRawData() const noexcept { return getData(); } - - /** Returns the number of bytes of data in the message. - @see getRawData - */ - int getRawDataSize() const noexcept { return size; } - - //============================================================================== - /** Returns a human-readable description of the midi message as a string, - for example "Note On C#3 Velocity 120 Channel 1". - */ - String getDescription() const; - - //============================================================================== - /** Returns the timestamp associated with this message. - - The exact meaning of this time and its units will vary, as messages are used in - a variety of different contexts. - - If you're getting the message from a midi file, this could be a time in seconds, or - a number of ticks - see MidiFile::convertTimestampTicksToSeconds(). - - If the message is being used in a MidiBuffer, it might indicate the number of - audio samples from the start of the buffer. - - If the message was created by a MidiInput, see MidiInputCallback::handleIncomingMidiMessage() - for details of the way that it initialises this value. - - @see setTimeStamp, addToTimeStamp - */ - double getTimeStamp() const noexcept { return timeStamp; } - - /** Changes the message's associated timestamp. - The units for the timestamp will be application-specific - see the notes for getTimeStamp(). - @see addToTimeStamp, getTimeStamp - */ - void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; } - - /** Adds a value to the message's timestamp. - The units for the timestamp will be application-specific. - */ - void addToTimeStamp (double delta) noexcept { timeStamp += delta; } - - //============================================================================== - /** Returns the midi channel associated with the message. - - @returns a value 1 to 16 if the message has a channel, or 0 if it hasn't (e.g. - if it's a sysex) - @see isForChannel, setChannel - */ - int getChannel() const noexcept; - - /** Returns true if the message applies to the given midi channel. - - @param channelNumber the channel number to look for, in the range 1 to 16 - @see getChannel, setChannel - */ - bool isForChannel (int channelNumber) const noexcept; - - /** Changes the message's midi channel. - This won't do anything for non-channel messages like sysexes. - @param newChannelNumber the channel number to change it to, in the range 1 to 16 - */ - void setChannel (int newChannelNumber) noexcept; - - //============================================================================== - /** Returns true if this is a system-exclusive message. - */ - bool isSysEx() const noexcept; - - /** Returns a pointer to the sysex data inside the message. - If this event isn't a sysex event, it'll return 0. - @see getSysExDataSize - */ - const uint8* getSysExData() const noexcept; - - /** Returns the size of the sysex data. - This value excludes the 0xf0 header byte and the 0xf7 at the end. - @see getSysExData - */ - int getSysExDataSize() const noexcept; - - //============================================================================== - /** Returns true if this message is a 'key-down' event. - - @param returnTrueForVelocity0 if true, then if this event is a note-on with - velocity 0, it will still be considered to be a note-on and the - method will return true. If returnTrueForVelocity0 is false, then - if this is a note-on event with velocity 0, it'll be regarded as - a note-off, and the method will return false - - @see isNoteOff, getNoteNumber, getVelocity, noteOn - */ - bool isNoteOn (bool returnTrueForVelocity0 = false) const noexcept; - - /** Creates a key-down message (using a floating-point velocity). - - @param channel the midi channel, in the range 1 to 16 - @param noteNumber the key number, 0 to 127 - @param velocity in the range 0 to 1.0 - @see isNoteOn - */ - static MidiMessage noteOn (int channel, int noteNumber, float velocity) noexcept; - - /** Creates a key-down message (using an integer velocity). - - @param channel the midi channel, in the range 1 to 16 - @param noteNumber the key number, 0 to 127 - @param velocity in the range 0 to 127 - @see isNoteOn - */ - static MidiMessage noteOn (int channel, int noteNumber, uint8 velocity) noexcept; - - /** Returns true if this message is a 'key-up' event. - - If returnTrueForNoteOnVelocity0 is true, then his will also return true - for a note-on event with a velocity of 0. - - @see isNoteOn, getNoteNumber, getVelocity, noteOff - */ - bool isNoteOff (bool returnTrueForNoteOnVelocity0 = true) const noexcept; - - /** Creates a key-up message. - - @param channel the midi channel, in the range 1 to 16 - @param noteNumber the key number, 0 to 127 - @param velocity in the range 0 to 1.0 - @see isNoteOff - */ - static MidiMessage noteOff (int channel, int noteNumber, float velocity) noexcept; - - /** Creates a key-up message. - - @param channel the midi channel, in the range 1 to 16 - @param noteNumber the key number, 0 to 127 - @param velocity in the range 0 to 127 - @see isNoteOff - */ - static MidiMessage noteOff (int channel, int noteNumber, uint8 velocity) noexcept; - - /** Creates a key-up message. - - @param channel the midi channel, in the range 1 to 16 - @param noteNumber the key number, 0 to 127 - @see isNoteOff - */ - static MidiMessage noteOff (int channel, int noteNumber) noexcept; - - /** Returns true if this message is a 'key-down' or 'key-up' event. - - @see isNoteOn, isNoteOff - */ - bool isNoteOnOrOff() const noexcept; - - /** Returns the midi note number for note-on and note-off messages. - If the message isn't a note-on or off, the value returned is undefined. - @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber - */ - int getNoteNumber() const noexcept; - - /** Changes the midi note number of a note-on or note-off message. - If the message isn't a note on or off, this will do nothing. - */ - void setNoteNumber (int newNoteNumber) noexcept; - - //============================================================================== - /** Returns the velocity of a note-on or note-off message. - - The value returned will be in the range 0 to 127. - If the message isn't a note-on or off event, it will return 0. - - @see getFloatVelocity - */ - uint8 getVelocity() const noexcept; - - /** Returns the velocity of a note-on or note-off message. - - The value returned will be in the range 0 to 1.0 - If the message isn't a note-on or off event, it will return 0. - - @see getVelocity, setVelocity - */ - float getFloatVelocity() const noexcept; - - /** Changes the velocity of a note-on or note-off message. - - If the message isn't a note on or off, this will do nothing. - - @param newVelocity the new velocity, in the range 0 to 1.0 - @see getFloatVelocity, multiplyVelocity - */ - void setVelocity (float newVelocity) noexcept; - - /** Multiplies the velocity of a note-on or note-off message by a given amount. - - If the message isn't a note on or off, this will do nothing. - - @param scaleFactor the value by which to multiply the velocity - @see setVelocity - */ - void multiplyVelocity (float scaleFactor) noexcept; - - //============================================================================== - /** Returns true if this message is a 'sustain pedal down' controller message. */ - bool isSustainPedalOn() const noexcept; - /** Returns true if this message is a 'sustain pedal up' controller message. */ - bool isSustainPedalOff() const noexcept; - - /** Returns true if this message is a 'sostenuto pedal down' controller message. */ - bool isSostenutoPedalOn() const noexcept; - /** Returns true if this message is a 'sostenuto pedal up' controller message. */ - bool isSostenutoPedalOff() const noexcept; - - /** Returns true if this message is a 'soft pedal down' controller message. */ - bool isSoftPedalOn() const noexcept; - /** Returns true if this message is a 'soft pedal up' controller message. */ - bool isSoftPedalOff() const noexcept; - - //============================================================================== - /** Returns true if the message is a program (patch) change message. - @see getProgramChangeNumber, getGMInstrumentName - */ - bool isProgramChange() const noexcept; - - /** Returns the new program number of a program change message. - If the message isn't a program change, the value returned is undefined. - @see isProgramChange, getGMInstrumentName - */ - int getProgramChangeNumber() const noexcept; - - /** Creates a program-change message. - - @param channel the midi channel, in the range 1 to 16 - @param programNumber the midi program number, 0 to 127 - @see isProgramChange, getGMInstrumentName - */ - static MidiMessage programChange (int channel, int programNumber) noexcept; - - //============================================================================== - /** Returns true if the message is a pitch-wheel move. - @see getPitchWheelValue, pitchWheel - */ - bool isPitchWheel() const noexcept; - - /** Returns the pitch wheel position from a pitch-wheel move message. - - The value returned is a 14-bit number from 0 to 0x3fff, indicating the wheel position. - If called for messages which aren't pitch wheel events, the number returned will be - nonsense. - - @see isPitchWheel - */ - int getPitchWheelValue() const noexcept; - - /** Creates a pitch-wheel move message. - - @param channel the midi channel, in the range 1 to 16 - @param position the wheel position, in the range 0 to 16383 - @see isPitchWheel - */ - static MidiMessage pitchWheel (int channel, int position) noexcept; - - //============================================================================== - /** Returns true if the message is an aftertouch event. - - For aftertouch events, use the getNoteNumber() method to find out the key - that it applies to, and getAftertouchValue() to find out the amount. Use - getChannel() to find out the channel. - - @see getAftertouchValue, getNoteNumber - */ - bool isAftertouch() const noexcept; - - /** Returns the amount of aftertouch from an aftertouch messages. - - The value returned is in the range 0 to 127, and will be nonsense for messages - other than aftertouch messages. - - @see isAftertouch - */ - int getAfterTouchValue() const noexcept; - - /** Creates an aftertouch message. - - @param channel the midi channel, in the range 1 to 16 - @param noteNumber the key number, 0 to 127 - @param aftertouchAmount the amount of aftertouch, 0 to 127 - @see isAftertouch - */ - static MidiMessage aftertouchChange (int channel, - int noteNumber, - int aftertouchAmount) noexcept; - - /** Returns true if the message is a channel-pressure change event. - - This is like aftertouch, but common to the whole channel rather than a specific - note. Use getChannelPressureValue() to find out the pressure, and getChannel() - to find out the channel. - - @see channelPressureChange - */ - bool isChannelPressure() const noexcept; - - /** Returns the pressure from a channel pressure change message. - - @returns the pressure, in the range 0 to 127 - @see isChannelPressure, channelPressureChange - */ - int getChannelPressureValue() const noexcept; - - /** Creates a channel-pressure change event. - - @param channel the midi channel: 1 to 16 - @param pressure the pressure, 0 to 127 - @see isChannelPressure - */ - static MidiMessage channelPressureChange (int channel, int pressure) noexcept; - - //============================================================================== - /** Returns true if this is a midi controller message. - - @see getControllerNumber, getControllerValue, controllerEvent - */ - bool isController() const noexcept; - - /** Returns the controller number of a controller message. - - The name of the controller can be looked up using the getControllerName() method. - Note that the value returned is invalid for messages that aren't controller changes. - - @see isController, getControllerName, getControllerValue - */ - int getControllerNumber() const noexcept; - - /** Returns the controller value from a controller message. - - A value 0 to 127 is returned to indicate the new controller position. - Note that the value returned is invalid for messages that aren't controller changes. - - @see isController, getControllerNumber - */ - int getControllerValue() const noexcept; - - /** Returns true if this message is a controller message and if it has the specified - controller type. - */ - bool isControllerOfType (int controllerType) const noexcept; - - /** Creates a controller message. - @param channel the midi channel, in the range 1 to 16 - @param controllerType the type of controller - @param value the controller value - @see isController - */ - static MidiMessage controllerEvent (int channel, - int controllerType, - int value) noexcept; - - /** Checks whether this message is an all-notes-off message. - @see allNotesOff - */ - bool isAllNotesOff() const noexcept; - - /** Checks whether this message is an all-sound-off message. - @see allSoundOff - */ - bool isAllSoundOff() const noexcept; - - /** Creates an all-notes-off message. - @param channel the midi channel, in the range 1 to 16 - @see isAllNotesOff - */ - static MidiMessage allNotesOff (int channel) noexcept; - - /** Creates an all-sound-off message. - @param channel the midi channel, in the range 1 to 16 - @see isAllSoundOff - */ - static MidiMessage allSoundOff (int channel) noexcept; - - /** Creates an all-controllers-off message. - @param channel the midi channel, in the range 1 to 16 - */ - static MidiMessage allControllersOff (int channel) noexcept; - - //============================================================================== - /** Returns true if this event is a meta-event. - - Meta-events are things like tempo changes, track names, etc. - - @see getMetaEventType, isTrackMetaEvent, isEndOfTrackMetaEvent, - isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, - isKeySignatureMetaEvent, isMidiChannelMetaEvent - */ - bool isMetaEvent() const noexcept; - - /** Returns a meta-event's type number. - - If the message isn't a meta-event, this will return -1. - - @see isMetaEvent, isTrackMetaEvent, isEndOfTrackMetaEvent, - isTextMetaEvent, isTrackNameEvent, isTempoMetaEvent, isTimeSignatureMetaEvent, - isKeySignatureMetaEvent, isMidiChannelMetaEvent - */ - int getMetaEventType() const noexcept; - - /** Returns a pointer to the data in a meta-event. - @see isMetaEvent, getMetaEventLength - */ - const uint8* getMetaEventData() const noexcept; - - /** Returns the length of the data for a meta-event. - @see isMetaEvent, getMetaEventData - */ - int getMetaEventLength() const noexcept; - - //============================================================================== - /** Returns true if this is a 'track' meta-event. */ - bool isTrackMetaEvent() const noexcept; - - /** Returns true if this is an 'end-of-track' meta-event. */ - bool isEndOfTrackMetaEvent() const noexcept; - - /** Creates an end-of-track meta-event. - @see isEndOfTrackMetaEvent - */ - static MidiMessage endOfTrack() noexcept; - - /** Returns true if this is an 'track name' meta-event. - You can use the getTextFromTextMetaEvent() method to get the track's name. - */ - bool isTrackNameEvent() const noexcept; - - /** Returns true if this is a 'text' meta-event. - @see getTextFromTextMetaEvent - */ - bool isTextMetaEvent() const noexcept; - - /** Returns the text from a text meta-event. - @see isTextMetaEvent - */ - String getTextFromTextMetaEvent() const; - - /** Creates a text meta-event. */ - static MidiMessage textMetaEvent (int type, StringRef text); - - //============================================================================== - /** Returns true if this is a 'tempo' meta-event. - @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote - */ - bool isTempoMetaEvent() const noexcept; - - /** Returns the tick length from a tempo meta-event. - - @param timeFormat the 16-bit time format value from the midi file's header. - @returns the tick length (in seconds). - @see isTempoMetaEvent - */ - double getTempoMetaEventTickLength (short timeFormat) const noexcept; - - /** Calculates the seconds-per-quarter-note from a tempo meta-event. - @see isTempoMetaEvent, getTempoMetaEventTickLength - */ - double getTempoSecondsPerQuarterNote() const noexcept; - - /** Creates a tempo meta-event. - @see isTempoMetaEvent - */ - static MidiMessage tempoMetaEvent (int microsecondsPerQuarterNote) noexcept; - - //============================================================================== - /** Returns true if this is a 'time-signature' meta-event. - @see getTimeSignatureInfo - */ - bool isTimeSignatureMetaEvent() const noexcept; - - /** Returns the time-signature values from a time-signature meta-event. - @see isTimeSignatureMetaEvent - */ - void getTimeSignatureInfo (int& numerator, int& denominator) const noexcept; - - /** Creates a time-signature meta-event. - @see isTimeSignatureMetaEvent - */ - static MidiMessage timeSignatureMetaEvent (int numerator, int denominator); - - //============================================================================== - /** Returns true if this is a 'key-signature' meta-event. - @see getKeySignatureNumberOfSharpsOrFlats, isKeySignatureMajorKey - */ - bool isKeySignatureMetaEvent() const noexcept; - - /** Returns the key from a key-signature meta-event. - This method must only be called if isKeySignatureMetaEvent() is true. - A positive number here indicates the number of sharps in the key signature, - and a negative number indicates a number of flats. So e.g. 3 = F# + C# + G#, - -2 = Bb + Eb - @see isKeySignatureMetaEvent, isKeySignatureMajorKey - */ - int getKeySignatureNumberOfSharpsOrFlats() const noexcept; - - /** Returns true if this key-signature event is major, or false if it's minor. - This method must only be called if isKeySignatureMetaEvent() is true. - */ - bool isKeySignatureMajorKey() const noexcept; - - /** Creates a key-signature meta-event. - @param numberOfSharpsOrFlats if positive, this indicates the number of sharps - in the key; if negative, the number of flats - @param isMinorKey if true, the key is minor; if false, it is major - @see isKeySignatureMetaEvent - */ - static MidiMessage keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey); - - //============================================================================== - /** Returns true if this is a 'channel' meta-event. - - A channel meta-event specifies the midi channel that should be used - for subsequent meta-events. - - @see getMidiChannelMetaEventChannel - */ - bool isMidiChannelMetaEvent() const noexcept; - - /** Returns the channel number from a channel meta-event. - - @returns the channel, in the range 1 to 16. - @see isMidiChannelMetaEvent - */ - int getMidiChannelMetaEventChannel() const noexcept; - - /** Creates a midi channel meta-event. - - @param channel the midi channel, in the range 1 to 16 - @see isMidiChannelMetaEvent - */ - static MidiMessage midiChannelMetaEvent (int channel) noexcept; - - //============================================================================== - /** Returns true if this is an active-sense message. */ - bool isActiveSense() const noexcept; - - //============================================================================== - /** Returns true if this is a midi start event. - @see midiStart - */ - bool isMidiStart() const noexcept; - - /** Creates a midi start event. */ - static MidiMessage midiStart() noexcept; - - /** Returns true if this is a midi continue event. - @see midiContinue - */ - bool isMidiContinue() const noexcept; - - /** Creates a midi continue event. */ - static MidiMessage midiContinue() noexcept; - - /** Returns true if this is a midi stop event. - @see midiStop - */ - bool isMidiStop() const noexcept; - - /** Creates a midi stop event. */ - static MidiMessage midiStop() noexcept; - - /** Returns true if this is a midi clock event. - @see midiClock, songPositionPointer - */ - bool isMidiClock() const noexcept; - - /** Creates a midi clock event. */ - static MidiMessage midiClock() noexcept; - - /** Returns true if this is a song-position-pointer message. - @see getSongPositionPointerMidiBeat, songPositionPointer - */ - bool isSongPositionPointer() const noexcept; - - /** Returns the midi beat-number of a song-position-pointer message. - @see isSongPositionPointer, songPositionPointer - */ - int getSongPositionPointerMidiBeat() const noexcept; - - /** Creates a song-position-pointer message. - - The position is a number of midi beats from the start of the song, where 1 midi - beat is 6 midi clocks, and there are 24 midi clocks in a quarter-note. So there - are 4 midi beats in a quarter-note. - - @see isSongPositionPointer, getSongPositionPointerMidiBeat - */ - static MidiMessage songPositionPointer (int positionInMidiBeats) noexcept; - - //============================================================================== - /** Returns true if this is a quarter-frame midi timecode message. - @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue - */ - bool isQuarterFrame() const noexcept; - - /** Returns the sequence number of a quarter-frame midi timecode message. - This will be a value between 0 and 7. - @see isQuarterFrame, getQuarterFrameValue, quarterFrame - */ - int getQuarterFrameSequenceNumber() const noexcept; - - /** Returns the value from a quarter-frame message. - This will be the lower nybble of the message's data-byte, a value between 0 and 15 - */ - int getQuarterFrameValue() const noexcept; - - /** Creates a quarter-frame MTC message. - - @param sequenceNumber a value 0 to 7 for the upper nybble of the message's data byte - @param value a value 0 to 15 for the lower nybble of the message's data byte - */ - static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept; - - /** SMPTE timecode types. - Used by the getFullFrameParameters() and fullFrame() methods. - */ - enum SmpteTimecodeType - { - fps24 = 0, - fps25 = 1, - fps30drop = 2, - fps30 = 3 - }; - - /** Returns true if this is a full-frame midi timecode message. */ - bool isFullFrame() const noexcept; - - /** Extracts the timecode information from a full-frame midi timecode message. - - You should only call this on messages where you've used isFullFrame() to - check that they're the right kind. - */ - void getFullFrameParameters (int& hours, - int& minutes, - int& seconds, - int& frames, - SmpteTimecodeType& timecodeType) const noexcept; - - /** Creates a full-frame MTC message. */ - static MidiMessage fullFrame (int hours, - int minutes, - int seconds, - int frames, - SmpteTimecodeType timecodeType); - - //============================================================================== - /** Types of MMC command. - - @see isMidiMachineControlMessage, getMidiMachineControlCommand, midiMachineControlCommand - */ - enum MidiMachineControlCommand - { - mmc_stop = 1, - mmc_play = 2, - mmc_deferredplay = 3, - mmc_fastforward = 4, - mmc_rewind = 5, - mmc_recordStart = 6, - mmc_recordStop = 7, - mmc_pause = 9 - }; - - /** Checks whether this is an MMC message. - If it is, you can use the getMidiMachineControlCommand() to find out its type. - */ - bool isMidiMachineControlMessage() const noexcept; - - /** For an MMC message, this returns its type. - - Make sure it's actually an MMC message with isMidiMachineControlMessage() before - calling this method. - */ - MidiMachineControlCommand getMidiMachineControlCommand() const noexcept; - - /** Creates an MMC message. */ - static MidiMessage midiMachineControlCommand (MidiMachineControlCommand command); - - /** Checks whether this is an MMC "goto" message. - If it is, the parameters passed-in are set to the time that the message contains. - @see midiMachineControlGoto - */ - bool isMidiMachineControlGoto (int& hours, - int& minutes, - int& seconds, - int& frames) const noexcept; - - /** Creates an MMC "goto" message. - This messages tells the device to go to a specific frame. - @see isMidiMachineControlGoto - */ - static MidiMessage midiMachineControlGoto (int hours, - int minutes, - int seconds, - int frames); - - //============================================================================== - /** Creates a master-volume change message. - @param volume the volume, 0 to 1.0 - */ - static MidiMessage masterVolume (float volume); - - //============================================================================== - /** Creates a system-exclusive message. - The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7. - */ - static MidiMessage createSysExMessage (const void* sysexData, - int dataSize); - - - //============================================================================== - /** Reads a midi variable-length integer. - - @param data the data to read the number from - @param numBytesUsed on return, this will be set to the number of bytes that were read - */ - static int readVariableLengthVal (const uint8* data, - int& numBytesUsed) noexcept; - - /** Based on the first byte of a short midi message, this uses a lookup table - to return the message length (either 1, 2, or 3 bytes). - - The value passed in must be 0x80 or higher. - */ - static int getMessageLengthFromFirstByte (uint8 firstByte) noexcept; - - //============================================================================== - /** Returns the name of a midi note number. - - E.g "C", "D#", etc. - - @param noteNumber the midi note number, 0 to 127 - @param useSharps if true, sharpened notes are used, e.g. "C#", otherwise - they'll be flattened, e.g. "Db" - @param includeOctaveNumber if true, the octave number will be appended to the string, - e.g. "C#4" - @param octaveNumForMiddleC if an octave number is being appended, this indicates the - number that will be used for middle C's octave - - @see getMidiNoteInHertz - */ - static String getMidiNoteName (int noteNumber, - bool useSharps, - bool includeOctaveNumber, - int octaveNumForMiddleC); - - /** Returns the frequency of a midi note number. - - The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch. - @see getMidiNoteName - */ - static double getMidiNoteInHertz (int noteNumber, double frequencyOfA = 440.0) noexcept; - - /** Returns true if the given midi note number is a black key. */ - static bool isMidiNoteBlack (int noteNumber) noexcept; - - /** Returns the standard name of a GM instrument, or nullptr if unknown for this index. - - @param midiInstrumentNumber the program number 0 to 127 - @see getProgramChangeNumber - */ - static const char* getGMInstrumentName (int midiInstrumentNumber); - - /** Returns the name of a bank of GM instruments, or nullptr if unknown for this bank number. - @param midiBankNumber the bank, 0 to 15 - */ - static const char* getGMInstrumentBankName (int midiBankNumber); - - /** Returns the standard name of a channel 10 percussion sound, or nullptr if unknown for this note number. - @param midiNoteNumber the key number, 35 to 81 - */ - static const char* getRhythmInstrumentName (int midiNoteNumber); - - /** Returns the name of a controller type number, or nullptr if unknown for this controller number. - @see getControllerNumber - */ - static const char* getControllerName (int controllerNumber); - - /** Converts a floating-point value between 0 and 1 to a MIDI 7-bit value between 0 and 127. */ - static uint8 floatValueToMidiByte (float valueBetween0and1) noexcept; - - /** Converts a pitchbend value in semitones to a MIDI 14-bit pitchwheel position value. */ - static uint16 pitchbendToPitchwheelPos (float pitchbendInSemitones, - float pitchbendRangeInSemitones) noexcept; - -private: - //============================================================================== - #ifndef DOXYGEN - union PackedData - { - uint8* allocatedData; - uint8 asBytes[sizeof (uint8*)]; - }; - - PackedData packedData; - double timeStamp = 0; - int size; - #endif - - inline bool isHeapAllocated() const noexcept { return size > (int) sizeof (packedData); } - inline uint8* getData() const noexcept { return isHeapAllocated() ? packedData.allocatedData : (uint8*) packedData.asBytes; } - uint8* allocateSpace (int); -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp deleted file mode 100644 index fe8b2c083..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) : message (mm) {} -MidiMessageSequence::MidiEventHolder::MidiEventHolder (MidiMessage&& mm) : message (static_cast (mm)) {} -MidiMessageSequence::MidiEventHolder::~MidiEventHolder() {} - -//============================================================================== -MidiMessageSequence::MidiMessageSequence() -{ -} - -MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) -{ - list.addCopiesOf (other.list); - updateMatchedPairs(); -} - -MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other) -{ - MidiMessageSequence otherCopy (other); - swapWith (otherCopy); - return *this; -} - -MidiMessageSequence::MidiMessageSequence (MidiMessageSequence&& other) noexcept - : list (static_cast&&> (other.list)) -{} - -MidiMessageSequence& MidiMessageSequence::operator= (MidiMessageSequence&& other) noexcept -{ - list = static_cast&&> (other.list); - return *this; -} - -MidiMessageSequence::~MidiMessageSequence() -{ -} - -void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept -{ - list.swapWith (other.list); -} - -void MidiMessageSequence::clear() -{ - list.clear(); -} - -int MidiMessageSequence::getNumEvents() const noexcept -{ - return list.size(); -} - -MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (int index) const noexcept -{ - return list[index]; -} - -MidiMessageSequence::MidiEventHolder** MidiMessageSequence::begin() const noexcept { return list.begin(); } -MidiMessageSequence::MidiEventHolder** MidiMessageSequence::end() const noexcept { return list.end(); } - -double MidiMessageSequence::getTimeOfMatchingKeyUp (int index) const noexcept -{ - if (auto* meh = list[index]) - if (meh->noteOffObject != nullptr) - return meh->noteOffObject->message.getTimeStamp(); - - return 0.0; -} - -int MidiMessageSequence::getIndexOfMatchingKeyUp (int index) const noexcept -{ - if (auto* meh = list [index]) - return list.indexOf (meh->noteOffObject); - - return -1; -} - -int MidiMessageSequence::getIndexOf (const MidiEventHolder* event) const noexcept -{ - return list.indexOf (event); -} - -int MidiMessageSequence::getNextIndexAtTime (double timeStamp) const noexcept -{ - const int numEvents = list.size(); - - int i; - for (i = 0; i < numEvents; ++i) - if (list.getUnchecked(i)->message.getTimeStamp() >= timeStamp) - break; - - return i; -} - -//============================================================================== -double MidiMessageSequence::getStartTime() const noexcept -{ - return getEventTime (0); -} - -double MidiMessageSequence::getEndTime() const noexcept -{ - return getEventTime (list.size() - 1); -} - -double MidiMessageSequence::getEventTime (const int index) const noexcept -{ - if (auto* meh = list [index]) - return meh->message.getTimeStamp(); - - return 0.0; -} - -//============================================================================== -MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiEventHolder* newEvent, double timeAdjustment) -{ - newEvent->message.addToTimeStamp (timeAdjustment); - auto time = newEvent->message.getTimeStamp(); - - int i; - for (i = list.size(); --i >= 0;) - if (list.getUnchecked(i)->message.getTimeStamp() <= time) - break; - - list.insert (i + 1, newEvent); - return newEvent; -} - -MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage, double timeAdjustment) -{ - return addEvent (new MidiEventHolder (newMessage), timeAdjustment); -} - -MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (MidiMessage&& newMessage, double timeAdjustment) -{ - return addEvent (new MidiEventHolder (static_cast (newMessage)), timeAdjustment); -} - -void MidiMessageSequence::deleteEvent (int index, bool deleteMatchingNoteUp) -{ - if (isPositiveAndBelow (index, list.size())) - { - if (deleteMatchingNoteUp) - deleteEvent (getIndexOfMatchingKeyUp (index), false); - - list.remove (index); - } -} - -void MidiMessageSequence::addSequence (const MidiMessageSequence& other, double timeAdjustment) -{ - for (auto* m : other) - { - auto newOne = new MidiEventHolder (m->message); - newOne->message.addToTimeStamp (timeAdjustment); - list.add (newOne); - } - - sort(); -} - -void MidiMessageSequence::addSequence (const MidiMessageSequence& other, - double timeAdjustment, - double firstAllowableTime, - double endOfAllowableDestTimes) -{ - for (auto* m : other) - { - auto t = m->message.getTimeStamp() + timeAdjustment; - - if (t >= firstAllowableTime && t < endOfAllowableDestTimes) - { - auto newOne = new MidiEventHolder (m->message); - newOne->message.setTimeStamp (t); - list.add (newOne); - } - } - - sort(); -} - -struct MidiMessageSequenceSorter -{ - static int compareElements (const MidiMessageSequence::MidiEventHolder* first, - const MidiMessageSequence::MidiEventHolder* second) noexcept - { - auto diff = first->message.getTimeStamp() - second->message.getTimeStamp(); - return (diff > 0) - (diff < 0); - } -}; - -void MidiMessageSequence::sort() noexcept -{ - MidiMessageSequenceSorter sorter; - list.sort (sorter, true); -} - -void MidiMessageSequence::updateMatchedPairs() noexcept -{ - for (int i = 0; i < list.size(); ++i) - { - auto* meh = list.getUnchecked(i); - auto& m1 = meh->message; - - if (m1.isNoteOn()) - { - meh->noteOffObject = nullptr; - auto note = m1.getNoteNumber(); - auto chan = m1.getChannel(); - auto len = list.size(); - - for (int j = i + 1; j < len; ++j) - { - auto* meh2 = list.getUnchecked(j); - auto& m = meh2->message; - - if (m.getNoteNumber() == note && m.getChannel() == chan) - { - if (m.isNoteOff()) - { - meh->noteOffObject = meh2; - break; - } - - if (m.isNoteOn()) - { - auto newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note)); - list.insert (j, newEvent); - newEvent->message.setTimeStamp (m.getTimeStamp()); - meh->noteOffObject = newEvent; - break; - } - } - } - } - } -} - -void MidiMessageSequence::addTimeToMessages (double delta) noexcept -{ - if (delta != 0) - for (auto* m : list) - m->message.addToTimeStamp (delta); -} - -//============================================================================== -void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract, - MidiMessageSequence& destSequence, - const bool alsoIncludeMetaEvents) const -{ - for (auto* meh : list) - if (meh->message.isForChannel (channelNumberToExtract) - || (alsoIncludeMetaEvents && meh->message.isMetaEvent())) - destSequence.addEvent (meh->message); -} - -void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const -{ - for (auto* meh : list) - if (meh->message.isSysEx()) - destSequence.addEvent (meh->message); -} - -void MidiMessageSequence::deleteMidiChannelMessages (const int channelNumberToRemove) -{ - for (int i = list.size(); --i >= 0;) - if (list.getUnchecked(i)->message.isForChannel (channelNumberToRemove)) - list.remove(i); -} - -void MidiMessageSequence::deleteSysExMessages() -{ - for (int i = list.size(); --i >= 0;) - if (list.getUnchecked(i)->message.isSysEx()) - list.remove(i); -} - -//============================================================================== -void MidiMessageSequence::createControllerUpdatesForTime (int channelNumber, double time, Array& dest) -{ - bool doneProg = false; - bool donePitchWheel = false; - bool doneControllers[128] = {}; - - for (int i = list.size(); --i >= 0;) - { - auto& mm = list.getUnchecked(i)->message; - - if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) - { - if (mm.isProgramChange() && ! doneProg) - { - doneProg = true; - dest.add (MidiMessage (mm, 0.0)); - } - else if (mm.isPitchWheel() && ! donePitchWheel) - { - donePitchWheel = true; - dest.add (MidiMessage (mm, 0.0)); - } - else if (mm.isController()) - { - const int controllerNumber = mm.getControllerNumber(); - jassert (isPositiveAndBelow (controllerNumber, 128)); - - if (! doneControllers[controllerNumber]) - { - doneControllers[controllerNumber] = true; - dest.add (MidiMessage (mm, 0.0)); - } - } - } - } -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h deleted file mode 100644 index 8f5e3f699..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - A sequence of timestamped midi messages. - - This allows the sequence to be manipulated, and also to be read from and - written to a standard midi file. - - @see MidiMessage, MidiFile -*/ -class JUCE_API MidiMessageSequence -{ -public: - //============================================================================== - /** Creates an empty midi sequence object. */ - MidiMessageSequence(); - - /** Creates a copy of another sequence. */ - MidiMessageSequence (const MidiMessageSequence&); - - /** Replaces this sequence with another one. */ - MidiMessageSequence& operator= (const MidiMessageSequence&); - - /** Move constructor */ - MidiMessageSequence (MidiMessageSequence&&) noexcept; - - /** Move assignment operator */ - MidiMessageSequence& operator= (MidiMessageSequence&&) noexcept; - - /** Destructor. */ - ~MidiMessageSequence(); - - //============================================================================== - /** Structure used to hold midi events in the sequence. - - These structures act as 'handles' on the events as they are moved about in - the list, and make it quick to find the matching note-offs for note-on events. - - @see MidiMessageSequence::getEventPointer - */ - class MidiEventHolder - { - public: - //============================================================================== - /** Destructor. */ - ~MidiEventHolder(); - - /** The message itself, whose timestamp is used to specify the event's time. */ - MidiMessage message; - - /** The matching note-off event (if this is a note-on event). - - If this isn't a note-on, this pointer will be nullptr. - - Use the MidiMessageSequence::updateMatchedPairs() method to keep these - note-offs up-to-date after events have been moved around in the sequence - or deleted. - */ - MidiEventHolder* noteOffObject = nullptr; - - private: - //============================================================================== - friend class MidiMessageSequence; - MidiEventHolder (const MidiMessage&); - MidiEventHolder (MidiMessage&&); - JUCE_LEAK_DETECTOR (MidiEventHolder) - }; - - //============================================================================== - /** Clears the sequence. */ - void clear(); - - /** Returns the number of events in the sequence. */ - int getNumEvents() const noexcept; - - /** Returns a pointer to one of the events. */ - MidiEventHolder* getEventPointer (int index) const noexcept; - - /** Iterator for the list of MidiEventHolders */ - MidiEventHolder** begin() const noexcept; - - /** Iterator for the list of MidiEventHolders */ - MidiEventHolder** end() const noexcept; - - /** Returns the time of the note-up that matches the note-on at this index. - If the event at this index isn't a note-on, it'll just return 0. - @see MidiMessageSequence::MidiEventHolder::noteOffObject - */ - double getTimeOfMatchingKeyUp (int index) const noexcept; - - /** Returns the index of the note-up that matches the note-on at this index. - If the event at this index isn't a note-on, it'll just return -1. - @see MidiMessageSequence::MidiEventHolder::noteOffObject - */ - int getIndexOfMatchingKeyUp (int index) const noexcept; - - /** Returns the index of an event. */ - int getIndexOf (const MidiEventHolder* event) const noexcept; - - /** Returns the index of the first event on or after the given timestamp. - If the time is beyond the end of the sequence, this will return the - number of events. - */ - int getNextIndexAtTime (double timeStamp) const noexcept; - - //============================================================================== - /** Returns the timestamp of the first event in the sequence. - @see getEndTime - */ - double getStartTime() const noexcept; - - /** Returns the timestamp of the last event in the sequence. - @see getStartTime - */ - double getEndTime() const noexcept; - - /** Returns the timestamp of the event at a given index. - If the index is out-of-range, this will return 0.0 - */ - double getEventTime (int index) const noexcept; - - //============================================================================== - /** Inserts a midi message into the sequence. - - The index at which the new message gets inserted will depend on its timestamp, - because the sequence is kept sorted. - - Remember to call updateMatchedPairs() after adding note-on events. - - @param newMessage the new message to add (an internal copy will be made) - @param timeAdjustment an optional value to add to the timestamp of the message - that will be inserted - @see updateMatchedPairs - */ - MidiEventHolder* addEvent (const MidiMessage& newMessage, double timeAdjustment = 0); - - /** Inserts a midi message into the sequence. - - The index at which the new message gets inserted will depend on its timestamp, - because the sequence is kept sorted. - - Remember to call updateMatchedPairs() after adding note-on events. - - @param newMessage the new message to add (an internal copy will be made) - @param timeAdjustment an optional value to add to the timestamp of the message - that will be inserted - @see updateMatchedPairs - */ - MidiEventHolder* addEvent (MidiMessage&& newMessage, double timeAdjustment = 0); - - /** Deletes one of the events in the sequence. - - Remember to call updateMatchedPairs() after removing events. - - @param index the index of the event to delete - @param deleteMatchingNoteUp whether to also remove the matching note-off - if the event you're removing is a note-on - */ - void deleteEvent (int index, bool deleteMatchingNoteUp); - - /** Merges another sequence into this one. - Remember to call updateMatchedPairs() after using this method. - - @param other the sequence to add from - @param timeAdjustmentDelta an amount to add to the timestamps of the midi events - as they are read from the other sequence - @param firstAllowableDestTime events will not be added if their time is earlier - than this time. (This is after their time has been adjusted - by the timeAdjustmentDelta) - @param endOfAllowableDestTimes events will not be added if their time is equal to - or greater than this time. (This is after their time has - been adjusted by the timeAdjustmentDelta) - */ - void addSequence (const MidiMessageSequence& other, - double timeAdjustmentDelta, - double firstAllowableDestTime, - double endOfAllowableDestTimes); - - /** Merges another sequence into this one. - Remember to call updateMatchedPairs() after using this method. - - @param other the sequence to add from - @param timeAdjustmentDelta an amount to add to the timestamps of the midi events - as they are read from the other sequence - */ - void addSequence (const MidiMessageSequence& other, - double timeAdjustmentDelta); - - //============================================================================== - /** Makes sure all the note-on and note-off pairs are up-to-date. - - Call this after re-ordering messages or deleting/adding messages, and it - will scan the list and make sure all the note-offs in the MidiEventHolder - structures are pointing at the correct ones. - */ - void updateMatchedPairs() noexcept; - - /** Forces a sort of the sequence. - You may need to call this if you've manually modified the timestamps of some - events such that the overall order now needs updating. - */ - void sort() noexcept; - - //============================================================================== - /** Copies all the messages for a particular midi channel to another sequence. - - @param channelNumberToExtract the midi channel to look for, in the range 1 to 16 - @param destSequence the sequence that the chosen events should be copied to - @param alsoIncludeMetaEvents if true, any meta-events (which don't apply to a specific - channel) will also be copied across. - @see extractSysExMessages - */ - void extractMidiChannelMessages (int channelNumberToExtract, - MidiMessageSequence& destSequence, - bool alsoIncludeMetaEvents) const; - - /** Copies all midi sys-ex messages to another sequence. - @param destSequence this is the sequence to which any sys-exes in this sequence - will be added - @see extractMidiChannelMessages - */ - void extractSysExMessages (MidiMessageSequence& destSequence) const; - - /** Removes any messages in this sequence that have a specific midi channel. - @param channelNumberToRemove the midi channel to look for, in the range 1 to 16 - */ - void deleteMidiChannelMessages (int channelNumberToRemove); - - /** Removes any sys-ex messages from this sequence. */ - void deleteSysExMessages(); - - /** Adds an offset to the timestamps of all events in the sequence. - @param deltaTime the amount to add to each timestamp. - */ - void addTimeToMessages (double deltaTime) noexcept; - - //============================================================================== - /** Scans through the sequence to determine the state of any midi controllers at - a given time. - - This will create a sequence of midi controller changes that can be - used to set all midi controllers to the state they would be in at the - specified time within this sequence. - - As well as controllers, it will also recreate the midi program number - and pitch bend position. - - @param channelNumber the midi channel to look for, in the range 1 to 16. Controllers - for other channels will be ignored. - @param time the time at which you want to find out the state - there are - no explicit units for this time measurement, it's the same units - as used for the timestamps of the messages - @param resultMessages an array to which midi controller-change messages will be added. This - will be the minimum number of controller changes to recreate the - state at the required time. - */ - void createControllerUpdatesForTime (int channelNumber, double time, - Array& resultMessages); - - //============================================================================== - /** Swaps this sequence with another one. */ - void swapWith (MidiMessageSequence&) noexcept; - -private: - //============================================================================== - friend class MidiFile; - OwnedArray list; - - MidiEventHolder* addEvent (MidiEventHolder*, double); - - JUCE_LEAK_DETECTOR (MidiMessageSequence) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp b/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp deleted file mode 100644 index 9e2c2df32..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiRPN.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -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", "MIDI/MPE") {} - - 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", "MIDI/MPE") {} - - 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 - -} // namespace juce diff --git a/source/modules/juce_audio_basics/midi/juce_MidiRPN.h b/source/modules/juce_audio_basics/midi/juce_MidiRPN.h deleted file mode 100644 index e8a74d107..000000000 --- a/source/modules/juce_audio_basics/midi/juce_MidiRPN.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** 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); -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp deleted file mode 100644 index 24bfd2e26..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.cpp +++ /dev/null @@ -1,2155 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -namespace -{ - const uint8 noLSBValueReceived = 0xff; - const Range allChannels = Range (1, 17); -} - -//============================================================================== -MPEInstrument::MPEInstrument() noexcept -{ - std::fill_n (lastPressureLowerBitReceivedOnChannel, 16, noLSBValueReceived); - std::fill_n (lastTimbreLowerBitReceivedOnChannel, 16, noLSBValueReceived); - std::fill_n (isNoteChannelSustained, 16, false); - - pitchbendDimension.value = &MPENote::pitchbend; - pressureDimension.value = &MPENote::pressure; - timbreDimension.value = &MPENote::timbre; - - // the default value for pressure is 0, for all other dimension it is centre (= default MPEValue) - std::fill_n (pressureDimension.lastValueReceivedOnChannel, 16, MPEValue::minValue()); - - legacyMode.isEnabled = false; - legacyMode.pitchbendRange = 2; - legacyMode.channelRange = Range (1, 17); -} - -MPEInstrument::~MPEInstrument() -{ -} - -//============================================================================== -MPEZoneLayout MPEInstrument::getZoneLayout() const noexcept -{ - return zoneLayout; -} - -void MPEInstrument::setZoneLayout (MPEZoneLayout newLayout) -{ - releaseAllNotes(); - - const ScopedLock sl (lock); - legacyMode.isEnabled = false; - zoneLayout = newLayout; -} - -//============================================================================== -void MPEInstrument::enableLegacyMode (int pitchbendRange, Range channelRange) -{ - releaseAllNotes(); - - const ScopedLock sl (lock); - legacyMode.isEnabled = true; - legacyMode.pitchbendRange = pitchbendRange; - legacyMode.channelRange = channelRange; - zoneLayout.clearAllZones(); -} - -bool MPEInstrument::isLegacyModeEnabled() const noexcept -{ - return legacyMode.isEnabled; -} - -Range MPEInstrument::getLegacyModeChannelRange() const noexcept -{ - return legacyMode.channelRange; -} - -void MPEInstrument::setLegacyModeChannelRange (Range channelRange) -{ - jassert (Range(1, 17).contains (channelRange)); - - releaseAllNotes(); - const ScopedLock sl (lock); - legacyMode.channelRange = channelRange; -} - -int MPEInstrument::getLegacyModePitchbendRange() const noexcept -{ - return legacyMode.pitchbendRange; -} - -void MPEInstrument::setLegacyModePitchbendRange (int pitchbendRange) -{ - jassert (pitchbendRange >= 0 && pitchbendRange <= 96); - - releaseAllNotes(); - const ScopedLock sl (lock); - legacyMode.pitchbendRange = pitchbendRange; -} - -//============================================================================== -void MPEInstrument::setPressureTrackingMode (TrackingMode modeToUse) -{ - pressureDimension.trackingMode = modeToUse; -} - -void MPEInstrument::setPitchbendTrackingMode (TrackingMode modeToUse) -{ - pitchbendDimension.trackingMode = modeToUse; -} - -void MPEInstrument::setTimbreTrackingMode (TrackingMode modeToUse) -{ - timbreDimension.trackingMode = modeToUse; -} - -//============================================================================== -void MPEInstrument::addListener (Listener* const listenerToAdd) noexcept -{ - listeners.add (listenerToAdd); -} - -void MPEInstrument::removeListener (Listener* const listenerToRemove) noexcept -{ - listeners.remove (listenerToRemove); -} - -//============================================================================== -void MPEInstrument::processNextMidiEvent (const MidiMessage& message) -{ - zoneLayout.processNextMidiEvent (message); - - if (message.isNoteOn (true)) processMidiNoteOnMessage (message); - else if (message.isNoteOff (false)) processMidiNoteOffMessage (message); - else if (message.isAllNotesOff()) processMidiAllNotesOffMessage (message); - else if (message.isPitchWheel()) processMidiPitchWheelMessage (message); - else if (message.isChannelPressure()) processMidiChannelPressureMessage (message); - else if (message.isController()) processMidiControllerMessage (message); -} - -//============================================================================== -void MPEInstrument::processMidiNoteOnMessage (const MidiMessage& message) -{ - // Note: if a note-on with velocity = 0 is used to convey a note-off, - // then the actual note-off velocity is not known. In this case, - // the MPE convention is to use note-off velocity = 64. - - if (message.getVelocity() == 0) - { - noteOff (message.getChannel(), - message.getNoteNumber(), - MPEValue::from7BitInt (64)); - } - else - { - noteOn (message.getChannel(), - message.getNoteNumber(), - MPEValue::from7BitInt (message.getVelocity())); - } -} - -//============================================================================== -void MPEInstrument::processMidiNoteOffMessage (const MidiMessage& message) -{ - noteOff (message.getChannel(), - message.getNoteNumber(), - MPEValue::from7BitInt (message.getVelocity())); -} - -//============================================================================== -void MPEInstrument::processMidiPitchWheelMessage (const MidiMessage& message) -{ - pitchbend (message.getChannel(), - MPEValue::from14BitInt (message.getPitchWheelValue())); -} - -//============================================================================== -void MPEInstrument::processMidiChannelPressureMessage (const MidiMessage& message) -{ - pressure (message.getChannel(), - MPEValue::from7BitInt (message.getChannelPressureValue())); -} - -//============================================================================== -void MPEInstrument::processMidiControllerMessage (const MidiMessage& message) -{ - switch (message.getControllerNumber()) - { - case 64: sustainPedal (message.getChannel(), message.isSustainPedalOn()); break; - case 66: sostenutoPedal (message.getChannel(), message.isSostenutoPedalOn()); break; - case 70: handlePressureMSB (message.getChannel(), message.getControllerValue()); break; - case 74: handleTimbreMSB (message.getChannel(), message.getControllerValue()); break; - case 102: handlePressureLSB (message.getChannel(), message.getControllerValue()); break; - case 106: handleTimbreLSB (message.getChannel(), message.getControllerValue()); break; - default: break; - } -} - -//============================================================================== -void MPEInstrument::processMidiAllNotesOffMessage (const MidiMessage& message) -{ - // in MPE mode, "all notes off" is per-zone and expected on the master channel; - // in legacy mode, "all notes off" is per MIDI channel (within the channel range used). - - if (legacyMode.isEnabled && legacyMode.channelRange.contains (message.getChannel())) - { - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (note.midiChannel == message.getChannel()) - { - note.keyState = MPENote::off; - note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number - listeners.call (&MPEInstrument::Listener::noteReleased, note); - notes.remove (i); - } - } - } - else if (MPEZone* zone = zoneLayout.getZoneByMasterChannel (message.getChannel())) - { - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (zone->isUsingChannelAsNoteChannel (note.midiChannel)) - { - note.keyState = MPENote::off; - note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number - listeners.call (&MPEInstrument::Listener::noteReleased, note); - notes.remove (i); - } - } - } -} - -//============================================================================== -void MPEInstrument::handlePressureMSB (int midiChannel, int value) noexcept -{ - const uint8 lsb = lastPressureLowerBitReceivedOnChannel[midiChannel - 1]; - - pressure (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value) - : MPEValue::from14BitInt (lsb + (value << 7))); -} - -void MPEInstrument::handlePressureLSB (int midiChannel, int value) noexcept -{ - lastPressureLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value); -} - -void MPEInstrument::handleTimbreMSB (int midiChannel, int value) noexcept -{ - const uint8 lsb = lastTimbreLowerBitReceivedOnChannel[midiChannel - 1]; - - timbre (midiChannel, lsb == noLSBValueReceived ? MPEValue::from7BitInt (value) - : MPEValue::from14BitInt (lsb + (value << 7))); -} - -void MPEInstrument::handleTimbreLSB (int midiChannel, int value) noexcept -{ - lastTimbreLowerBitReceivedOnChannel[midiChannel - 1] = uint8 (value); -} - -//============================================================================== -void MPEInstrument::noteOn (int midiChannel, - int midiNoteNumber, - MPEValue midiNoteOnVelocity) -{ - if (! isNoteChannel (midiChannel)) - return; - - MPENote newNote (midiChannel, - midiNoteNumber, - midiNoteOnVelocity, - getInitialValueForNewNote (midiChannel, pitchbendDimension), - getInitialValueForNewNote (midiChannel, pressureDimension), - getInitialValueForNewNote (midiChannel, timbreDimension), - isNoteChannelSustained[midiChannel - 1] ? MPENote::keyDownAndSustained : MPENote::keyDown); - - const ScopedLock sl (lock); - updateNoteTotalPitchbend (newNote); - - if (MPENote* alreadyPlayingNote = getNotePtr (midiChannel, midiNoteNumber)) - { - // pathological case: second note-on received for same note -> retrigger it - alreadyPlayingNote->keyState = MPENote::off; - alreadyPlayingNote->noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number - listeners.call (&MPEInstrument::Listener::noteReleased, *alreadyPlayingNote); - notes.remove (alreadyPlayingNote); - } - - notes.add (newNote); - listeners.call (&MPEInstrument::Listener::noteAdded, newNote); -} - -//============================================================================== -void MPEInstrument::noteOff (int midiChannel, - int midiNoteNumber, - MPEValue midiNoteOffVelocity) -{ - if (notes.isEmpty() || ! isNoteChannel (midiChannel)) - return; - - const ScopedLock sl (lock); - - if (MPENote* note = getNotePtr (midiChannel, midiNoteNumber)) - { - note->keyState = (note->keyState == MPENote::keyDownAndSustained) ? MPENote::sustained : MPENote::off; - note->noteOffVelocity = midiNoteOffVelocity; - - // last dimension values received for this note should not be re-used for - // any new notes, so reset them: - pressureDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::minValue(); - pitchbendDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); - timbreDimension.lastValueReceivedOnChannel[midiChannel - 1] = MPEValue::centreValue(); - - if (note->keyState == MPENote::off) - { - listeners.call (&MPEInstrument::Listener::noteReleased, *note); - notes.remove (note); - } - else - { - listeners.call (&MPEInstrument::Listener::noteKeyStateChanged, *note); - } - } -} - -//============================================================================== -void MPEInstrument::pitchbend (int midiChannel, MPEValue value) -{ - const ScopedLock sl (lock); - updateDimension (midiChannel, pitchbendDimension, value); -} - -void MPEInstrument::pressure (int midiChannel, MPEValue value) -{ - const ScopedLock sl (lock); - updateDimension (midiChannel, pressureDimension, value); -} - -void MPEInstrument::timbre (int midiChannel, MPEValue value) -{ - const ScopedLock sl (lock); - updateDimension (midiChannel, timbreDimension, value); -} - -MPEValue MPEInstrument::getInitialValueForNewNote (int midiChannel, MPEDimension& dimension) const -{ - if (getLastNotePlayedPtr (midiChannel) != nullptr) - return &dimension == &pressureDimension ? MPEValue::minValue() : MPEValue::centreValue(); - - return dimension.lastValueReceivedOnChannel[midiChannel - 1]; -} - -//============================================================================== -void MPEInstrument::updateDimension (int midiChannel, MPEDimension& dimension, MPEValue value) -{ - dimension.lastValueReceivedOnChannel[midiChannel - 1] = value; - - if (notes.isEmpty()) - return; - - if (MPEZone* zone = zoneLayout.getZoneByMasterChannel (midiChannel)) - { - updateDimensionMaster (*zone, dimension, value); - } - else if (isNoteChannel (midiChannel)) - { - if (dimension.trackingMode == allNotesOnChannel) - { - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (note.midiChannel == midiChannel) - updateDimensionForNote (note, dimension, value); - } - } - else - { - if (MPENote* note = getNotePtr (midiChannel, dimension.trackingMode)) - updateDimensionForNote (*note, dimension, value); - } - } -} - -//============================================================================== -void MPEInstrument::updateDimensionMaster (MPEZone& zone, MPEDimension& dimension, MPEValue value) -{ - const Range channels (zone.getNoteChannelRange()); - - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (! channels.contains (note.midiChannel)) - continue; - - if (&dimension == &pitchbendDimension) - { - // master pitchbend is a special case: we don't change the note's own pitchbend, - // instead we have to update its total (master + note) pitchbend. - updateNoteTotalPitchbend (note); - listeners.call (&MPEInstrument::Listener::notePitchbendChanged, note); - } - else if (dimension.getValue (note) != value) - { - dimension.getValue (note) = value; - callListenersDimensionChanged (note, dimension); - } - } -} - -//============================================================================== -void MPEInstrument::updateDimensionForNote (MPENote& note, MPEDimension& dimension, MPEValue value) -{ - if (dimension.getValue (note) != value) - { - dimension.getValue (note) = value; - - if (&dimension == &pitchbendDimension) - updateNoteTotalPitchbend (note); - - callListenersDimensionChanged (note, dimension); - } -} - -//============================================================================== -void MPEInstrument::callListenersDimensionChanged (MPENote& note, MPEDimension& dimension) -{ - if (&dimension == &pressureDimension) { listeners.call (&MPEInstrument::Listener::notePressureChanged, note); return; } - if (&dimension == &timbreDimension) { listeners.call (&MPEInstrument::Listener::noteTimbreChanged, note); return; } - if (&dimension == &pitchbendDimension) { listeners.call (&MPEInstrument::Listener::notePitchbendChanged, note); return; } -} - -//============================================================================== -void MPEInstrument::updateNoteTotalPitchbend (MPENote& note) -{ - if (legacyMode.isEnabled) - { - note.totalPitchbendInSemitones = note.pitchbend.asSignedFloat() * legacyMode.pitchbendRange; - } - else - { - if (MPEZone* zone = zoneLayout.getZoneByNoteChannel (note.midiChannel)) - { - double notePitchbendInSemitones = note.pitchbend.asSignedFloat() * zone->getPerNotePitchbendRange(); - double masterPitchbendInSemitones = pitchbendDimension.lastValueReceivedOnChannel[zone->getMasterChannel() - 1].asSignedFloat() * zone->getMasterPitchbendRange(); - note.totalPitchbendInSemitones = notePitchbendInSemitones + masterPitchbendInSemitones; - } - else - { - // oops - this note seems to not belong to any zone! - jassertfalse; - } - } -} - -//============================================================================== -void MPEInstrument::sustainPedal (int midiChannel, bool isDown) -{ - const ScopedLock sl (lock); - handleSustainOrSostenuto (midiChannel, isDown, false); -} - -void MPEInstrument::sostenutoPedal (int midiChannel, bool isDown) -{ - const ScopedLock sl (lock); - handleSustainOrSostenuto (midiChannel, isDown, true); -} - -//============================================================================== -void MPEInstrument::handleSustainOrSostenuto (int midiChannel, bool isDown, bool isSostenuto) -{ - // in MPE mode, sustain/sostenuto is per-zone and expected on the master channel; - // in legacy mode, sustain/sostenuto is per MIDI channel (within the channel range used). - - MPEZone* affectedZone = zoneLayout.getZoneByMasterChannel (midiChannel); - - if (legacyMode.isEnabled ? (! legacyMode.channelRange.contains (midiChannel)) : (affectedZone == nullptr)) - return; - - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (legacyMode.isEnabled ? (note.midiChannel == midiChannel) : affectedZone->isUsingChannel (note.midiChannel)) - { - if (note.keyState == MPENote::keyDown && isDown) - note.keyState = MPENote::keyDownAndSustained; - else if (note.keyState == MPENote::sustained && ! isDown) - note.keyState = MPENote::off; - else if (note.keyState == MPENote::keyDownAndSustained && ! isDown) - note.keyState = MPENote::keyDown; - - if (note.keyState == MPENote::off) - { - listeners.call (&MPEInstrument::Listener::noteReleased, note); - notes.remove (i); - } - else - { - listeners.call (&MPEInstrument::Listener::noteKeyStateChanged, note); - } - } - } - - if (! isSostenuto) - { - if (legacyMode.isEnabled) - isNoteChannelSustained[midiChannel - 1] = isDown; - else - for (int i = affectedZone->getFirstNoteChannel(); i <= affectedZone->getLastNoteChannel(); ++i) - isNoteChannelSustained[i - 1] = isDown; - } -} - -//============================================================================== -bool MPEInstrument::isNoteChannel (int midiChannel) const noexcept -{ - if (legacyMode.isEnabled) - return legacyMode.channelRange.contains (midiChannel); - - return zoneLayout.getZoneByNoteChannel (midiChannel) != nullptr; -} - -bool MPEInstrument::isMasterChannel (int midiChannel) const noexcept -{ - if (legacyMode.isEnabled) - return false; - - return zoneLayout.getZoneByMasterChannel (midiChannel) != nullptr; -} - -//============================================================================== -int MPEInstrument::getNumPlayingNotes() const noexcept -{ - return notes.size(); -} - -MPENote MPEInstrument::getNote (int midiChannel, int midiNoteNumber) const noexcept -{ - if (MPENote* note = getNotePtr (midiChannel, midiNoteNumber)) - return *note; - - return MPENote(); -} - -MPENote MPEInstrument::getNote (int index) const noexcept -{ - return notes[index]; -} - -//============================================================================== -MPENote MPEInstrument::getMostRecentNote (int midiChannel) const noexcept -{ - if (MPENote* note = getLastNotePlayedPtr (midiChannel)) - return *note; - - return MPENote(); -} - -MPENote MPEInstrument::getMostRecentNoteOtherThan (MPENote otherThanThisNote) const noexcept -{ - for (int i = notes.size(); --i >= 0;) - { - const MPENote& note = notes.getReference (i); - - if (note != otherThanThisNote) - return note; - } - - return MPENote(); -} - -//============================================================================== -MPENote* MPEInstrument::getNotePtr (int midiChannel, int midiNoteNumber) const noexcept -{ - for (int i = 0; i < notes.size(); ++i) - { - MPENote& note = notes.getReference (i); - - if (note.midiChannel == midiChannel && note.initialNote == midiNoteNumber) - return ¬e; - } - - return nullptr; -} - -//============================================================================== -MPENote* MPEInstrument::getNotePtr (int midiChannel, TrackingMode mode) const noexcept -{ - // for the "all notes" tracking mode, this method can never possibly - // work because it returns 0 or 1 note but there might be more than one! - jassert (mode != allNotesOnChannel); - - if (mode == lastNotePlayedOnChannel) return getLastNotePlayedPtr (midiChannel); - if (mode == lowestNoteOnChannel) return getLowestNotePtr (midiChannel); - if (mode == highestNoteOnChannel) return getHighestNotePtr (midiChannel); - - return nullptr; -} - -//============================================================================== -MPENote* MPEInstrument::getLastNotePlayedPtr (int midiChannel) const noexcept -{ - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (note.midiChannel == midiChannel - && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained)) - return ¬e; - } - - return nullptr; -} - -//============================================================================== -MPENote* MPEInstrument::getHighestNotePtr (int midiChannel) const noexcept -{ - int initialNoteMax = -1; - MPENote* result = nullptr; - - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (note.midiChannel == midiChannel - && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained) - && note.initialNote > initialNoteMax) - { - result = ¬e; - initialNoteMax = note.initialNote; - } - } - - return result; -} - -MPENote* MPEInstrument::getLowestNotePtr (int midiChannel) const noexcept -{ - int initialNoteMin = 128; - MPENote* result = nullptr; - - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - - if (note.midiChannel == midiChannel - && (note.keyState == MPENote::keyDown || note.keyState == MPENote::keyDownAndSustained) - && note.initialNote < initialNoteMin) - { - result = ¬e; - initialNoteMin = note.initialNote; - } - } - - return result; -} - -//============================================================================== -void MPEInstrument::releaseAllNotes() -{ - const ScopedLock sl (lock); - - for (int i = notes.size(); --i >= 0;) - { - MPENote& note = notes.getReference (i); - note.keyState = MPENote::off; - note.noteOffVelocity = MPEValue::from7BitInt (64); // some reasonable number - listeners.call (&MPEInstrument::Listener::noteReleased, note); - } - - notes.clear(); -} - -//============================================================================== -//============================================================================== -#if JUCE_UNIT_TESTS - -class MPEInstrumentTests : public UnitTest -{ -public: - MPEInstrumentTests() - : UnitTest ("MPEInstrument class", "MIDI/MPE") - { - // using two MPE zones with the following layout for testing - // - // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - // * ...................| * ........................| - - testLayout.addZone (MPEZone (2, 5)); - testLayout.addZone (MPEZone (9, 6)); - } - - void runTest() override - { - beginTest ("initial zone layout"); - { - MPEInstrument test; - expectEquals (test.getZoneLayout().getNumZones(), 0); - } - - beginTest ("get/setZoneLayout"); - { - MPEInstrument test; - test.setZoneLayout (testLayout); - - MPEZoneLayout newLayout = test.getZoneLayout(); - expectEquals (newLayout.getNumZones(), 2); - expectEquals (newLayout.getZoneByIndex (0)->getMasterChannel(), 2); - expectEquals (newLayout.getZoneByIndex (0)->getNumNoteChannels(), 5); - expectEquals (newLayout.getZoneByIndex (1)->getMasterChannel(), 9); - expectEquals (newLayout.getZoneByIndex (1)->getNumNoteChannels(), 6); - } - - beginTest ("noteOn / noteOff"); - { - { - MPEInstrument test; - test.setZoneLayout (testLayout); - expectEquals (test.getNumPlayingNotes(), 0); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // note-on on master channel - ignore - test.noteOn (9, 60, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 0); - expectEquals (test.noteAddedCallCounter, 0); - - // note-on on any other channel - ignore - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 0); - expectEquals (test.noteAddedCallCounter, 0); - - // note-on on note channel - create new note - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 1); - expectEquals (test.noteAddedCallCounter, 1); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - - // note-off - test.noteOff (3, 60, MPEValue::from7BitInt (33)); - expectEquals (test.getNumPlayingNotes(), 0); - expectEquals (test.noteReleasedCallCounter, 1); - expectHasFinishedNote (test, 3, 60, 33); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - - // note off with non-matching note number shouldn't do anything - test.noteOff (3, 61, MPEValue::from7BitInt (33)); - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteReleasedCallCounter, 0); - - // note off with non-matching midi channel shouldn't do anything - test.noteOff (2, 60, MPEValue::from7BitInt (33)); - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteReleasedCallCounter, 0); - } - { - // can have multiple notes on the same channel - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 0, MPEValue::from7BitInt (100)); - test.noteOn (3, 1, MPEValue::from7BitInt (100)); - test.noteOn (3, 2, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 3); - expectNote (test.getNote (3, 0), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 1), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 2), 100, 0, 8192, 64, MPENote::keyDown); - } - { - // pathological case: second note-on for same note should retrigger it. - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 0, MPEValue::from7BitInt (100)); - test.noteOn (3, 0, MPEValue::from7BitInt (60)); - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 0), 60, 0, 8192, 64, MPENote::keyDown); - } - } - - beginTest ("noteReleased after setZoneLayout"); - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.noteOn (4, 61, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 3); - expectEquals (test.noteReleasedCallCounter, 0); - - test.setZoneLayout (testLayout); - expectEquals (test.getNumPlayingNotes(), 0); - expectEquals (test.noteReleasedCallCounter, 3); - } - - beginTest ("releaseAllNotes"); - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (4, 61, MPEValue::from7BitInt (100)); - test.noteOn (15, 62, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 3); - - test.releaseAllNotes(); - expectEquals (test.getNumPlayingNotes(), 0); - } - - beginTest ("sustainPedal"); - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in Zone 1 - test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in Zone 2 - - // sustain pedal on per-note channel shouldn't do anything. - test.sustainPedal (3, true); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - - - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 0); - - // sustain pedal on non-zone channel shouldn't do anything either. - test.sustainPedal (1, true); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 0); - - // sustain pedal on master channel should sustain notes on *that* zone. - test.sustainPedal (2, true); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 1); - - // release - test.sustainPedal (2, false); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 2); - - // should also sustain new notes added after the press - test.sustainPedal (2, true); - expectEquals (test.noteKeyStateChangedCallCounter, 3); - test.noteOn (4, 51, MPEValue::from7BitInt (100)); - expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDownAndSustained); - expectEquals (test.noteKeyStateChangedCallCounter, 3); - - // ...but only if that sustain came on the master channel of that zone! - test.sustainPedal (11, true); - test.noteOn (11, 52, MPEValue::from7BitInt (100)); - expectNote (test.getNote (11, 52), 100, 0, 8192, 64, MPENote::keyDown); - test.noteOff (11, 52, MPEValue::from7BitInt (100)); - expectEquals (test.noteReleasedCallCounter, 1); - - // note-off should not turn off sustained notes inside the same zone - test.noteOff (3, 60, MPEValue::from7BitInt (100)); - test.noteOff (4, 51, MPEValue::from7BitInt (100)); - test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected! - expectEquals (test.getNumPlayingNotes(), 2); - expectEquals (test.noteReleasedCallCounter, 2); - expectEquals (test.noteKeyStateChangedCallCounter, 5); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); - expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::sustained); - - // notes should be turned off when pedal is released - test.sustainPedal (2, false); - expectEquals (test.getNumPlayingNotes(), 0); - expectEquals (test.noteReleasedCallCounter, 4); - } - - beginTest ("sostenutoPedal"); - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); // note in Zone 1 - test.noteOn (10, 60, MPEValue::from7BitInt (100)); // note in Zone 2 - - // sostenuto pedal on per-note channel shouldn't do anything. - test.sostenutoPedal (3, true); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 0); - - // sostenuto pedal on non-zone channel shouldn't do anything either. - test.sostenutoPedal (1, true); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 0); - - // sostenuto pedal on master channel should sustain notes on *that* zone. - test.sostenutoPedal (2, true); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 1); - - // release - test.sostenutoPedal (2, false); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 2); - - // should only sustain notes turned on *before* the press (difference to sustain pedal) - test.sostenutoPedal (2, true); - expectEquals (test.noteKeyStateChangedCallCounter, 3); - test.noteOn (4, 51, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 3); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDownAndSustained); - expectNote (test.getNote (4, 51), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteKeyStateChangedCallCounter, 3); - - // note-off should not turn off sustained notes inside the same zone, - // but only if they were turned on *before* the sostenuto pedal (difference to sustain pedal) - test.noteOff (3, 60, MPEValue::from7BitInt (100)); - test.noteOff (4, 51, MPEValue::from7BitInt (100)); - test.noteOff (10, 60, MPEValue::from7BitInt (100)); // not affected! - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); - expectEquals (test.noteReleasedCallCounter, 2); - expectEquals (test.noteKeyStateChangedCallCounter, 4); - - // notes should be turned off when pedal is released - test.sustainPedal (2, false); - expectEquals (test.getNumPlayingNotes(), 0); - expectEquals (test.noteReleasedCallCounter, 3); - } - - beginTest ("getMostRecentNote"); - { - MPEInstrument test; - test.setZoneLayout (testLayout); - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - - { - MPENote note = test.getMostRecentNote (2); - expect (! note.isValid()); - } - { - MPENote note = test.getMostRecentNote (3); - expect (note.isValid()); - expectEquals (int (note.midiChannel), 3); - expectEquals (int (note.initialNote), 61); - } - - test.sustainPedal (2, true); - test.noteOff (3, 61, MPEValue::from7BitInt (100)); - - { - MPENote note = test.getMostRecentNote (3); - expect (note.isValid()); - expectEquals (int (note.midiChannel), 3); - expectEquals (int (note.initialNote), 60); - } - - test.sustainPedal (2, false); - test.noteOff (3, 60, MPEValue::from7BitInt (100)); - - { - MPENote note = test.getMostRecentNote (3); - expect (! note.isValid()); - } - } - - beginTest ("getMostRecentNoteOtherThan"); - { - MPENote testNote (3, 60, - MPEValue::centreValue(), MPEValue::centreValue(), - MPEValue::centreValue(), MPEValue::centreValue()); - - { - // case 1: the note to exclude is not the most recent one. - - MPEInstrument test; - test.setZoneLayout (testLayout); - expect (! test.getMostRecentNoteOtherThan (testNote).isValid()); - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expect (! test.getMostRecentNoteOtherThan (testNote).isValid()); - - test.noteOn (4, 61, MPEValue::from7BitInt (100)); - expect (test.getMostRecentNoteOtherThan (testNote).isValid()); - expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4); - expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61); - } - { - // case 2: the note to exclude is the most recent one. - - MPEInstrument test; - test.setZoneLayout (testLayout); - expect (! test.getMostRecentNoteOtherThan (testNote).isValid()); - - test.noteOn (4, 61, MPEValue::from7BitInt (100)); - expect (test.getMostRecentNoteOtherThan (testNote).isValid()); - expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4); - expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61); - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expect (test.getMostRecentNoteOtherThan (testNote).isValid()); - expect (test.getMostRecentNoteOtherThan (testNote).midiChannel == 4); - expect (test.getMostRecentNoteOtherThan (testNote).initialNote == 61); - } - } - - beginTest ("pressure"); - { - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (4, 60, MPEValue::from7BitInt (100)); - test.noteOn (10, 60, MPEValue::from7BitInt (100)); - - // applying pressure on a per-note channel should modulate one note - test.pressure (3, MPEValue::from7BitInt (33)); - expectNote (test.getNote (3, 60), 100, 33, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 1); - - // applying pressure on a master channel should modulate all notes in this zone - test.pressure (2, MPEValue::from7BitInt (44)); - expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 3); - - // applying pressure on an unrelated channel should be ignored - test.pressure (1, MPEValue::from7BitInt (55)); - expectNote (test.getNote (3, 60), 100, 44, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 44, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 3); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // two notes on same channel - only last added should be modulated - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (66)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 66, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 1); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // edge case: two notes on same channel, one gets released, - // then the other should be modulated - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.noteOff (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (77)); - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 1); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // if no pressure is sent before note-on, default = 0 should be used - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // if pressure is sent before note-on, use that - test.pressure (3, MPEValue::from7BitInt (77)); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectNote (test.getNote (3, 60), 100, 77, 8192, 64, MPENote::keyDown); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // if pressure is sent before note-on, but it belonged to another note - // on the same channel that has since been turned off, use default = 0 - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (77)); - test.noteOff (3, 61, MPEValue::from7BitInt (100)); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // edge case: two notes on the same channel simultaneously. the second one should use - // pressure = 0 initially but then react to additional pressure messages - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (77)); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - test.pressure (3, MPEValue::from7BitInt (78)); - expectNote (test.getNote (3, 60), 100, 78, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 77, 8192, 64, MPENote::keyDown); - } - } - - beginTest ("pitchbend"); - { - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (4, 60, MPEValue::from7BitInt (100)); - test.noteOn (10, 60, MPEValue::from7BitInt (100)); - - // applying pitchbend on a per-note channel should modulate one note - test.pitchbend (3, MPEValue::from14BitInt (1111)); - expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 1); - - // applying pitchbend on a master channel should be ignored for the - // value of per-note pitchbend. Tests covering master pitchbend below. - // Note: noteChanged will be called anyway for notes in that zone - // because the total pitchbend for those notes has changed - test.pitchbend (2, MPEValue::from14BitInt (2222)); - expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 3); - - // applying pitchbend on an unrelated channel should do nothing. - test.pitchbend (1, MPEValue::from14BitInt (3333)); - expectNote (test.getNote (3, 60), 100, 0, 1111, 64, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 3); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // two notes on same channel - only last added should be bent - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (4444)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 4444, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 1); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // edge case: two notes on same channel, one gets released, - // then the other should be bent - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.noteOff (3, 61, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (5555)); - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 1); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // Richard's edge case: - // - press one note - // - press sustain (careful: must be sent on master channel) - // - release first note (is still sustained!) - // - press another note (happens to be on the same MIDI channel!) - // - pitchbend that other note - // - the first note should not be bent, only the second one. - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.sustainPedal (2, true); - test.noteOff (3, 60, MPEValue::from7BitInt (64)); - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); - expectEquals (test.noteKeyStateChangedCallCounter, 2); - - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (6666)); - expectEquals (test.getNumPlayingNotes(), 2); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::sustained); - expectNote (test.getNote (3, 61), 100, 0, 6666, 64, MPENote::keyDownAndSustained); - expectEquals (test.notePitchbendChangedCallCounter, 1); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // Zsolt's edge case: - // - press one note - // - modulate pitchbend or timbre - // - release the note - // - press same note again without sending a pitchbend or timbre message before the note-on - // - the note should be turned on with a default value for pitchbend/timbre, - // and *not* the last value received on channel. - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (5555)); - expectNote (test.getNote (3, 60), 100, 0, 5555, 64, MPENote::keyDown); - - test.noteOff (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - } - { - // applying per-note pitchbend should set the note's totalPitchbendInSemitones - // correctly depending on the per-note pitchbend range of the zone. - UnitTestInstrument test; - - MPEZoneLayout layout = testLayout; - test.setZoneLayout (layout); // default should be +/- 48 semitones - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (4096)); - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -24.0, 0.01); - - layout.getZoneByIndex (0)->setPerNotePitchbendRange (96); - test.setZoneLayout (layout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (0)); // -max - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01); - - layout.getZoneByIndex (0)->setPerNotePitchbendRange (1); - test.setZoneLayout (layout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (16383)); // +max - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01); - - layout.getZoneByIndex (0)->setPerNotePitchbendRange (0); // pitchbendrange = 0 --> no pitchbend at all - test.setZoneLayout (layout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (12345)); - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01); - } - { - // applying master pitchbend should set the note's totalPitchbendInSemitones - // correctly depending on the master pitchbend range of the zone. - UnitTestInstrument test; - - MPEZoneLayout layout = testLayout; - test.setZoneLayout (layout); // default should be +/- 2 semitones - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (2, MPEValue::from14BitInt (4096)); //halfway between -max and centre - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -1.0, 0.01); - - layout.getZoneByIndex (0)->setMasterPitchbendRange (96); - test.setZoneLayout (layout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (2, MPEValue::from14BitInt (0)); // -max - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -96.0, 0.01); - - layout.getZoneByIndex (0)->setMasterPitchbendRange (1); - test.setZoneLayout (layout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (2, MPEValue::from14BitInt (16383)); // +max - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 1.0, 0.01); - - layout.getZoneByIndex (0)->setMasterPitchbendRange (0); // pitchbendrange = 0 --> no pitchbend at all - test.setZoneLayout (layout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.pitchbend (2, MPEValue::from14BitInt (12345)); - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, 0.0, 0.01); - } - { - // applying both per-note and master pitchbend simultaneously should set - // the note's totalPitchbendInSemitones to the sum of both, correctly - // weighted with the per-note and master pitchbend range, respectively. - UnitTestInstrument test; - - MPEZoneLayout layout = testLayout; - layout.getZoneByIndex (0)->setPerNotePitchbendRange (12); - layout.getZoneByIndex (0)->setMasterPitchbendRange (1); - test.setZoneLayout (layout); - - test.pitchbend (2, MPEValue::from14BitInt (4096)); // master pitchbend 0.5 semitones down - test.pitchbend (3, MPEValue::from14BitInt (0)); // per-note pitchbend 12 semitones down - // additionally, note should react to both pitchbend messages - // correctly even if they arrived before the note-on. - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectDoubleWithinRelativeError (test.getMostRecentNote (3).totalPitchbendInSemitones, -12.5, 0.01); - } - } - - beginTest ("timbre"); - { - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (4, 60, MPEValue::from7BitInt (100)); - test.noteOn (10, 60, MPEValue::from7BitInt (100)); - - // modulating timbre on a per-note channel should modulate one note - test.timbre (3, MPEValue::from7BitInt (33)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 33, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 1); - - // modulating timbre on a master channel should modulate all notes in this zone - test.timbre (2, MPEValue::from7BitInt (44)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 3); - - // modulating timbre on an unrelated channel should be ignored - test.timbre (1, MPEValue::from7BitInt (55)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 44, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 44, MPENote::keyDown); - expectNote (test.getNote (10, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 3); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // two notes on same channel - only last added should be modulated - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.timbre (3, MPEValue::from7BitInt (66)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 66, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 1); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // edge case: two notes on same channel, one gets released, - // then the other should be modulated - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.noteOff (3, 61, MPEValue::from7BitInt (100)); - test.timbre (3, MPEValue::from7BitInt (77)); - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (3, 60), 100, 0, 8192, 77, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 1); - } - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - // Zsolt's edge case for timbre - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.timbre (3, MPEValue::from7BitInt (42)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 42, MPENote::keyDown); - - test.noteOff (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - } - } - - beginTest ("setPressureTrackingMode"); - { - { - // last note played (= default) - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPressureTrackingMode (MPEInstrument::lastNotePlayedOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 1); - } - { - // lowest note - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPressureTrackingMode (MPEInstrument::lowestNoteOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 1); - } - { - // highest note - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPressureTrackingMode (MPEInstrument::highestNoteOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 1); - } - { - // all notes - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPressureTrackingMode (MPEInstrument::allNotesOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pressure (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 99, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 99, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 99, 8192, 64, MPENote::keyDown); - expectEquals (test.notePressureChangedCallCounter, 3); - } - } - - beginTest ("setPitchbendTrackingMode"); - { - { - // last note played (= default) - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 1); - } - { - // lowest note - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 1); - } - { - // highest note - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 1); - } - { - // all notes - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.pitchbend (3, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (3, 60), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 9999, 64, MPENote::keyDown); - expectEquals (test.notePitchbendChangedCallCounter, 3); - } - } - - beginTest ("setTimbreTrackingMode"); - { - { - // last note played (= default) - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setTimbreTrackingMode (MPEInstrument::lastNotePlayedOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.timbre (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 1); - } - { - // lowest note - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setTimbreTrackingMode (MPEInstrument::lowestNoteOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.timbre (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 1); - } - { - // highest note - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setTimbreTrackingMode (MPEInstrument::highestNoteOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.timbre (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 1); - } - { - // all notes - UnitTestInstrument test; - test.setZoneLayout (testLayout); - - test.setTimbreTrackingMode (MPEInstrument::allNotesOnChannel); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 62, MPEValue::from7BitInt (100)); - test.noteOn (3, 61, MPEValue::from7BitInt (100)); - test.timbre (3, MPEValue::from7BitInt (99)); - expectNote (test.getNote (3, 60), 100, 0, 8192, 99, MPENote::keyDown); - expectNote (test.getNote (3, 62), 100, 0, 8192, 99, MPENote::keyDown); - expectNote (test.getNote (3, 61), 100, 0, 8192, 99, MPENote::keyDown); - expectEquals (test.noteTimbreChangedCallCounter, 3); - } - } - - beginTest ("processNextMidiEvent"); - { - UnitTestInstrument test; - - // note on should trigger noteOn method call - - test.processNextMidiEvent (MidiMessage::noteOn (3, 42, uint8 (92))); - expectEquals (test.noteOnCallCounter, 1); - expectEquals (test.lastMidiChannelReceived, 3); - expectEquals (test.lastMidiNoteNumberReceived, 42); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 92); - - // note off should trigger noteOff method call - - test.processNextMidiEvent (MidiMessage::noteOff (4, 12, uint8 (33))); - expectEquals (test.noteOffCallCounter, 1); - expectEquals (test.lastMidiChannelReceived, 4); - expectEquals (test.lastMidiNoteNumberReceived, 12); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 33); - - // note on with velocity = 0 should trigger noteOff method call - // with a note off velocity of 64 (centre value) - - test.processNextMidiEvent (MidiMessage::noteOn (5, 11, uint8 (0))); - expectEquals (test.noteOffCallCounter, 2); - expectEquals (test.lastMidiChannelReceived, 5); - expectEquals (test.lastMidiNoteNumberReceived, 11); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 64); - - // pitchwheel message should trigger pitchbend method call - - test.processNextMidiEvent (MidiMessage::pitchWheel (1, 3333)); - expectEquals (test.pitchbendCallCounter, 1); - expectEquals (test.lastMidiChannelReceived, 1); - expectEquals (test.lastMPEValueReceived.as14BitInt(), 3333); - - // pressure using channel pressure message (7-bit value) should - // trigger pressure method call - - test.processNextMidiEvent (MidiMessage::channelPressureChange (10, 35)); - expectEquals (test.pressureCallCounter, 1); - expectEquals (test.lastMidiChannelReceived, 10); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 35); - - // pressure using 14-bit value over CC70 and CC102 should trigger - // pressure method call after the MSB is sent - - // a) sending only the MSB - test.processNextMidiEvent (MidiMessage::controllerEvent (3, 70, 120)); - expectEquals (test.pressureCallCounter, 2); - expectEquals (test.lastMidiChannelReceived, 3); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 120); - - // b) sending LSB and MSB (only the MSB should trigger the call) - per MIDI channel! - test.processNextMidiEvent (MidiMessage::controllerEvent (4, 102, 121)); - expectEquals (test.pressureCallCounter, 2); - test.processNextMidiEvent (MidiMessage::controllerEvent (5, 102, 122)); - expectEquals (test.pressureCallCounter, 2); - test.processNextMidiEvent (MidiMessage::controllerEvent (4, 70, 123)); - expectEquals (test.pressureCallCounter, 3); - expectEquals (test.lastMidiChannelReceived, 4); - expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7)); - test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 124)); - expectEquals (test.pressureCallCounter, 4); - expectEquals (test.lastMidiChannelReceived, 5); - expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7)); - test.processNextMidiEvent (MidiMessage::controllerEvent (5, 70, 64)); - expectEquals (test.pressureCallCounter, 5); - expectEquals (test.lastMidiChannelReceived, 5); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 64); - - // same for timbre 14-bit value over CC74 and CC106 - test.processNextMidiEvent (MidiMessage::controllerEvent (3, 74, 120)); - expectEquals (test.timbreCallCounter, 1); - expectEquals (test.lastMidiChannelReceived, 3); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 120); - test.processNextMidiEvent (MidiMessage::controllerEvent (4, 106, 121)); - expectEquals (test.timbreCallCounter, 1); - test.processNextMidiEvent (MidiMessage::controllerEvent (5, 106, 122)); - expectEquals (test.timbreCallCounter, 1); - test.processNextMidiEvent (MidiMessage::controllerEvent (4, 74, 123)); - expectEquals (test.timbreCallCounter, 2); - expectEquals (test.lastMidiChannelReceived, 4); - expectEquals (test.lastMPEValueReceived.as14BitInt(), 121 + (123 << 7)); - test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 124)); - expectEquals (test.timbreCallCounter, 3); - expectEquals (test.lastMidiChannelReceived, 5); - expectEquals (test.lastMPEValueReceived.as14BitInt(), 122 + (124 << 7)); - test.processNextMidiEvent (MidiMessage::controllerEvent (5, 74, 64)); - expectEquals (test.timbreCallCounter, 4); - expectEquals (test.lastMidiChannelReceived, 5); - expectEquals (test.lastMPEValueReceived.as7BitInt(), 64); - - // sustain pedal message (CC64) should trigger sustainPedal method call - test.processNextMidiEvent (MidiMessage::controllerEvent (1, 64, 127)); - expectEquals (test.sustainPedalCallCounter, 1); - expectEquals (test.lastMidiChannelReceived, 1); - expect (test.lastSustainPedalValueReceived); - test.processNextMidiEvent (MidiMessage::controllerEvent (16, 64, 0)); - expectEquals (test.sustainPedalCallCounter, 2); - expectEquals (test.lastMidiChannelReceived, 16); - expect (! test.lastSustainPedalValueReceived); - - // sostenuto pedal message (CC66) should trigger sostenutoPedal method call - test.processNextMidiEvent (MidiMessage::controllerEvent (1, 66, 127)); - expectEquals (test.sostenutoPedalCallCounter, 1); - expectEquals (test.lastMidiChannelReceived, 1); - expect (test.lastSostenutoPedalValueReceived); - test.processNextMidiEvent (MidiMessage::controllerEvent (16, 66, 0)); - expectEquals (test.sostenutoPedalCallCounter, 2); - expectEquals (test.lastMidiChannelReceived, 16); - expect (! test.lastSostenutoPedalValueReceived); - } - { - // MIDI messages modifying the zone layout should be correctly - // forwarded to the internal zone layout and modify it. - // (testing the actual logic of the zone layout is done in the - // MPEZoneLayout unit tests) - MPEInstrument test; - - MidiBuffer buffer; - buffer.addEvents (MPEMessages::addZone (MPEZone (2, 5)), 0, -1, 0); - buffer.addEvents (MPEMessages::addZone (MPEZone (9, 6)), 0, -1, 0); - - MidiBuffer::Iterator iter (buffer); - MidiMessage message; - int samplePosition; // not actually used, so no need to initialise. - - while (iter.getNextEvent (message, samplePosition)) - test.processNextMidiEvent (message); - - expectEquals (test.getZoneLayout().getNumZones(), 2); - expectEquals (test.getZoneLayout().getZoneByIndex (0)->getMasterChannel(), 2); - expectEquals (test.getZoneLayout().getZoneByIndex (0)->getNumNoteChannels(), 5); - expectEquals (test.getZoneLayout().getZoneByIndex (1)->getMasterChannel(), 9); - expectEquals (test.getZoneLayout().getZoneByIndex (1)->getNumNoteChannels(), 6); - } - - beginTest ("MIDI all notes off"); - { - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (4, 61, MPEValue::from7BitInt (100)); - test.noteOn (15, 62, MPEValue::from7BitInt (100)); - test.noteOn (15, 63, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 4); - - // on note channel: ignore. - test.processNextMidiEvent (MidiMessage::allNotesOff (3)); - expectEquals (test.getNumPlayingNotes(), 4); - - // on unused channel: ignore. - test.processNextMidiEvent (MidiMessage::allNotesOff (1)); - expectEquals (test.getNumPlayingNotes(), 4); - - // on master channel: release notes in that zone only. - test.processNextMidiEvent (MidiMessage::allNotesOff (2)); - expectEquals (test.getNumPlayingNotes(), 2); - test.processNextMidiEvent (MidiMessage::allNotesOff (9)); - expectEquals (test.getNumPlayingNotes(), 0); - } - - beginTest ("MIDI all notes off (legacy mode)"); - { - UnitTestInstrument test; - test.enableLegacyMode(); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - test.noteOn (4, 61, MPEValue::from7BitInt (100)); - test.noteOn (15, 62, MPEValue::from7BitInt (100)); - test.noteOn (15, 63, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 4); - - test.processNextMidiEvent (MidiMessage::allNotesOff (3)); - expectEquals (test.getNumPlayingNotes(), 3); - - test.processNextMidiEvent (MidiMessage::allNotesOff (15)); - expectEquals (test.getNumPlayingNotes(), 1); - - test.processNextMidiEvent (MidiMessage::allNotesOff (4)); - expectEquals (test.getNumPlayingNotes(), 0); - } - - beginTest ("default initial values for pitchbend and timbre"); - { - MPEInstrument test; - test.setZoneLayout (testLayout); - - test.pitchbend (3, MPEValue::from14BitInt (3333)); // use for next note-on on ch. 3 - test.pitchbend (2, MPEValue::from14BitInt (4444)); // ignore - test.pitchbend (2, MPEValue::from14BitInt (5555)); // ignore - - test.timbre (3, MPEValue::from7BitInt (66)); // use for next note-on on ch. 3 - test.timbre (2, MPEValue::from7BitInt (77)); // ignore - test.timbre (2, MPEValue::from7BitInt (88)); // ignore - - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - - expectNote (test.getMostRecentNote (3), 100, 0, 3333, 66, MPENote::keyDown); - } - - beginTest ("Legacy mode"); - { - { - // basic check - MPEInstrument test; - expect (! test.isLegacyModeEnabled()); - - test.setZoneLayout (testLayout); - expect (! test.isLegacyModeEnabled()); - - test.enableLegacyMode(); - expect (test.isLegacyModeEnabled()); - - test.setZoneLayout (testLayout); - expect (! test.isLegacyModeEnabled()); - } - { - // constructor w/o default arguments - MPEInstrument test; - test.enableLegacyMode (0, Range (1, 11)); - expectEquals (test.getLegacyModePitchbendRange(), 0); - expect (test.getLegacyModeChannelRange() == Range (1, 11)); - } - { - // getters and setters - MPEInstrument test; - test.enableLegacyMode(); - - expectEquals (test.getLegacyModePitchbendRange(), 2); - expect (test.getLegacyModeChannelRange() == Range (1, 17)); - - test.setLegacyModePitchbendRange (96); - expectEquals (test.getLegacyModePitchbendRange(), 96); - - test.setLegacyModeChannelRange (Range (10, 12)); - expect (test.getLegacyModeChannelRange() == Range (10, 12)); - } - { - // note on should trigger notes on all 16 channels - - UnitTestInstrument test; - test.enableLegacyMode(); - - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.noteOn (2, 60, MPEValue::from7BitInt (100)); - test.noteOn (15, 60, MPEValue::from7BitInt (100)); - test.noteOn (16, 60, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 4); - - // polyphonic modulation should work across all 16 channels - - test.pitchbend (1, MPEValue::from14BitInt (9999)); - test.pressure (2, MPEValue::from7BitInt (88)); - test.timbre (15, MPEValue::from7BitInt (77)); - - expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (2, 60), 100, 88, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (15, 60), 100, 0, 8192, 77, MPENote::keyDown); - expectNote (test.getNote (16, 60), 100, 0, 8192, 64, MPENote::keyDown); - - // note off should work in legacy mode - - test.noteOff (15, 60, MPEValue::from7BitInt (0)); - test.noteOff (1, 60, MPEValue::from7BitInt (0)); - test.noteOff (2, 60, MPEValue::from7BitInt (0)); - test.noteOff (16, 60, MPEValue::from7BitInt (0)); - expectEquals (test.getNumPlayingNotes(), 0); - } - { - // legacy mode w/ custom channel range: note on should trigger notes only within range - - UnitTestInstrument test; - test.enableLegacyMode (2, Range (3, 8)); // channels 3-7 - - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.noteOn (2, 60, MPEValue::from7BitInt (100)); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); // should trigger - test.noteOn (4, 60, MPEValue::from7BitInt (100)); // should trigger - test.noteOn (6, 60, MPEValue::from7BitInt (100)); // should trigger - test.noteOn (7, 60, MPEValue::from7BitInt (100)); // should trigger - test.noteOn (8, 60, MPEValue::from7BitInt (100)); - test.noteOn (16, 60, MPEValue::from7BitInt (100)); - - expectEquals (test.getNumPlayingNotes(), 4); - expectNote (test.getNote (3, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (4, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (6, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (7, 60), 100, 0, 8192, 64, MPENote::keyDown); - } - { - // tracking mode in legacy mode - { - UnitTestInstrument test; - test.enableLegacyMode(); - - test.setPitchbendTrackingMode (MPEInstrument::lastNotePlayedOnChannel); - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.noteOn (1, 62, MPEValue::from7BitInt (100)); - test.noteOn (1, 61, MPEValue::from7BitInt (100)); - test.pitchbend (1, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown); - } - { - UnitTestInstrument test; - test.enableLegacyMode(); - - test.setPitchbendTrackingMode (MPEInstrument::lowestNoteOnChannel); - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.noteOn (1, 62, MPEValue::from7BitInt (100)); - test.noteOn (1, 61, MPEValue::from7BitInt (100)); - test.pitchbend (1, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (1, 62), 100, 0, 8192, 64, MPENote::keyDown); - } - { - UnitTestInstrument test; - test.enableLegacyMode(); - - test.setPitchbendTrackingMode (MPEInstrument::highestNoteOnChannel); - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.noteOn (1, 62, MPEValue::from7BitInt (100)); - test.noteOn (1, 61, MPEValue::from7BitInt (100)); - test.pitchbend (1, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (1, 61), 100, 0, 8192, 64, MPENote::keyDown); - expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown); - } - { - UnitTestInstrument test; - test.enableLegacyMode(); - - test.setPitchbendTrackingMode (MPEInstrument::allNotesOnChannel); - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.noteOn (1, 62, MPEValue::from7BitInt (100)); - test.noteOn (1, 61, MPEValue::from7BitInt (100)); - test.pitchbend (1, MPEValue::from14BitInt (9999)); - expectNote (test.getNote (1, 60), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (1, 61), 100, 0, 9999, 64, MPENote::keyDown); - expectNote (test.getNote (1, 62), 100, 0, 9999, 64, MPENote::keyDown); - } - } - { - // custom pitchbend range in legacy mode. - UnitTestInstrument test; - test.enableLegacyMode (11); - - test.pitchbend (1, MPEValue::from14BitInt (4096)); - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - expectDoubleWithinRelativeError (test.getMostRecentNote (1).totalPitchbendInSemitones, -5.5, 0.01); - } - { - // sustain pedal should be per channel in legacy mode. - UnitTestInstrument test; - test.enableLegacyMode(); - - test.sustainPedal (1, true); - test.noteOn (2, 61, MPEValue::from7BitInt (100)); - test.noteOff (2, 61, MPEValue::from7BitInt (100)); - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.noteOff (1, 60, MPEValue::from7BitInt (100)); - - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained); - - test.sustainPedal (1, false); - expectEquals (test.getNumPlayingNotes(), 0); - - test.noteOn (2, 61, MPEValue::from7BitInt (100)); - test.sustainPedal (1, true); - test.noteOff (2, 61, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 0); - - } - { - // sostenuto pedal should be per channel in legacy mode. - UnitTestInstrument test; - test.enableLegacyMode(); - - test.noteOn (1, 60, MPEValue::from7BitInt (100)); - test.sostenutoPedal (1, true); - test.noteOff (1, 60, MPEValue::from7BitInt (100)); - test.noteOn (2, 61, MPEValue::from7BitInt (100)); - test.noteOff (2, 61, MPEValue::from7BitInt (100)); - - expectEquals (test.getNumPlayingNotes(), 1); - expectNote (test.getNote (1, 60), 100, 0, 8192, 64, MPENote::sustained); - - test.sostenutoPedal (1, false); - expectEquals (test.getNumPlayingNotes(), 0); - - test.noteOn (2, 61, MPEValue::from7BitInt (100)); - test.sostenutoPedal (1, true); - test.noteOff (2, 61, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 0); - } - { - // all notes released when switching layout - UnitTestInstrument test; - test.setZoneLayout (testLayout); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 1); - - test.enableLegacyMode(); - expectEquals (test.getNumPlayingNotes(), 0); - test.noteOn (3, 60, MPEValue::from7BitInt (100)); - expectEquals (test.getNumPlayingNotes(), 1); - - test.setZoneLayout (testLayout); - expectEquals (test.getNumPlayingNotes(), 0); - } - } - } - -private: - //============================================================================== - /* This mock class is used for unit testing whether the methods of - MPEInstrument are called correctly. - */ - class UnitTestInstrument : public MPEInstrument, - private MPEInstrument::Listener - { - typedef MPEInstrument Base; - public: - UnitTestInstrument() - : noteOnCallCounter (0), noteOffCallCounter (0), pitchbendCallCounter (0), - pressureCallCounter (0), timbreCallCounter (0), sustainPedalCallCounter (0), - sostenutoPedalCallCounter (0), noteAddedCallCounter (0), notePressureChangedCallCounter (0), - notePitchbendChangedCallCounter (0), noteTimbreChangedCallCounter (0), - noteKeyStateChangedCallCounter (0), noteReleasedCallCounter (0), - lastMidiChannelReceived (-1), lastMidiNoteNumberReceived (-1), - lastSustainPedalValueReceived (false), lastSostenutoPedalValueReceived (false) - { - addListener (this); - } - - void noteOn (int midiChannel, int midiNoteNumber, MPEValue midiNoteOnVelocity) override - { - Base::noteOn (midiChannel, midiNoteNumber, midiNoteOnVelocity); - - noteOnCallCounter++; - lastMidiChannelReceived = midiChannel; - lastMidiNoteNumberReceived = midiNoteNumber; - lastMPEValueReceived = midiNoteOnVelocity; - } - - void noteOff (int midiChannel, int midiNoteNumber, MPEValue midiNoteOffVelocity) override - { - Base::noteOff (midiChannel, midiNoteNumber, midiNoteOffVelocity); - - noteOffCallCounter++; - lastMidiChannelReceived = midiChannel; - lastMidiNoteNumberReceived = midiNoteNumber; - lastMPEValueReceived = midiNoteOffVelocity; - } - - void pitchbend (int midiChannel, MPEValue value) override - { - Base::pitchbend (midiChannel, value); - - pitchbendCallCounter++; - lastMidiChannelReceived = midiChannel; - lastMPEValueReceived = value; - } - - void pressure (int midiChannel, MPEValue value) override - { - Base::pressure (midiChannel, value); - - pressureCallCounter++; - lastMidiChannelReceived = midiChannel; - lastMPEValueReceived = value; - } - - void timbre (int midiChannel, MPEValue value) override - { - Base::timbre (midiChannel, value); - - timbreCallCounter++; - lastMidiChannelReceived = midiChannel; - lastMPEValueReceived = value; - } - - void sustainPedal (int midiChannel, bool value) override - { - Base::sustainPedal (midiChannel, value); - - sustainPedalCallCounter++; - lastMidiChannelReceived = midiChannel; - lastSustainPedalValueReceived = value; - } - - void sostenutoPedal (int midiChannel, bool value) override - { - Base::sostenutoPedal (midiChannel, value); - - sostenutoPedalCallCounter++; - lastMidiChannelReceived = midiChannel; - lastSostenutoPedalValueReceived = value; - } - - int noteOnCallCounter, noteOffCallCounter, pitchbendCallCounter, - pressureCallCounter, timbreCallCounter, sustainPedalCallCounter, - sostenutoPedalCallCounter, noteAddedCallCounter, - notePressureChangedCallCounter, notePitchbendChangedCallCounter, - noteTimbreChangedCallCounter, noteKeyStateChangedCallCounter, - noteReleasedCallCounter, lastMidiChannelReceived, lastMidiNoteNumberReceived; - - bool lastSustainPedalValueReceived, lastSostenutoPedalValueReceived; - MPEValue lastMPEValueReceived; - ScopedPointer lastNoteFinished; - - private: - //============================================================================== - void noteAdded (MPENote) override { noteAddedCallCounter++; } - - void notePressureChanged (MPENote) override { notePressureChangedCallCounter++; } - void notePitchbendChanged (MPENote) override { notePitchbendChangedCallCounter++; } - void noteTimbreChanged (MPENote) override { noteTimbreChangedCallCounter++; } - void noteKeyStateChanged (MPENote) override { noteKeyStateChangedCallCounter++; } - - void noteReleased (MPENote finishedNote) override - { - noteReleasedCallCounter++; - lastNoteFinished = new MPENote (finishedNote); - } - }; - - //============================================================================== - void expectNote (MPENote noteToTest, - int noteOnVelocity7Bit, - int pressure7Bit, - int pitchbend14Bit, - int timbre7Bit, - MPENote::KeyState keyState) - { - expect (noteToTest.isValid()); - expectEquals (noteToTest.noteOnVelocity.as7BitInt(), noteOnVelocity7Bit); - expectEquals (noteToTest.pressure.as7BitInt(), pressure7Bit); - expectEquals (noteToTest.pitchbend.as14BitInt(), pitchbend14Bit); - expectEquals (noteToTest.timbre.as7BitInt(),timbre7Bit); - expect (noteToTest.keyState == keyState); - } - - void expectHasFinishedNote (const UnitTestInstrument& test, - int channel, int noteNumber, int noteOffVelocity7Bit) - { - expect (test.lastNoteFinished != nullptr); - expectEquals (int (test.lastNoteFinished->midiChannel), channel); - expectEquals (int (test.lastNoteFinished->initialNote), noteNumber); - expectEquals (test.lastNoteFinished->noteOffVelocity.as7BitInt(), noteOffVelocity7Bit); - expect (test.lastNoteFinished->keyState == MPENote::off); - } - - void expectDoubleWithinRelativeError (double actual, double expected, double maxRelativeError) - { - const double maxAbsoluteError = jmax (1.0, std::fabs (expected)) * maxRelativeError; - expect (std::fabs (expected - actual) < maxAbsoluteError); - } - - //============================================================================== - MPEZoneLayout testLayout; -}; - -static MPEInstrumentTests MPEInstrumentUnitTests; - -#endif // JUCE_UNIT_TESTS - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h b/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h deleted file mode 100644 index b6d05ea84..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEInstrument.h +++ /dev/null @@ -1,378 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/* - 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 JUCE_API Listener - { - public: - /** 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* listenerToAdd) noexcept; - - /** Removes a listener. */ - void removeListener (Listener* 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 channelRange = Range (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 getLegacyModeChannelRange() const noexcept; - - /** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */ - void setLegacyModeChannelRange (Range 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); - -private: - //============================================================================== - CriticalSection lock; - Array notes; - MPEZoneLayout zoneLayout; - ListenerList listeners; - - uint8 lastPressureLowerBitReceivedOnChannel[16]; - uint8 lastTimbreLowerBitReceivedOnChannel[16]; - bool isNoteChannelSustained[16]; - - struct LegacyMode - { - bool isEnabled; - Range 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&); - MPEValue getInitialValueForNewNote (int midiChannel, MPEDimension&) const; - - 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) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp deleted file mode 100644 index ebdc68993..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEMessages.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -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", "MIDI/MPE") {} - - 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 - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h b/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h deleted file mode 100644 index 76b58b201..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEMessages.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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; -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp b/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp deleted file mode 100644 index dad3ab602..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPENote.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -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 < 128; -} - -//============================================================================== -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", "MIDI/MPE") {} - - //============================================================================== - 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 - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPENote.h b/source/modules/juce_audio_basics/mpe/juce_MPENote.h deleted file mode 100644 index 8d549ddd9..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPENote.h +++ /dev/null @@ -1,176 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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; -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp deleted file mode 100644 index 94763f1bc..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -MPESynthesiser::MPESynthesiser() -{ -} - -MPESynthesiser::MPESynthesiser (MPEInstrument* mpeInstrument) : MPESynthesiserBase (mpeInstrument) -{ -} - -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 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& 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& 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); - } -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h deleted file mode 100644 index 408970e6c..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiser.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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& 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& 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 voices; - -private: - //============================================================================== - bool shouldStealVoices; - CriticalSection voicesLock; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiser) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp deleted file mode 100644 index 7759eea5e..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -MPESynthesiserBase::MPESynthesiserBase() - : instrument (new MPEInstrument), - sampleRate (0), - minimumSubBlockSize (32), - subBlockSubdivisionIsStrict (false) -{ - 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 channelRange) -{ - instrument->enableLegacyMode (pitchbendRange, channelRange); -} - -bool MPESynthesiserBase::isLegacyModeEnabled() const noexcept -{ - return instrument->isLegacyModeEnabled(); -} - -Range MPESynthesiserBase::getLegacyModeChannelRange() const noexcept -{ - return instrument->getLegacyModeChannelRange(); -} - -void MPESynthesiserBase::setLegacyModeChannelRange (Range channelRange) -{ - instrument->setLegacyModeChannelRange (channelRange); -} - -int MPESynthesiserBase::getLegacyModePitchbendRange() const noexcept -{ - return instrument->getLegacyModePitchbendRange(); -} - -void MPESynthesiserBase::setLegacyModePitchbendRange (int pitchbendRange) -{ - instrument->setLegacyModePitchbendRange (pitchbendRange); -} - -//============================================================================== -void MPESynthesiserBase::setPressureTrackingMode (TrackingMode modeToUse) -{ - instrument->setPressureTrackingMode (modeToUse); -} - -void MPESynthesiserBase::setPitchbendTrackingMode (TrackingMode modeToUse) -{ - instrument->setPitchbendTrackingMode (modeToUse); -} - -void MPESynthesiserBase::setTimbreTrackingMode (TrackingMode modeToUse) -{ - instrument->setTimbreTrackingMode (modeToUse); -} - -//============================================================================== -void MPESynthesiserBase::handleMidiEvent (const MidiMessage& m) -{ - instrument->processNextMidiEvent (m); -} - -//============================================================================== -template -void MPESynthesiserBase::renderNextBlock (AudioBuffer& 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); - - bool firstEvent = true; - int midiEventPos; - MidiMessage m; - - const ScopedLock sl (noteStateLock); - - 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 < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize)) - { - handleMidiEvent (m); - continue; - } - - firstEvent = false; - - 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 (AudioBuffer&, const MidiBuffer&, int, int); -template void MPESynthesiserBase::renderNextBlock (AudioBuffer&, const MidiBuffer&, int, int); - -//============================================================================== -void MPESynthesiserBase::setCurrentPlaybackSampleRate (const double newRate) -{ - if (sampleRate != newRate) - { - const ScopedLock sl (noteStateLock); - instrument->releaseAllNotes(); - sampleRate = newRate; - } -} - -//============================================================================== -void MPESynthesiserBase::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept -{ - jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 - minimumSubBlockSize = numSamples; - subBlockSubdivisionIsStrict = shouldBeStrict; -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h deleted file mode 100644 index b2b787a58..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserBase.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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 - void renderNextBlock (AudioBuffer& 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. - - If shouldBeStrict is true, the audio sub-blocks will strictly never be smaller than numSamples. - - If shouldBeStrict is false (default), the first audio sub-block in the buffer is allowed - to be smaller, to make sure that the first MIDI event in a buffer will always be sample-accurate - (this can sometimes help to avoid quantisation or phasing issues). - */ - void setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict = false) 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 channelRange = Range (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 getLegacyModeChannelRange() const noexcept; - - /** Re-sets the range of MIDI channels (1-16) to be used for notes when in legacy mode. */ - void setLegacyModeChannelRange (Range 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); - - //============================================================================== - typedef MPEInstrument::TrackingMode TrackingMode; - - /** 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); - -protected: - //============================================================================== - /** Implement this method to render your audio inside. - @see renderNextBlock - */ - virtual void renderNextSubBlock (AudioBuffer& 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& /*outputAudio*/, - int /*startSample*/, - int /*numSamples*/) {} - -protected: - //============================================================================== - /** @internal */ - ScopedPointer instrument; - -private: - //============================================================================== - CriticalSection noteStateLock; - double sampleRate; - int minimumSubBlockSize; - bool subBlockSubdivisionIsStrict; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MPESynthesiserBase) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp deleted file mode 100644 index 6ac688eba..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -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(); -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h b/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h deleted file mode 100644 index 142c96378..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPESynthesiserVoice.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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& 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& /*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) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp deleted file mode 100644 index d9fbd7db3..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEValue.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -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 (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 (normalisedValue), 0.0f, 8192.0f, -1.0f, 0.0f) - : jmap (float (normalisedValue), 8192.0f, 16383.0f, 0.0f, 1.0f); -} - -float MPEValue::asUnsignedFloat() const noexcept -{ - return jmap (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", "MIDI/MPE") {} - - 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 - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEValue.h b/source/modules/juce_audio_basics/mpe/juce_MPEValue.h deleted file mode 100644 index 4bb5afc96..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEValue.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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; -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp deleted file mode 100644 index 5b97f0677..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEZone.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -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 MPEZone::getNoteChannelRange() const noexcept -{ - return Range::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; -} - -//============================================================================== -bool MPEZone::operator== (const MPEZone& other) const noexcept -{ - return masterChannel == other.masterChannel - && numNoteChannels == other.numNoteChannels - && perNotePitchbendRange == other.perNotePitchbendRange - && masterPitchbendRange == other.masterPitchbendRange; -} - -bool MPEZone::operator!= (const MPEZone& other) const noexcept -{ - return ! operator== (other); -} - -//============================================================================== -//============================================================================== -#if JUCE_UNIT_TESTS - -class MPEZoneTests : public UnitTest -{ -public: - MPEZoneTests() : UnitTest ("MPEZone class", "MIDI/MPE") {} - - 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 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 - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZone.h b/source/modules/juce_audio_basics/mpe/juce_MPEZone.h deleted file mode 100644 index 728439826..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEZone.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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 number (in the range 1-16) of this zone. */ - int getMasterChannel() const noexcept; - - /** Returns the number of note channels occupied by this zone. */ - int getNumNoteChannels() const noexcept; - - /* Returns the MIDI channel number (in the range 1-16) of the - lowest-numbered note channel of this zone. - */ - int getFirstNoteChannel() const noexcept; - - /* Returns the MIDI channel number (in the range 1-16) of the - highest-numbered note channel of this zone. - */ - int getLastNoteChannel() const noexcept; - - /** Returns the MIDI channel numbers (in the range 1-16) of the - note channels of this zone as a Range. - */ - Range 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; - - /** @returns true if this zone is equal to the one passed in. */ - bool operator== (const MPEZone& other) const noexcept; - - /** @returns true if this zone is not equal to the one passed in. */ - bool operator!= (const MPEZone& other) const noexcept; - -private: - //============================================================================== - int masterChannel; - int numNoteChannels; - int perNotePitchbendRange; - int masterPitchbendRange; -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp b/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp deleted file mode 100644 index f0d525ad3..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -MPEZoneLayout::MPEZoneLayout() noexcept -{ -} - -MPEZoneLayout::MPEZoneLayout (const MPEZoneLayout& other) - : zones (other.zones) -{ -} - -MPEZoneLayout& MPEZoneLayout::operator= (const MPEZoneLayout& other) -{ - zones = other.zones; - listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this); - return *this; -} - -//============================================================================== -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); - listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this); - 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(); - listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this); -} - -//============================================================================== -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)) - { - if (zone->getPerNotePitchbendRange() != rpn.value) - { - zone->setPerNotePitchbendRange (rpn.value); - listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this); - return; - } - } - - if (MPEZone* zone = getZoneByMasterChannel (rpn.channel)) - { - if (zone->getMasterPitchbendRange() != rpn.value) - { - zone->setMasterPitchbendRange (rpn.value); - listeners.call (&MPEZoneLayout::Listener::zoneLayoutChanged, *this); - return; - } - } -} - -//============================================================================== -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; -} - -//============================================================================== -void MPEZoneLayout::addListener (Listener* const listenerToAdd) noexcept -{ - listeners.add (listenerToAdd); -} - -void MPEZoneLayout::removeListener (Listener* const listenerToRemove) noexcept -{ - listeners.remove (listenerToRemove); -} - -//============================================================================== -//============================================================================== -#if JUCE_UNIT_TESTS - - -class MPEZoneLayoutTests : public UnitTest -{ -public: - MPEZoneLayoutTests() : UnitTest ("MPEZoneLayout class", "MIDI/MPE") {} - - 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 - -} // namespace juce diff --git a/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h b/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h deleted file mode 100644 index 5b0337c11..000000000 --- a/source/modules/juce_audio_basics/mpe/juce_MPEZoneLayout.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - 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; - - /** Copy constuctor. - This will not copy the listeners registered to the MPEZoneLayout. - */ - MPEZoneLayout (const MPEZoneLayout& other); - - /** Copy assignment operator. - This will not copy the listeners registered to the MPEZoneLayout. - */ - MPEZoneLayout& operator= (const MPEZoneLayout& other); - - /** 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. Zones are sorted by insertion order (most recently added - zone last). - */ - 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; - - //============================================================================== - /** Listener class. Derive from this class to allow your class to be - notified about changes to the zone layout. - */ - class Listener - { - public: - /** Destructor. */ - virtual ~Listener() {} - - /** Implement this callback to be notified about any changes to this - MPEZoneLayout. Will be called whenever a zone is added, zones are - removed, or any zone's master or note pitchbend ranges change. - */ - virtual void zoneLayoutChanged (const MPEZoneLayout& layout) = 0; - }; - - //============================================================================== - /** Adds a listener. */ - void addListener (Listener* const listenerToAdd) noexcept; - - /** Removes a listener. */ - void removeListener (Listener* const listenerToRemove) noexcept; - -private: - //============================================================================== - Array zones; - MidiRPNDetector rpnDetector; - ListenerList listeners; - - void processRpnMessage (MidiRPNMessage); - void processZoneLayoutRpnMessage (MidiRPNMessage); - void processPitchbendRangeRpnMessage (MidiRPNMessage); -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h b/source/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h deleted file mode 100644 index 0aaf6f666..000000000 --- a/source/modules/juce_audio_basics/native/juce_mac_CoreAudioLayouts.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -#if JUCE_MAC || JUCE_IOS - -struct CoreAudioLayouts -{ - //============================================================================== - /** Convert CoreAudio's native AudioChannelLayout to JUCE's AudioChannelSet. - - Note that this method cannot preserve the order of channels. - */ - static AudioChannelSet fromCoreAudio (const AudioChannelLayout& layout) - { - return AudioChannelSet::channelSetWithChannels (getCoreAudioLayoutChannels (layout)); - } - - /** Convert CoreAudio's native AudioChannelLayoutTag to JUCE's AudioChannelSet. - - Note that this method cannot preserve the order of channels. - */ - static AudioChannelSet fromCoreAudio (AudioChannelLayoutTag layoutTag) - { - return AudioChannelSet::channelSetWithChannels (getSpeakerLayoutForCoreAudioTag (layoutTag)); - } - - /** Convert JUCE's AudioChannelSet to CoreAudio's AudioChannelLayoutTag. - - Note that this method cannot preserve the order of channels. - */ - static AudioChannelLayoutTag toCoreAudio (const AudioChannelSet& set) - { - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) - { - AudioChannelSet caSet; - - for (int i = 0; i < numElementsInArray (tbl->channelTypes) - && tbl->channelTypes[i] != AudioChannelSet::unknown; ++i) - caSet.addChannel (tbl->channelTypes[i]); - - if (caSet == set) - return tbl->tag; - } - - return kAudioChannelLayoutTag_DiscreteInOrder | static_cast (set.size()); - } - - static const Array& getKnownCoreAudioTags() - { - static Array tags (createKnownCoreAudioTags()); - return tags; - } - - //============================================================================== - /** Convert CoreAudio's native AudioChannelLayout to an array of JUCE ChannelTypes. */ - static Array getCoreAudioLayoutChannels (const AudioChannelLayout& layout) - { - switch (layout.mChannelLayoutTag & 0xffff0000) - { - case kAudioChannelLayoutTag_UseChannelBitmap: - return AudioChannelSet::fromWaveChannelMask (static_cast (layout.mChannelBitmap)).getChannelTypes(); - case kAudioChannelLayoutTag_UseChannelDescriptions: - { - Array channels; - - for (UInt32 i = 0; i < layout.mNumberChannelDescriptions; ++i) - channels.addIfNotAlreadyThere (getChannelTypeFromAudioChannelLabel (layout.mChannelDescriptions[i].mChannelLabel)); - - // different speaker mappings may point to the same JUCE speaker so fill up - // this array with discrete channels - for (int j = 0; channels.size() < static_cast (layout.mNumberChannelDescriptions); ++j) - channels.addIfNotAlreadyThere (static_cast (AudioChannelSet::discreteChannel0 + j)); - - return channels; - } - case kAudioChannelLayoutTag_DiscreteInOrder: - return AudioChannelSet::discreteChannels (static_cast (layout.mChannelLayoutTag) & 0xffff).getChannelTypes(); - default: - break; - } - - return getSpeakerLayoutForCoreAudioTag (layout.mChannelLayoutTag); - } - - static Array getSpeakerLayoutForCoreAudioTag (AudioChannelLayoutTag tag) - { - // You need to specify the full AudioChannelLayout when using - // the UseChannelBitmap and UseChannelDescriptions layout tag - jassert (tag != kAudioChannelLayoutTag_UseChannelBitmap && tag != kAudioChannelLayoutTag_UseChannelDescriptions); - - Array speakers; - - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) - { - if (tag == tbl->tag) - { - for (int i = 0; i < numElementsInArray (tbl->channelTypes) - && tbl->channelTypes[i] != AudioChannelSet::unknown; ++i) - speakers.add (tbl->channelTypes[i]); - - return speakers; - } - } - - auto numChannels = tag & 0xffff; - for (UInt32 i = 0; i < numChannels; ++i) - speakers.add (static_cast (AudioChannelSet::discreteChannel0 + i)); - - return speakers; - } - -private: - //============================================================================== - struct LayoutTagSpeakerList - { - AudioChannelLayoutTag tag; - AudioChannelSet::ChannelType channelTypes[16]; - }; - - static Array createKnownCoreAudioTags() - { - Array tags; - - for (auto* tbl = SpeakerLayoutTable::get(); tbl->tag != 0; ++tbl) - tags.addIfNotAlreadyThere (tbl->tag); - - return tags; - } - - //============================================================================== - // This list has been derived from https://pastebin.com/24dQ4BPJ - // Apple channel labels have been replaced by JUCE channel names - // This means that some layouts will be identical in JUCE but not in CoreAudio - - // In Apple's official definition the following tags exist with the same speaker layout and order - // even when *not* represented in JUCE channels - // kAudioChannelLayoutTag_Binaural = kAudioChannelLayoutTag_Stereo - // kAudioChannelLayoutTag_MPEG_5_0_B = kAudioChannelLayoutTag_Pentagonal - // kAudioChannelLayoutTag_ITU_2_2 = kAudioChannelLayoutTag_Quadraphonic - // kAudioChannelLayoutTag_AudioUnit_6_0 = kAudioChannelLayoutTag_Hexagonal - struct SpeakerLayoutTable : AudioChannelSet // save us some typing - { - static LayoutTagSpeakerList* get() noexcept - { - static LayoutTagSpeakerList tbl[] = { - // list layouts for which there is a corresponding named AudioChannelSet first - { kAudioChannelLayoutTag_Mono, { centre } }, - { kAudioChannelLayoutTag_Stereo, { left, right } }, - { kAudioChannelLayoutTag_MPEG_3_0_A, { left, right, centre } }, - { kAudioChannelLayoutTag_ITU_2_1, { left, right, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_4_0_A, { left, right, centre, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_A, { left, right, centre, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_1_A, { left, right, centre, LFE, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_AudioUnit_6_0, { left, right, leftSurround, rightSurround, centre, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_6_1_A, { left, right, centre, LFE, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_DTS_6_0_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_6_1_A, { leftSurroundSide, rightSurroundSide, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_AudioUnit_7_0, { left, right, leftSurroundSide, rightSurroundSide, centre, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_AudioUnit_7_0_Front, { left, right, leftSurround, rightSurround, centre, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_MPEG_7_1_C, { left, right, centre, LFE, leftSurroundSide, rightSurroundSide, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_MPEG_7_1_A, { left, right, centre, LFE, leftSurround, rightSurround, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_Ambisonic_B_Format, { ambisonicW, ambisonicX, ambisonicY, ambisonicZ } }, - { kAudioChannelLayoutTag_Quadraphonic, { left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_Pentagonal, { left, right, leftSurroundRear, rightSurroundRear, centre } }, - { kAudioChannelLayoutTag_Hexagonal, { left, right, leftSurroundRear, rightSurroundRear, centre, centreSurround } }, - { kAudioChannelLayoutTag_Octagonal, { left, right, leftSurround, rightSurround, centre, centreSurround, wideLeft, wideRight } }, - - // more uncommon layouts - { kAudioChannelLayoutTag_StereoHeadphones, { left, right } }, - { kAudioChannelLayoutTag_MatrixStereo, { left, right } }, - { kAudioChannelLayoutTag_MidSide, { centre, discreteChannel0 } }, - { kAudioChannelLayoutTag_XY, { ambisonicX, ambisonicY } }, - { kAudioChannelLayoutTag_Binaural, { left, right } }, - { kAudioChannelLayoutTag_Cube, { left, right, leftSurround, rightSurround, topFrontLeft, topFrontRight, topRearLeft, topRearRight } }, - { kAudioChannelLayoutTag_MPEG_3_0_B, { centre, left, right } }, - { kAudioChannelLayoutTag_MPEG_4_0_B, { centre, left, right, centreSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_B, { left, right, leftSurround, rightSurround, centre } }, - { kAudioChannelLayoutTag_MPEG_5_0_C, { left, centre, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_0_D, { centre, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_MPEG_5_1_B, { left, right, leftSurround, rightSurround, centre, LFE } }, - { kAudioChannelLayoutTag_MPEG_5_1_C, { left, centre, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_MPEG_5_1_D, { centre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_MPEG_7_1_B, { centre, leftCentre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_Emagic_Default_7_1, { left, right, leftSurround, rightSurround, centre, LFE, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_SMPTE_DTV, { left, right, centre, LFE, leftSurround, rightSurround, discreteChannel0 /* leftMatrixTotal */, (ChannelType) (discreteChannel0 + 1) /* rightMatrixTotal */} }, - { kAudioChannelLayoutTag_ITU_2_2, { left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DVD_4, { left, right, LFE } }, - { kAudioChannelLayoutTag_DVD_5, { left, right, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DVD_6, { left, right, LFE, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DVD_10, { left, right, centre, LFE } }, - { kAudioChannelLayoutTag_DVD_11, { left, right, centre, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DVD_18, { left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_AAC_6_0, { centre, left, right, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_AAC_6_1, { centre, left, right, leftSurround, rightSurround, centreSurround, LFE } }, - { kAudioChannelLayoutTag_AAC_7_0, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_AAC_7_1_B, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_AAC_7_1_C, { centre, left, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, - { kAudioChannelLayoutTag_AAC_Octagonal, { centre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, centreSurround } }, - { kAudioChannelLayoutTag_TMH_10_2_std, { left, right, centre, topFrontCentre, leftSurroundSide, rightSurroundSide, leftSurround, rightSurround, topFrontLeft, topFrontRight, wideLeft, wideRight, topRearCentre, centreSurround, LFE, LFE2 } }, - { kAudioChannelLayoutTag_AC3_1_0_1, { centre, LFE } }, - { kAudioChannelLayoutTag_AC3_3_0, { left, centre, right } }, - { kAudioChannelLayoutTag_AC3_3_1, { left, centre, right, centreSurround } }, - { kAudioChannelLayoutTag_AC3_3_0_1, { left, centre, right, LFE } }, - { kAudioChannelLayoutTag_AC3_2_1_1, { left, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_AC3_3_1_1, { left, centre, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_EAC_6_0_A, { left, centre, right, leftSurround, rightSurround, centreSurround } }, - { kAudioChannelLayoutTag_EAC_7_0_A, { left, centre, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_EAC3_6_1_A, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_EAC3_6_1_B, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_EAC3_6_1_C, { left, centre, right, leftSurround, rightSurround, LFE, topFrontCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_A, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_EAC3_7_1_B, { left, centre, right, leftSurround, rightSurround, LFE, leftCentre, rightCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_C, { left, centre, right, leftSurround, rightSurround, LFE, leftSurroundSide, rightSurroundSide } }, - { kAudioChannelLayoutTag_EAC3_7_1_D, { left, centre, right, leftSurround, rightSurround, LFE, wideLeft, wideRight } }, - { kAudioChannelLayoutTag_EAC3_7_1_E, { left, centre, right, leftSurround, rightSurround, LFE, topFrontLeft, topFrontRight } }, - { kAudioChannelLayoutTag_EAC3_7_1_F, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topMiddle } }, - { kAudioChannelLayoutTag_EAC3_7_1_G, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, - { kAudioChannelLayoutTag_EAC3_7_1_H, { left, centre, right, leftSurround, rightSurround, LFE, centreSurround, topFrontCentre } }, - { kAudioChannelLayoutTag_DTS_3_1, { centre, left, right, LFE } }, - { kAudioChannelLayoutTag_DTS_4_1, { centre, left, right, centreSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_6_0_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround } }, - { kAudioChannelLayoutTag_DTS_6_0_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_DTS_6_1_B, { centre, left, right, leftSurroundRear, rightSurroundRear, centreSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_6_1_C, { centre, centreSurround, left, right, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_DTS_6_1_D, { centre, left, right, leftSurround, rightSurround, LFE, centreSurround } }, - { kAudioChannelLayoutTag_DTS_7_0, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_7_1, { leftCentre, centre, rightCentre, left, right, leftSurround, rightSurround, LFE } }, - { kAudioChannelLayoutTag_DTS_8_0_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear } }, - { kAudioChannelLayoutTag_DTS_8_0_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround } }, - { kAudioChannelLayoutTag_DTS_8_1_A, { leftCentre, rightCentre, left, right, leftSurround, rightSurround, leftSurroundRear, rightSurroundRear, LFE } }, - { kAudioChannelLayoutTag_DTS_8_1_B, { leftCentre, centre, rightCentre, left, right, leftSurround, centreSurround, rightSurround, LFE } }, - { 0, {} } - }; - - return tbl; - } - }; - - //============================================================================== - static AudioChannelSet::ChannelType getChannelTypeFromAudioChannelLabel (AudioChannelLabel label) noexcept - { - if (label >= kAudioChannelLabel_Discrete_0 && label <= kAudioChannelLabel_Discrete_65535) - { - const unsigned int discreteChannelNum = label - kAudioChannelLabel_Discrete_0; - return static_cast (AudioChannelSet::discreteChannel0 + discreteChannelNum); - } - - switch (label) - { - case kAudioChannelLabel_Center: - case kAudioChannelLabel_Mono: return AudioChannelSet::centre; - case kAudioChannelLabel_Left: - case kAudioChannelLabel_HeadphonesLeft: return AudioChannelSet::left; - case kAudioChannelLabel_Right: - case kAudioChannelLabel_HeadphonesRight: return AudioChannelSet::right; - case kAudioChannelLabel_LFEScreen: return AudioChannelSet::LFE; - case kAudioChannelLabel_LeftSurround: return AudioChannelSet::leftSurround; - case kAudioChannelLabel_RightSurround: return AudioChannelSet::rightSurround; - case kAudioChannelLabel_LeftCenter: return AudioChannelSet::leftCentre; - case kAudioChannelLabel_RightCenter: return AudioChannelSet::rightCentre; - case kAudioChannelLabel_CenterSurround: return AudioChannelSet::surround; - case kAudioChannelLabel_LeftSurroundDirect: return AudioChannelSet::leftSurroundSide; - case kAudioChannelLabel_RightSurroundDirect: return AudioChannelSet::rightSurroundSide; - case kAudioChannelLabel_TopCenterSurround: return AudioChannelSet::topMiddle; - case kAudioChannelLabel_VerticalHeightLeft: return AudioChannelSet::topFrontLeft; - case kAudioChannelLabel_VerticalHeightRight: return AudioChannelSet::topFrontRight; - case kAudioChannelLabel_VerticalHeightCenter: return AudioChannelSet::topFrontCentre; - case kAudioChannelLabel_TopBackLeft: return AudioChannelSet::topRearLeft; - case kAudioChannelLabel_RearSurroundLeft: return AudioChannelSet::leftSurroundRear; - case kAudioChannelLabel_TopBackRight: return AudioChannelSet::topRearRight; - case kAudioChannelLabel_RearSurroundRight: return AudioChannelSet::rightSurroundRear; - case kAudioChannelLabel_TopBackCenter: return AudioChannelSet::topRearCentre; - case kAudioChannelLabel_LFE2: return AudioChannelSet::LFE2; - case kAudioChannelLabel_LeftWide: return AudioChannelSet::wideLeft; - case kAudioChannelLabel_RightWide: return AudioChannelSet::wideRight; - case kAudioChannelLabel_Ambisonic_W: return AudioChannelSet::ambisonicW; - case kAudioChannelLabel_Ambisonic_X: return AudioChannelSet::ambisonicX; - case kAudioChannelLabel_Ambisonic_Y: return AudioChannelSet::ambisonicY; - case kAudioChannelLabel_Ambisonic_Z: return AudioChannelSet::ambisonicZ; - default: return AudioChannelSet::unknown; - } - } -}; - -#endif - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_AudioSource.h b/source/modules/juce_audio_basics/sources/juce_AudioSource.h deleted file mode 100644 index 6e5ddba5d..000000000 --- a/source/modules/juce_audio_basics/sources/juce_AudioSource.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Used by AudioSource::getNextAudioBlock(). -*/ -struct JUCE_API AudioSourceChannelInfo -{ - /** Creates an uninitialised AudioSourceChannelInfo. */ - AudioSourceChannelInfo() noexcept - { - } - - /** Creates an AudioSourceChannelInfo. */ - AudioSourceChannelInfo (AudioSampleBuffer* bufferToUse, - int startSampleOffset, int numSamplesToUse) noexcept - : buffer (bufferToUse), - startSample (startSampleOffset), - numSamples (numSamplesToUse) - { - } - - /** Creates an AudioSourceChannelInfo that uses the whole of a buffer. - Note that the buffer provided must not be deleted while the - AudioSourceChannelInfo is still using it. - */ - explicit AudioSourceChannelInfo (AudioSampleBuffer& bufferToUse) noexcept - : buffer (&bufferToUse), - startSample (0), - numSamples (bufferToUse.getNumSamples()) - { - } - - /** The destination buffer to fill with audio data. - - When the AudioSource::getNextAudioBlock() method is called, the active section - of this buffer should be filled with whatever output the source produces. - - Only the samples specified by the startSample and numSamples members of this structure - should be affected by the call. - - The contents of the buffer when it is passed to the AudioSource::getNextAudioBlock() - method can be treated as the input if the source is performing some kind of filter operation, - but should be cleared if this is not the case - the clearActiveBufferRegion() is - a handy way of doing this. - - The number of channels in the buffer could be anything, so the AudioSource - must cope with this in whatever way is appropriate for its function. - */ - AudioSampleBuffer* buffer; - - /** The first sample in the buffer from which the callback is expected - to write data. */ - int startSample; - - /** The number of samples in the buffer which the callback is expected to - fill with data. */ - int numSamples; - - /** Convenient method to clear the buffer if the source is not producing any data. */ - void clearActiveBufferRegion() const - { - if (buffer != nullptr) - buffer->clear (startSample, numSamples); - } -}; - - -//============================================================================== -/** - Base class for objects that can produce a continuous stream of audio. - - An AudioSource has two states: 'prepared' and 'unprepared'. - - When a source needs to be played, it is first put into a 'prepared' state by a call to - prepareToPlay(), and then repeated calls will be made to its getNextAudioBlock() method to - process the audio data. - - Once playback has finished, the releaseResources() method is called to put the stream - back into an 'unprepared' state. - - @see AudioFormatReaderSource, ResamplingAudioSource -*/ -class JUCE_API AudioSource -{ -protected: - //============================================================================== - /** Creates an AudioSource. */ - AudioSource() noexcept {} - -public: - /** Destructor. */ - virtual ~AudioSource() {} - - //============================================================================== - /** Tells the source to prepare for playing. - - An AudioSource has two states: prepared and unprepared. - - The prepareToPlay() method is guaranteed to be called at least once on an 'unpreprared' - source to put it into a 'prepared' state before any calls will be made to getNextAudioBlock(). - This callback allows the source to initialise any resources it might need when playing. - - Once playback has finished, the releaseResources() method is called to put the stream - back into an 'unprepared' state. - - Note that this method could be called more than once in succession without - a matching call to releaseResources(), so make sure your code is robust and - can handle that kind of situation. - - @param samplesPerBlockExpected the number of samples that the source - will be expected to supply each time its - getNextAudioBlock() method is called. This - number may vary slightly, because it will be dependent - on audio hardware callbacks, and these aren't - guaranteed to always use a constant block size, so - the source should be able to cope with small variations. - @param sampleRate the sample rate that the output will be used at - this - is needed by sources such as tone generators. - @see releaseResources, getNextAudioBlock - */ - virtual void prepareToPlay (int samplesPerBlockExpected, - double sampleRate) = 0; - - /** Allows the source to release anything it no longer needs after playback has stopped. - - This will be called when the source is no longer going to have its getNextAudioBlock() - method called, so it should release any spare memory, etc. that it might have - allocated during the prepareToPlay() call. - - Note that there's no guarantee that prepareToPlay() will actually have been called before - releaseResources(), and it may be called more than once in succession, so make sure your - code is robust and doesn't make any assumptions about when it will be called. - - @see prepareToPlay, getNextAudioBlock - */ - virtual void releaseResources() = 0; - - /** Called repeatedly to fetch subsequent blocks of audio data. - - After calling the prepareToPlay() method, this callback will be made each - time the audio playback hardware (or whatever other destination the audio - data is going to) needs another block of data. - - It will generally be called on a high-priority system thread, or possibly even - an interrupt, so be careful not to do too much work here, as that will cause - audio glitches! - - @see AudioSourceChannelInfo, prepareToPlay, releaseResources - */ - virtual void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) = 0; -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp deleted file mode 100644 index ae33bea0d..000000000 --- a/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s, - TimeSliceThread& thread, - const bool deleteSourceWhenDeleted, - const int bufferSizeSamples, - const int numChannels, - bool prefillBufferOnPrepareToPlay) - : source (s, deleteSourceWhenDeleted), - backgroundThread (thread), - numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)), - numberOfChannels (numChannels), - bufferValidStart (0), - bufferValidEnd (0), - nextPlayPos (0), - sampleRate (0), - wasSourceLooping (false), - isPrepared (false), - prefillBuffer (prefillBufferOnPrepareToPlay) -{ - jassert (source != nullptr); - - jassert (numberOfSamplesToBuffer > 1024); // not much point using this class if you're - // not using a larger buffer.. -} - -BufferingAudioSource::~BufferingAudioSource() -{ - releaseResources(); -} - -//============================================================================== -void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate) -{ - const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer); - - if (newSampleRate != sampleRate - || bufferSizeNeeded != buffer.getNumSamples() - || ! isPrepared) - { - backgroundThread.removeTimeSliceClient (this); - - isPrepared = true; - sampleRate = newSampleRate; - - source->prepareToPlay (samplesPerBlockExpected, newSampleRate); - - buffer.setSize (numberOfChannels, bufferSizeNeeded); - buffer.clear(); - - bufferValidStart = 0; - bufferValidEnd = 0; - - backgroundThread.addTimeSliceClient (this); - - do - { - backgroundThread.moveToFrontOfQueue (this); - Thread::sleep (5); - } - while (prefillBuffer - && (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2))); - } -} - -void BufferingAudioSource::releaseResources() -{ - isPrepared = false; - backgroundThread.removeTimeSliceClient (this); - - buffer.setSize (numberOfChannels, 0); - - // MSVC2015 seems to need this if statement to not generate a warning during linking. - // As source is set in the constructor, there is no way that source could - // ever equal this, but it seems to make MSVC2015 happy. - if (source != this) - source->releaseResources(); -} - -void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) -{ - const ScopedLock sl (bufferStartPosLock); - - const int validStart = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos); - const int validEnd = (int) (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos); - - if (validStart == validEnd) - { - // total cache miss - info.clearActiveBufferRegion(); - } - else - { - if (validStart > 0) - info.buffer->clear (info.startSample, validStart); // partial cache miss at start - - if (validEnd < info.numSamples) - info.buffer->clear (info.startSample + validEnd, - info.numSamples - validEnd); // partial cache miss at end - - if (validStart < validEnd) - { - for (int chan = jmin (numberOfChannels, info.buffer->getNumChannels()); --chan >= 0;) - { - jassert (buffer.getNumSamples() > 0); - const int startBufferIndex = (int) ((validStart + nextPlayPos) % buffer.getNumSamples()); - const int endBufferIndex = (int) ((validEnd + nextPlayPos) % buffer.getNumSamples()); - - if (startBufferIndex < endBufferIndex) - { - info.buffer->copyFrom (chan, info.startSample + validStart, - buffer, - chan, startBufferIndex, - validEnd - validStart); - } - else - { - const int initialSize = buffer.getNumSamples() - startBufferIndex; - - info.buffer->copyFrom (chan, info.startSample + validStart, - buffer, - chan, startBufferIndex, - initialSize); - - info.buffer->copyFrom (chan, info.startSample + validStart + initialSize, - buffer, - chan, 0, - (validEnd - validStart) - initialSize); - } - } - } - - nextPlayPos += info.numSamples; - } -} - -bool BufferingAudioSource::waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout) -{ - if (!source || source->getTotalLength() <= 0) - return false; - - if (nextPlayPos + info.numSamples < 0) - return true; - - if (! isLooping() && nextPlayPos > getTotalLength()) - return true; - - uint32 now = Time::getMillisecondCounter(); - const uint32 startTime = now; - - uint32 elapsed = (now >= startTime ? now - startTime - : (std::numeric_limits::max() - startTime) + now); - - while (elapsed <= timeout) - { - { - const ScopedLock sl (bufferStartPosLock); - - const int validStart = static_cast (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos) - nextPlayPos); - const int validEnd = static_cast (jlimit (bufferValidStart, bufferValidEnd, nextPlayPos + info.numSamples) - nextPlayPos); - - if (validStart <= 0 && validStart < validEnd && validEnd >= info.numSamples) - return true; - } - - - - if (elapsed < timeout && (! bufferReadyEvent.wait (static_cast (timeout - elapsed)))) - return false; - - now = Time::getMillisecondCounter(); - elapsed = (now >= startTime ? now - startTime - : (std::numeric_limits::max() - startTime) + now); - } - - return false; -} - -int64 BufferingAudioSource::getNextReadPosition() const -{ - jassert (source->getTotalLength() > 0); - return (source->isLooping() && nextPlayPos > 0) - ? nextPlayPos % source->getTotalLength() - : nextPlayPos; -} - -void BufferingAudioSource::setNextReadPosition (int64 newPosition) -{ - const ScopedLock sl (bufferStartPosLock); - - nextPlayPos = newPosition; - backgroundThread.moveToFrontOfQueue (this); -} - -bool BufferingAudioSource::readNextBufferChunk() -{ - int64 newBVS, newBVE, sectionToReadStart, sectionToReadEnd; - - { - const ScopedLock sl (bufferStartPosLock); - - if (wasSourceLooping != isLooping()) - { - wasSourceLooping = isLooping(); - bufferValidStart = 0; - bufferValidEnd = 0; - } - - newBVS = jmax ((int64) 0, nextPlayPos); - newBVE = newBVS + buffer.getNumSamples() - 4; - sectionToReadStart = 0; - sectionToReadEnd = 0; - - const int maxChunkSize = 2048; - - if (newBVS < bufferValidStart || newBVS >= bufferValidEnd) - { - newBVE = jmin (newBVE, newBVS + maxChunkSize); - - sectionToReadStart = newBVS; - sectionToReadEnd = newBVE; - - bufferValidStart = 0; - bufferValidEnd = 0; - } - else if (std::abs ((int) (newBVS - bufferValidStart)) > 512 - || std::abs ((int) (newBVE - bufferValidEnd)) > 512) - { - newBVE = jmin (newBVE, bufferValidEnd + maxChunkSize); - - sectionToReadStart = bufferValidEnd; - sectionToReadEnd = newBVE; - - bufferValidStart = newBVS; - bufferValidEnd = jmin (bufferValidEnd, newBVE); - } - } - - if (sectionToReadStart == sectionToReadEnd) - return false; - - jassert (buffer.getNumSamples() > 0); - const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples()); - const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples()); - - if (bufferIndexStart < bufferIndexEnd) - { - readBufferSection (sectionToReadStart, - (int) (sectionToReadEnd - sectionToReadStart), - bufferIndexStart); - } - else - { - const int initialSize = buffer.getNumSamples() - bufferIndexStart; - - readBufferSection (sectionToReadStart, - initialSize, - bufferIndexStart); - - readBufferSection (sectionToReadStart + initialSize, - (int) (sectionToReadEnd - sectionToReadStart) - initialSize, - 0); - } - - { - const ScopedLock sl2 (bufferStartPosLock); - - bufferValidStart = newBVS; - bufferValidEnd = newBVE; - } - - bufferReadyEvent.signal(); - - return true; -} - -void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset) -{ - if (source->getNextReadPosition() != start) - source->setNextReadPosition (start); - - AudioSourceChannelInfo info (&buffer, bufferOffset, length); - source->getNextAudioBlock (info); -} - -int BufferingAudioSource::useTimeSlice() -{ - return readNextBufferChunk() ? 1 : 100; -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h b/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h deleted file mode 100644 index 5624f5ceb..000000000 --- a/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - An AudioSource which takes another source as input, and buffers it using a thread. - - Create this as a wrapper around another thread, and it will read-ahead with - a background thread to smooth out playback. You can either create one of these - directly, or use it indirectly using an AudioTransportSource. - - @see PositionableAudioSource, AudioTransportSource -*/ -class JUCE_API BufferingAudioSource : public PositionableAudioSource, - private TimeSliceClient -{ -public: - //============================================================================== - /** Creates a BufferingAudioSource. - - @param source the input source to read from - @param backgroundThread a background thread that will be used for the - background read-ahead. This object must not be deleted - until after any BufferingAudioSources that are using it - have been deleted! - @param deleteSourceWhenDeleted if true, then the input source object will - be deleted when this object is deleted - @param numberOfSamplesToBuffer the size of buffer to use for reading ahead - @param numberOfChannels the number of channels that will be played - @param prefillBufferOnPrepareToPlay if true, then calling prepareToPlay on this object will - block until the buffer has been filled - */ - BufferingAudioSource (PositionableAudioSource* source, - TimeSliceThread& backgroundThread, - bool deleteSourceWhenDeleted, - int numberOfSamplesToBuffer, - int numberOfChannels = 2, - bool prefillBufferOnPrepareToPlay = true); - - /** Destructor. - - The input source may be deleted depending on whether the deleteSourceWhenDeleted - flag was set in the constructor. - */ - ~BufferingAudioSource(); - - //============================================================================== - /** Implementation of the AudioSource method. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - - /** Implementation of the AudioSource method. */ - void releaseResources() override; - - /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo&) override; - - //============================================================================== - /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int64 newPosition) override; - - /** Implements the PositionableAudioSource method. */ - int64 getNextReadPosition() const override; - - /** Implements the PositionableAudioSource method. */ - int64 getTotalLength() const override { return source->getTotalLength(); } - - /** Implements the PositionableAudioSource method. */ - bool isLooping() const override { return source->isLooping(); } - - /** A useful function to block until the next the buffer info can be filled. - - This is useful for offline rendering. - */ - bool waitForNextAudioBlockReady (const AudioSourceChannelInfo& info, const uint32 timeout); - -private: - //============================================================================== - OptionalScopedPointer source; - TimeSliceThread& backgroundThread; - int numberOfSamplesToBuffer, numberOfChannels; - AudioSampleBuffer buffer; - CriticalSection bufferStartPosLock; - WaitableEvent bufferReadyEvent; - int64 volatile bufferValidStart, bufferValidEnd, nextPlayPos; - double volatile sampleRate; - bool wasSourceLooping, isPrepared, prefillBuffer; - - bool readNextBufferChunk(); - void readBufferSection (int64 start, int length, int bufferOffset); - int useTimeSlice() override; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp deleted file mode 100644 index 3cec22131..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -ChannelRemappingAudioSource::ChannelRemappingAudioSource (AudioSource* const source_, - const bool deleteSourceWhenDeleted) - : source (source_, deleteSourceWhenDeleted), - requiredNumberOfChannels (2) -{ - remappedInfo.buffer = &buffer; - remappedInfo.startSample = 0; -} - -ChannelRemappingAudioSource::~ChannelRemappingAudioSource() {} - -//============================================================================== -void ChannelRemappingAudioSource::setNumberOfChannelsToProduce (const int requiredNumberOfChannels_) -{ - const ScopedLock sl (lock); - requiredNumberOfChannels = requiredNumberOfChannels_; -} - -void ChannelRemappingAudioSource::clearAllMappings() -{ - const ScopedLock sl (lock); - - remappedInputs.clear(); - remappedOutputs.clear(); -} - -void ChannelRemappingAudioSource::setInputChannelMapping (const int destIndex, const int sourceIndex) -{ - const ScopedLock sl (lock); - - while (remappedInputs.size() < destIndex) - remappedInputs.add (-1); - - remappedInputs.set (destIndex, sourceIndex); -} - -void ChannelRemappingAudioSource::setOutputChannelMapping (const int sourceIndex, const int destIndex) -{ - const ScopedLock sl (lock); - - while (remappedOutputs.size() < sourceIndex) - remappedOutputs.add (-1); - - remappedOutputs.set (sourceIndex, destIndex); -} - -int ChannelRemappingAudioSource::getRemappedInputChannel (const int inputChannelIndex) const -{ - const ScopedLock sl (lock); - - if (inputChannelIndex >= 0 && inputChannelIndex < remappedInputs.size()) - return remappedInputs.getUnchecked (inputChannelIndex); - - return -1; -} - -int ChannelRemappingAudioSource::getRemappedOutputChannel (const int outputChannelIndex) const -{ - const ScopedLock sl (lock); - - if (outputChannelIndex >= 0 && outputChannelIndex < remappedOutputs.size()) - return remappedOutputs .getUnchecked (outputChannelIndex); - - return -1; -} - -//============================================================================== -void ChannelRemappingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) -{ - source->prepareToPlay (samplesPerBlockExpected, sampleRate); -} - -void ChannelRemappingAudioSource::releaseResources() -{ - source->releaseResources(); -} - -void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) -{ - const ScopedLock sl (lock); - - buffer.setSize (requiredNumberOfChannels, bufferToFill.numSamples, false, false, true); - - const int numChans = bufferToFill.buffer->getNumChannels(); - - for (int i = 0; i < buffer.getNumChannels(); ++i) - { - const int remappedChan = getRemappedInputChannel (i); - - if (remappedChan >= 0 && remappedChan < numChans) - { - buffer.copyFrom (i, 0, *bufferToFill.buffer, - remappedChan, - bufferToFill.startSample, - bufferToFill.numSamples); - } - else - { - buffer.clear (i, 0, bufferToFill.numSamples); - } - } - - remappedInfo.numSamples = bufferToFill.numSamples; - - source->getNextAudioBlock (remappedInfo); - - bufferToFill.clearActiveBufferRegion(); - - for (int i = 0; i < requiredNumberOfChannels; ++i) - { - const int remappedChan = getRemappedOutputChannel (i); - - if (remappedChan >= 0 && remappedChan < numChans) - { - bufferToFill.buffer->addFrom (remappedChan, bufferToFill.startSample, - buffer, i, 0, bufferToFill.numSamples); - - } - } -} - -//============================================================================== -XmlElement* ChannelRemappingAudioSource::createXml() const -{ - XmlElement* e = new XmlElement ("MAPPINGS"); - String ins, outs; - - const ScopedLock sl (lock); - - for (int i = 0; i < remappedInputs.size(); ++i) - ins << remappedInputs.getUnchecked(i) << ' '; - - for (int i = 0; i < remappedOutputs.size(); ++i) - outs << remappedOutputs.getUnchecked(i) << ' '; - - e->setAttribute ("inputs", ins.trimEnd()); - e->setAttribute ("outputs", outs.trimEnd()); - - return e; -} - -void ChannelRemappingAudioSource::restoreFromXml (const XmlElement& e) -{ - if (e.hasTagName ("MAPPINGS")) - { - const ScopedLock sl (lock); - - clearAllMappings(); - - StringArray ins, outs; - ins.addTokens (e.getStringAttribute ("inputs"), false); - outs.addTokens (e.getStringAttribute ("outputs"), false); - - for (int i = 0; i < ins.size(); ++i) - remappedInputs.add (ins[i].getIntValue()); - - for (int i = 0; i < outs.size(); ++i) - remappedOutputs.add (outs[i].getIntValue()); - } -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h b/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h deleted file mode 100644 index b5a5cf058..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - An AudioSource that takes the audio from another source, and re-maps its - input and output channels to a different arrangement. - - You can use this to increase or decrease the number of channels that an - audio source uses, or to re-order those channels. - - Call the reset() method before using it to set up a default mapping, and then - the setInputChannelMapping() and setOutputChannelMapping() methods to - create an appropriate mapping, otherwise no channels will be connected and - it'll produce silence. - - @see AudioSource -*/ -class ChannelRemappingAudioSource : public AudioSource -{ -public: - //============================================================================== - /** Creates a remapping source that will pass on audio from the given input. - - @param source the input source to use. Make sure that this doesn't - get deleted before the ChannelRemappingAudioSource object - @param deleteSourceWhenDeleted if true, the input source will be deleted - when this object is deleted, if false, the caller is - responsible for its deletion - */ - ChannelRemappingAudioSource (AudioSource* source, - bool deleteSourceWhenDeleted); - - /** Destructor. */ - ~ChannelRemappingAudioSource(); - - //============================================================================== - /** Specifies a number of channels that this audio source must produce from its - getNextAudioBlock() callback. - */ - void setNumberOfChannelsToProduce (int requiredNumberOfChannels); - - /** Clears any mapped channels. - - After this, no channels are mapped, so this object will produce silence. Create - some mappings with setInputChannelMapping() and setOutputChannelMapping(). - */ - void clearAllMappings(); - - /** Creates an input channel mapping. - - When the getNextAudioBlock() method is called, the data in channel sourceChannelIndex of the incoming - data will be sent to destChannelIndex of our input source. - - @param destChannelIndex the index of an input channel in our input audio source (i.e. the - source specified when this object was created). - @param sourceChannelIndex the index of the input channel in the incoming audio data buffer - during our getNextAudioBlock() callback - */ - void setInputChannelMapping (int destChannelIndex, - int sourceChannelIndex); - - /** Creates an output channel mapping. - - When the getNextAudioBlock() method is called, the data returned in channel sourceChannelIndex by - our input audio source will be copied to channel destChannelIndex of the final buffer. - - @param sourceChannelIndex the index of an output channel coming from our input audio source - (i.e. the source specified when this object was created). - @param destChannelIndex the index of the output channel in the incoming audio data buffer - during our getNextAudioBlock() callback - */ - void setOutputChannelMapping (int sourceChannelIndex, - int destChannelIndex); - - /** Returns the channel from our input that will be sent to channel inputChannelIndex of - our input audio source. - */ - int getRemappedInputChannel (int inputChannelIndex) const; - - /** Returns the output channel to which channel outputChannelIndex of our input audio - source will be sent to. - */ - int getRemappedOutputChannel (int outputChannelIndex) const; - - - //============================================================================== - /** Returns an XML object to encapsulate the state of the mappings. - @see restoreFromXml - */ - XmlElement* createXml() const; - - /** Restores the mappings from an XML object created by createXML(). - @see createXml - */ - void restoreFromXml (const XmlElement&); - - //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - void releaseResources() override; - void getNextAudioBlock (const AudioSourceChannelInfo&) override; - - -private: - //============================================================================== - OptionalScopedPointer source; - Array remappedInputs, remappedOutputs; - int requiredNumberOfChannels; - - AudioSampleBuffer buffer; - AudioSourceChannelInfo remappedInfo; - CriticalSection lock; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelRemappingAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp deleted file mode 100644 index cbea3b8c2..000000000 --- a/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -IIRFilterAudioSource::IIRFilterAudioSource (AudioSource* const inputSource, - const bool deleteInputWhenDeleted) - : input (inputSource, deleteInputWhenDeleted) -{ - jassert (inputSource != nullptr); - - for (int i = 2; --i >= 0;) - iirFilters.add (new IIRFilter()); -} - -IIRFilterAudioSource::~IIRFilterAudioSource() {} - -//============================================================================== -void IIRFilterAudioSource::setCoefficients (const IIRCoefficients& newCoefficients) -{ - for (int i = iirFilters.size(); --i >= 0;) - iirFilters.getUnchecked(i)->setCoefficients (newCoefficients); -} - -void IIRFilterAudioSource::makeInactive() -{ - for (int i = iirFilters.size(); --i >= 0;) - iirFilters.getUnchecked(i)->makeInactive(); -} - -//============================================================================== -void IIRFilterAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) -{ - input->prepareToPlay (samplesPerBlockExpected, sampleRate); - - for (int i = iirFilters.size(); --i >= 0;) - iirFilters.getUnchecked(i)->reset(); -} - -void IIRFilterAudioSource::releaseResources() -{ - input->releaseResources(); -} - -void IIRFilterAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) -{ - input->getNextAudioBlock (bufferToFill); - - const int numChannels = bufferToFill.buffer->getNumChannels(); - - while (numChannels > iirFilters.size()) - iirFilters.add (new IIRFilter (*iirFilters.getUnchecked (0))); - - for (int i = 0; i < numChannels; ++i) - iirFilters.getUnchecked(i) - ->processSamples (bufferToFill.buffer->getWritePointer (i, bufferToFill.startSample), - bufferToFill.numSamples); -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h b/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h deleted file mode 100644 index 38faf285b..000000000 --- a/source/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - An AudioSource that performs an IIR filter on another source. -*/ -class JUCE_API IIRFilterAudioSource : public AudioSource -{ -public: - //============================================================================== - /** Creates a IIRFilterAudioSource for a given input source. - - @param inputSource the input source to read from - this must not be null - @param deleteInputWhenDeleted if true, the input source will be deleted when - this object is deleted - */ - IIRFilterAudioSource (AudioSource* inputSource, - bool deleteInputWhenDeleted); - - /** Destructor. */ - ~IIRFilterAudioSource(); - - //============================================================================== - /** Changes the filter to use the same parameters as the one being passed in. */ - void setCoefficients (const IIRCoefficients& newCoefficients); - - /** Calls IIRFilter::makeInactive() on all the filters being used internally. */ - void makeInactive(); - - //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - void releaseResources() override; - void getNextAudioBlock (const AudioSourceChannelInfo&) override; - -private: - //============================================================================== - OptionalScopedPointer input; - OwnedArray iirFilters; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IIRFilterAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp deleted file mode 100644 index 6adec0444..000000000 --- a/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -MemoryAudioSource::MemoryAudioSource (AudioBuffer& bufferToUse, bool copyMemory, bool shouldLoop) - : isLooping (shouldLoop) -{ - if (copyMemory) - buffer.makeCopyOf (bufferToUse); - else - buffer.setDataToReferTo (bufferToUse.getArrayOfWritePointers(), - bufferToUse.getNumChannels(), - bufferToUse.getNumSamples()); -} - -//============================================================================== -void MemoryAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, double /*sampleRate*/) -{ - position = 0; -} - -void MemoryAudioSource::releaseResources() {} - -void MemoryAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) -{ - auto& dst = *bufferToFill.buffer; - auto channels = jmin (dst.getNumChannels(), buffer.getNumChannels()); - auto max = 0, pos = 0; - auto n = buffer.getNumSamples(), m = bufferToFill.numSamples; - - for (auto i = position; (i < n || isLooping) && (pos < m); i += max) - { - max = jmin (m - pos, n - (i % n)); - - int ch = 0; - for (; ch < channels; ++ch) - dst.copyFrom (ch, bufferToFill.startSample + pos, buffer, ch, i % n, max); - - for (; ch < dst.getNumChannels(); ++ch) - dst.clear (ch, bufferToFill.startSample + pos, max); - - pos += max; - } - - if (pos < m) - dst.clear (bufferToFill.startSample + pos, m - pos); -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h b/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h deleted file mode 100644 index 50b11c7b9..000000000 --- a/source/modules/juce_audio_basics/sources/juce_MemoryAudioSource.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - An AudioSource which takes some float audio data as an input. -*/ -class JUCE_API MemoryAudioSource : public AudioSource -{ -public: - //============================================================================== - /** Creates a MemoryAudioSource by providing an audio buffer. - - If copyMemory is true then the buffer will be copied into an internal - buffer which will be owned by the MemoryAudioSource. If copyMemory is - false, then you must ensure that the lifetime of the audio buffer is - at least as long as the MemoryAudioSource. - */ - MemoryAudioSource (AudioBuffer& audioBuffer, bool copyMemory, bool shouldLoop = false); - - //============================================================================== - /** Implementation of the AudioSource method. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - - /** Implementation of the AudioSource method. */ - void releaseResources() override; - - /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override; - -private: - //============================================================================== - AudioBuffer buffer; - int position = 0; - bool isLooping; - - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp deleted file mode 100644 index b3f1d148c..000000000 --- a/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -MixerAudioSource::MixerAudioSource() - : currentSampleRate (0.0), bufferSizeExpected (0) -{ -} - -MixerAudioSource::~MixerAudioSource() -{ - removeAllInputs(); -} - -//============================================================================== -void MixerAudioSource::addInputSource (AudioSource* input, const bool deleteWhenRemoved) -{ - if (input != nullptr && ! inputs.contains (input)) - { - double localRate; - int localBufferSize; - - { - const ScopedLock sl (lock); - localRate = currentSampleRate; - localBufferSize = bufferSizeExpected; - } - - if (localRate > 0.0) - input->prepareToPlay (localBufferSize, localRate); - - const ScopedLock sl (lock); - - inputsToDelete.setBit (inputs.size(), deleteWhenRemoved); - inputs.add (input); - } -} - -void MixerAudioSource::removeInputSource (AudioSource* const input) -{ - if (input != nullptr) - { - ScopedPointer toDelete; - - { - const ScopedLock sl (lock); - const int index = inputs.indexOf (input); - - if (index < 0) - return; - - if (inputsToDelete [index]) - toDelete = input; - - inputsToDelete.shiftBits (-1, index); - inputs.remove (index); - } - - input->releaseResources(); - } -} - -void MixerAudioSource::removeAllInputs() -{ - OwnedArray toDelete; - - { - const ScopedLock sl (lock); - - for (int i = inputs.size(); --i >= 0;) - if (inputsToDelete[i]) - toDelete.add (inputs.getUnchecked(i)); - - inputs.clear(); - } - - for (int i = toDelete.size(); --i >= 0;) - toDelete.getUnchecked(i)->releaseResources(); -} - -void MixerAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) -{ - tempBuffer.setSize (2, samplesPerBlockExpected); - - const ScopedLock sl (lock); - - currentSampleRate = sampleRate; - bufferSizeExpected = samplesPerBlockExpected; - - for (int i = inputs.size(); --i >= 0;) - inputs.getUnchecked(i)->prepareToPlay (samplesPerBlockExpected, sampleRate); -} - -void MixerAudioSource::releaseResources() -{ - const ScopedLock sl (lock); - - for (int i = inputs.size(); --i >= 0;) - inputs.getUnchecked(i)->releaseResources(); - - tempBuffer.setSize (2, 0); - - currentSampleRate = 0; - bufferSizeExpected = 0; -} - -void MixerAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) -{ - const ScopedLock sl (lock); - - if (inputs.size() > 0) - { - inputs.getUnchecked(0)->getNextAudioBlock (info); - - if (inputs.size() > 1) - { - tempBuffer.setSize (jmax (1, info.buffer->getNumChannels()), - info.buffer->getNumSamples()); - - AudioSourceChannelInfo info2 (&tempBuffer, 0, info.numSamples); - - for (int i = 1; i < inputs.size(); ++i) - { - inputs.getUnchecked(i)->getNextAudioBlock (info2); - - for (int chan = 0; chan < info.buffer->getNumChannels(); ++chan) - info.buffer->addFrom (chan, info.startSample, tempBuffer, chan, 0, info.numSamples); - } - } - } - else - { - info.clearActiveBufferRegion(); - } -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.h b/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.h deleted file mode 100644 index 0c2b6e22c..000000000 --- a/source/modules/juce_audio_basics/sources/juce_MixerAudioSource.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - An AudioSource that mixes together the output of a set of other AudioSources. - - Input sources can be added and removed while the mixer is running as long as their - prepareToPlay() and releaseResources() methods are called before and after adding - them to the mixer. -*/ -class JUCE_API MixerAudioSource : public AudioSource -{ -public: - //============================================================================== - /** Creates a MixerAudioSource. */ - MixerAudioSource(); - - /** Destructor. */ - ~MixerAudioSource(); - - //============================================================================== - /** Adds an input source to the mixer. - - If the mixer is running you'll need to make sure that the input source - is ready to play by calling its prepareToPlay() method before adding it. - If the mixer is stopped, then its input sources will be automatically - prepared when the mixer's prepareToPlay() method is called. - - @param newInput the source to add to the mixer - @param deleteWhenRemoved if true, then this source will be deleted when - no longer needed by the mixer. - */ - void addInputSource (AudioSource* newInput, bool deleteWhenRemoved); - - /** Removes an input source. - If the source was added by calling addInputSource() with the deleteWhenRemoved - flag set, it will be deleted by this method. - */ - void removeInputSource (AudioSource* input); - - /** Removes all the input sources. - Any sources which were added by calling addInputSource() with the deleteWhenRemoved - flag set will be deleted by this method. - */ - void removeAllInputs(); - - //============================================================================== - /** Implementation of the AudioSource method. - This will call prepareToPlay() on all its input sources. - */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - - /** Implementation of the AudioSource method. - This will call releaseResources() on all its input sources. - */ - void releaseResources() override; - - /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo&) override; - - -private: - //============================================================================== - Array inputs; - BigInteger inputsToDelete; - CriticalSection lock; - AudioSampleBuffer tempBuffer; - double currentSampleRate; - int bufferSizeExpected; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MixerAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h b/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h deleted file mode 100644 index 5908bb82b..000000000 --- a/source/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - A type of AudioSource which can be repositioned. - - The basic AudioSource just streams continuously with no idea of a current - time or length, so the PositionableAudioSource is used for a finite stream - that has a current read position. - - @see AudioSource, AudioTransportSource -*/ -class JUCE_API PositionableAudioSource : public AudioSource -{ -protected: - //============================================================================== - /** Creates the PositionableAudioSource. */ - PositionableAudioSource() noexcept {} - -public: - /** Destructor */ - ~PositionableAudioSource() {} - - //============================================================================== - /** Tells the stream to move to a new position. - - Calling this indicates that the next call to AudioSource::getNextAudioBlock() - should return samples from this position. - - Note that this may be called on a different thread to getNextAudioBlock(), - so the subclass should make sure it's synchronised. - */ - virtual void setNextReadPosition (int64 newPosition) = 0; - - /** Returns the position from which the next block will be returned. - - @see setNextReadPosition - */ - virtual int64 getNextReadPosition() const = 0; - - /** Returns the total length of the stream (in samples). */ - virtual int64 getTotalLength() const = 0; - - /** Returns true if this source is actually playing in a loop. */ - virtual bool isLooping() const = 0; - - /** Tells the source whether you'd like it to play in a loop. */ - virtual void setLooping (bool shouldLoop) { ignoreUnused (shouldLoop); } -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp deleted file mode 100644 index 67ff49ccd..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, - const bool deleteInputWhenDeleted, - const int channels) - : input (inputSource, deleteInputWhenDeleted), - ratio (1.0), - lastRatio (1.0), - bufferPos (0), - sampsInBuffer (0), - subSampleOffset (0), - numChannels (channels) -{ - jassert (input != nullptr); - zeromem (coefficients, sizeof (coefficients)); -} - -ResamplingAudioSource::~ResamplingAudioSource() {} - -void ResamplingAudioSource::setResamplingRatio (const double samplesInPerOutputSample) -{ - jassert (samplesInPerOutputSample > 0); - - const SpinLock::ScopedLockType sl (ratioLock); - ratio = jmax (0.0, samplesInPerOutputSample); -} - -void ResamplingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) -{ - const SpinLock::ScopedLockType sl (ratioLock); - - const int scaledBlockSize = roundToInt (samplesPerBlockExpected * ratio); - input->prepareToPlay (scaledBlockSize, sampleRate * ratio); - - buffer.setSize (numChannels, scaledBlockSize + 32); - - filterStates.calloc ((size_t) numChannels); - srcBuffers.calloc ((size_t) numChannels); - destBuffers.calloc ((size_t) numChannels); - createLowPass (ratio); - - flushBuffers(); -} - -void ResamplingAudioSource::flushBuffers() -{ - buffer.clear(); - bufferPos = 0; - sampsInBuffer = 0; - subSampleOffset = 0.0; - resetFilters(); -} - -void ResamplingAudioSource::releaseResources() -{ - input->releaseResources(); - buffer.setSize (numChannels, 0); -} - -void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) -{ - double localRatio; - - { - const SpinLock::ScopedLockType sl (ratioLock); - localRatio = ratio; - } - - if (lastRatio != localRatio) - { - createLowPass (localRatio); - lastRatio = localRatio; - } - - const int sampsNeeded = roundToInt (info.numSamples * localRatio) + 3; - - int bufferSize = buffer.getNumSamples(); - - if (bufferSize < sampsNeeded + 8) - { - bufferPos %= bufferSize; - bufferSize = sampsNeeded + 32; - buffer.setSize (buffer.getNumChannels(), bufferSize, true, true); - } - - bufferPos %= bufferSize; - - int endOfBufferPos = bufferPos + sampsInBuffer; - const int channelsToProcess = jmin (numChannels, info.buffer->getNumChannels()); - - while (sampsNeeded > sampsInBuffer) - { - endOfBufferPos %= bufferSize; - - int numToDo = jmin (sampsNeeded - sampsInBuffer, - bufferSize - endOfBufferPos); - - AudioSourceChannelInfo readInfo (&buffer, endOfBufferPos, numToDo); - input->getNextAudioBlock (readInfo); - - if (localRatio > 1.0001) - { - // for down-sampling, pre-apply the filter.. - - for (int i = channelsToProcess; --i >= 0;) - applyFilter (buffer.getWritePointer (i, endOfBufferPos), numToDo, filterStates[i]); - } - - sampsInBuffer += numToDo; - endOfBufferPos += numToDo; - } - - for (int channel = 0; channel < channelsToProcess; ++channel) - { - destBuffers[channel] = info.buffer->getWritePointer (channel, info.startSample); - srcBuffers[channel] = buffer.getReadPointer (channel); - } - - int nextPos = (bufferPos + 1) % bufferSize; - - for (int m = info.numSamples; --m >= 0;) - { - jassert (sampsInBuffer > 0 && nextPos != endOfBufferPos); - - const float alpha = (float) subSampleOffset; - - for (int channel = 0; channel < channelsToProcess; ++channel) - *destBuffers[channel]++ = srcBuffers[channel][bufferPos] - + alpha * (srcBuffers[channel][nextPos] - srcBuffers[channel][bufferPos]); - - subSampleOffset += localRatio; - - while (subSampleOffset >= 1.0) - { - if (++bufferPos >= bufferSize) - bufferPos = 0; - - --sampsInBuffer; - - nextPos = (bufferPos + 1) % bufferSize; - subSampleOffset -= 1.0; - } - } - - if (localRatio < 0.9999) - { - // for up-sampling, apply the filter after transposing.. - for (int i = channelsToProcess; --i >= 0;) - applyFilter (info.buffer->getWritePointer (i, info.startSample), info.numSamples, filterStates[i]); - } - else if (localRatio <= 1.0001 && info.numSamples > 0) - { - // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities - for (int i = channelsToProcess; --i >= 0;) - { - const float* const endOfBuffer = info.buffer->getReadPointer (i, info.startSample + info.numSamples - 1); - FilterState& fs = filterStates[i]; - - if (info.numSamples > 1) - { - fs.y2 = fs.x2 = *(endOfBuffer - 1); - } - else - { - fs.y2 = fs.y1; - fs.x2 = fs.x1; - } - - fs.y1 = fs.x1 = *endOfBuffer; - } - } - - jassert (sampsInBuffer >= 0); -} - -void ResamplingAudioSource::createLowPass (const double frequencyRatio) -{ - const double proportionalRate = (frequencyRatio > 1.0) ? 0.5 / frequencyRatio - : 0.5 * frequencyRatio; - - const double n = 1.0 / std::tan (double_Pi * jmax (0.001, proportionalRate)); - const double nSquared = n * n; - const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); - - setFilterCoefficients (c1, - c1 * 2.0f, - c1, - 1.0, - c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); -} - -void ResamplingAudioSource::setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6) -{ - const double a = 1.0 / c4; - - c1 *= a; - c2 *= a; - c3 *= a; - c5 *= a; - c6 *= a; - - coefficients[0] = c1; - coefficients[1] = c2; - coefficients[2] = c3; - coefficients[3] = c4; - coefficients[4] = c5; - coefficients[5] = c6; -} - -void ResamplingAudioSource::resetFilters() -{ - if (filterStates != nullptr) - filterStates.clear ((size_t) numChannels); -} - -void ResamplingAudioSource::applyFilter (float* samples, int num, FilterState& fs) -{ - while (--num >= 0) - { - const double in = *samples; - - double out = coefficients[0] * in - + coefficients[1] * fs.x1 - + coefficients[2] * fs.x2 - - coefficients[4] * fs.y1 - - coefficients[5] * fs.y2; - - #if JUCE_INTEL - if (! (out < -1.0e-8 || out > 1.0e-8)) - out = 0; - #endif - - fs.x2 = fs.x1; - fs.x1 = in; - fs.y2 = fs.y1; - fs.y1 = out; - - *samples++ = (float) out; - } -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h b/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h deleted file mode 100644 index 9f8fcd239..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - A type of AudioSource that takes an input source and changes its sample rate. - - @see AudioSource, LagrangeInterpolator, CatmullRomInterpolator -*/ -class JUCE_API ResamplingAudioSource : public AudioSource -{ -public: - //============================================================================== - /** Creates a ResamplingAudioSource for a given input source. - - @param inputSource the input source to read from - @param deleteInputWhenDeleted if true, the input source will be deleted when - this object is deleted - @param numChannels the number of channels to process - */ - ResamplingAudioSource (AudioSource* inputSource, - bool deleteInputWhenDeleted, - int numChannels = 2); - - /** Destructor. */ - ~ResamplingAudioSource(); - - /** Changes the resampling ratio. - - (This value can be changed at any time, even while the source is running). - - @param samplesInPerOutputSample if set to 1.0, the input is passed through; higher - values will speed it up; lower values will slow it - down. The ratio must be greater than 0 - */ - void setResamplingRatio (double samplesInPerOutputSample); - - /** Returns the current resampling ratio. - - This is the value that was set by setResamplingRatio(). - */ - double getResamplingRatio() const noexcept { return ratio; } - - /** Clears any buffers and filters that the resampler is using. */ - void flushBuffers(); - - //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - void releaseResources() override; - void getNextAudioBlock (const AudioSourceChannelInfo&) override; - -private: - //============================================================================== - OptionalScopedPointer input; - double ratio, lastRatio; - AudioSampleBuffer buffer; - int bufferPos, sampsInBuffer; - double subSampleOffset; - double coefficients[6]; - SpinLock ratioLock; - const int numChannels; - HeapBlock destBuffers; - HeapBlock srcBuffers; - - void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); - void createLowPass (double proportionalRate); - - struct FilterState - { - double x1, x2, y1, y2; - }; - - HeapBlock filterStates; - void resetFilters(); - - void applyFilter (float* samples, int num, FilterState& fs); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResamplingAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp deleted file mode 100644 index dd4e7ab03..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -ReverbAudioSource::ReverbAudioSource (AudioSource* const inputSource, const bool deleteInputWhenDeleted) - : input (inputSource, deleteInputWhenDeleted), - bypass (false) -{ - jassert (inputSource != nullptr); -} - -ReverbAudioSource::~ReverbAudioSource() {} - -void ReverbAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate) -{ - const ScopedLock sl (lock); - input->prepareToPlay (samplesPerBlockExpected, sampleRate); - reverb.setSampleRate (sampleRate); -} - -void ReverbAudioSource::releaseResources() {} - -void ReverbAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) -{ - const ScopedLock sl (lock); - - input->getNextAudioBlock (bufferToFill); - - if (! bypass) - { - float* const firstChannel = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); - - if (bufferToFill.buffer->getNumChannels() > 1) - { - reverb.processStereo (firstChannel, - bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample), - bufferToFill.numSamples); - } - else - { - reverb.processMono (firstChannel, bufferToFill.numSamples); - } - } -} - -void ReverbAudioSource::setParameters (const Reverb::Parameters& newParams) -{ - const ScopedLock sl (lock); - reverb.setParameters (newParams); -} - -void ReverbAudioSource::setBypassed (bool b) noexcept -{ - if (bypass != b) - { - const ScopedLock sl (lock); - bypass = b; - reverb.reset(); - } -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h b/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h deleted file mode 100644 index 1d2591b64..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - An AudioSource that uses the Reverb class to apply a reverb to another AudioSource. - - @see Reverb -*/ -class JUCE_API ReverbAudioSource : public AudioSource -{ -public: - /** Creates a ReverbAudioSource to process a given input source. - - @param inputSource the input source to read from - this must not be null - @param deleteInputWhenDeleted if true, the input source will be deleted when - this object is deleted - */ - ReverbAudioSource (AudioSource* inputSource, - bool deleteInputWhenDeleted); - - /** Destructor. */ - ~ReverbAudioSource(); - - //============================================================================== - /** Returns the parameters from the reverb. */ - const Reverb::Parameters& getParameters() const noexcept { return reverb.getParameters(); } - - /** Changes the reverb's parameters. */ - void setParameters (const Reverb::Parameters& newParams); - - void setBypassed (bool isBypassed) noexcept; - bool isBypassed() const noexcept { return bypass; } - - //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - void releaseResources() override; - void getNextAudioBlock (const AudioSourceChannelInfo&) override; - -private: - //============================================================================== - CriticalSection lock; - OptionalScopedPointer input; - Reverb reverb; - volatile bool bypass; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp deleted file mode 100644 index b33a92249..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -ToneGeneratorAudioSource::ToneGeneratorAudioSource() - : frequency (1000.0), - sampleRate (44100.0), - currentPhase (0.0), - phasePerSample (0.0), - amplitude (0.5f) -{ -} - -ToneGeneratorAudioSource::~ToneGeneratorAudioSource() -{ -} - -//============================================================================== -void ToneGeneratorAudioSource::setAmplitude (const float newAmplitude) -{ - amplitude = newAmplitude; -} - -void ToneGeneratorAudioSource::setFrequency (const double newFrequencyHz) -{ - frequency = newFrequencyHz; - phasePerSample = 0.0; -} - -//============================================================================== -void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, double rate) -{ - currentPhase = 0.0; - phasePerSample = 0.0; - sampleRate = rate; -} - -void ToneGeneratorAudioSource::releaseResources() -{ -} - -void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) -{ - if (phasePerSample == 0.0) - phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); - - for (int i = 0; i < info.numSamples; ++i) - { - const float sample = amplitude * (float) std::sin (currentPhase); - currentPhase += phasePerSample; - - for (int j = info.buffer->getNumChannels(); --j >= 0;) - info.buffer->setSample (j, info.startSample + i, sample); - } -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h b/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h deleted file mode 100644 index 9c8e939d9..000000000 --- a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - A simple AudioSource that generates a sine wave. - -*/ -class JUCE_API ToneGeneratorAudioSource : public AudioSource -{ -public: - //============================================================================== - /** Creates a ToneGeneratorAudioSource. */ - ToneGeneratorAudioSource(); - - /** Destructor. */ - ~ToneGeneratorAudioSource(); - - //============================================================================== - /** Sets the signal's amplitude. */ - void setAmplitude (float newAmplitude); - - /** Sets the signal's frequency. */ - void setFrequency (double newFrequencyHz); - - - //============================================================================== - /** Implementation of the AudioSource method. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; - - /** Implementation of the AudioSource method. */ - void releaseResources() override; - - /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo&) override; - - -private: - //============================================================================== - double frequency, sampleRate; - double currentPhase, phasePerSample; - float amplitude; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToneGeneratorAudioSource) -}; - -} // namespace juce diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp deleted file mode 100644 index cb2eaf904..000000000 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ /dev/null @@ -1,574 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -SynthesiserSound::SynthesiserSound() {} -SynthesiserSound::~SynthesiserSound() {} - -//============================================================================== -SynthesiserVoice::SynthesiserVoice() {} -SynthesiserVoice::~SynthesiserVoice() {} - -bool SynthesiserVoice::isPlayingChannel (const int midiChannel) const -{ - return currentPlayingMidiChannel == midiChannel; -} - -void SynthesiserVoice::setCurrentPlaybackSampleRate (const double newRate) -{ - currentSampleRate = newRate; -} - -bool SynthesiserVoice::isVoiceActive() const -{ - return getCurrentlyPlayingNote() >= 0; -} - -void SynthesiserVoice::clearCurrentNote() -{ - currentlyPlayingNote = -1; - currentlyPlayingSound = nullptr; - currentPlayingMidiChannel = 0; -} - -void SynthesiserVoice::aftertouchChanged (int) {} -void SynthesiserVoice::channelPressureChanged (int) {} - -bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept -{ - return noteOnTime < other.noteOnTime; -} - -void SynthesiserVoice::renderNextBlock (AudioBuffer& outputBuffer, - int startSample, int numSamples) -{ - AudioBuffer subBuffer (outputBuffer.getArrayOfWritePointers(), - outputBuffer.getNumChannels(), - startSample, numSamples); - - tempBuffer.makeCopyOf (subBuffer, true); - renderNextBlock (tempBuffer, 0, numSamples); - subBuffer.makeCopyOf (tempBuffer, true); -} - -//============================================================================== -Synthesiser::Synthesiser() -{ - for (int i = 0; i < numElementsInArray (lastPitchWheelValues); ++i) - lastPitchWheelValues[i] = 0x2000; -} - -Synthesiser::~Synthesiser() -{ -} - -//============================================================================== -SynthesiserVoice* Synthesiser::getVoice (const int index) const -{ - const ScopedLock sl (lock); - return voices [index]; -} - -void Synthesiser::clearVoices() -{ - const ScopedLock sl (lock); - voices.clear(); -} - -SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice) -{ - const ScopedLock sl (lock); - newVoice->setCurrentPlaybackSampleRate (sampleRate); - return voices.add (newVoice); -} - -void Synthesiser::removeVoice (const int index) -{ - const ScopedLock sl (lock); - voices.remove (index); -} - -void Synthesiser::clearSounds() -{ - const ScopedLock sl (lock); - sounds.clear(); -} - -SynthesiserSound* Synthesiser::addSound (const SynthesiserSound::Ptr& newSound) -{ - const ScopedLock sl (lock); - return sounds.add (newSound); -} - -void Synthesiser::removeSound (const int index) -{ - const ScopedLock sl (lock); - sounds.remove (index); -} - -void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) -{ - shouldStealNotes = shouldSteal; -} - -void Synthesiser::setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict) noexcept -{ - jassert (numSamples > 0); // it wouldn't make much sense for this to be less than 1 - minimumSubBlockSize = numSamples; - subBlockSubdivisionIsStrict = shouldBeStrict; -} - -//============================================================================== -void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) -{ - if (sampleRate != newRate) - { - const ScopedLock sl (lock); - allNotesOff (0, false); - sampleRate = newRate; - - for (auto* voice : voices) - voice->setCurrentPlaybackSampleRate (newRate); - } -} - -template -void Synthesiser::processNextBlock (AudioBuffer& outputAudio, - const MidiBuffer& midiData, - int startSample, - int numSamples) -{ - // must set the sample rate before using this! - jassert (sampleRate != 0); - const int targetChannels = outputAudio.getNumChannels(); - - MidiBuffer::Iterator midiIterator (midiData); - midiIterator.setNextSamplePosition (startSample); - - bool firstEvent = true; - int midiEventPos; - MidiMessage m; - - const ScopedLock sl (lock); - - while (numSamples > 0) - { - if (! midiIterator.getNextEvent (m, midiEventPos)) - { - if (targetChannels > 0) - renderVoices (outputAudio, startSample, numSamples); - - return; - } - - const int samplesToNextMidiMessage = midiEventPos - startSample; - - if (samplesToNextMidiMessage >= numSamples) - { - if (targetChannels > 0) - renderVoices (outputAudio, startSample, numSamples); - - handleMidiEvent (m); - break; - } - - if (samplesToNextMidiMessage < ((firstEvent && ! subBlockSubdivisionIsStrict) ? 1 : minimumSubBlockSize)) - { - handleMidiEvent (m); - continue; - } - - firstEvent = false; - - if (targetChannels > 0) - renderVoices (outputAudio, startSample, samplesToNextMidiMessage); - - handleMidiEvent (m); - startSample += samplesToNextMidiMessage; - numSamples -= samplesToNextMidiMessage; - } - - while (midiIterator.getNextEvent (m, midiEventPos)) - handleMidiEvent (m); -} - -// explicit template instantiation -template void Synthesiser::processNextBlock (AudioBuffer&, const MidiBuffer&, int, int); -template void Synthesiser::processNextBlock (AudioBuffer&, const MidiBuffer&, int, int); - -void Synthesiser::renderVoices (AudioBuffer& buffer, int startSample, int numSamples) -{ - for (auto* voice : voices) - voice->renderNextBlock (buffer, startSample, numSamples); -} - -void Synthesiser::renderVoices (AudioBuffer& buffer, int startSample, int numSamples) -{ - for (auto* voice : voices) - voice->renderNextBlock (buffer, startSample, numSamples); -} - -void Synthesiser::handleMidiEvent (const MidiMessage& m) -{ - const int channel = m.getChannel(); - - if (m.isNoteOn()) - { - noteOn (channel, m.getNoteNumber(), m.getFloatVelocity()); - } - else if (m.isNoteOff()) - { - noteOff (channel, m.getNoteNumber(), m.getFloatVelocity(), true); - } - else if (m.isAllNotesOff() || m.isAllSoundOff()) - { - allNotesOff (channel, true); - } - else if (m.isPitchWheel()) - { - const int wheelPos = m.getPitchWheelValue(); - lastPitchWheelValues [channel - 1] = wheelPos; - handlePitchWheel (channel, wheelPos); - } - else if (m.isAftertouch()) - { - handleAftertouch (channel, m.getNoteNumber(), m.getAfterTouchValue()); - } - else if (m.isChannelPressure()) - { - handleChannelPressure (channel, m.getChannelPressureValue()); - } - else if (m.isController()) - { - handleController (channel, m.getControllerNumber(), m.getControllerValue()); - } - else if (m.isProgramChange()) - { - handleProgramChange (channel, m.getProgramChangeNumber()); - } -} - -//============================================================================== -void Synthesiser::noteOn (const int midiChannel, - const int midiNoteNumber, - const float velocity) -{ - const ScopedLock sl (lock); - - for (auto* sound : sounds) - { - if (sound->appliesToNote (midiNoteNumber) && sound->appliesToChannel (midiChannel)) - { - // If hitting a note that's still ringing, stop it first (it could be - // still playing because of the sustain or sostenuto pedal). - for (auto* voice : voices) - if (voice->getCurrentlyPlayingNote() == midiNoteNumber && voice->isPlayingChannel (midiChannel)) - stopVoice (voice, 1.0f, true); - - startVoice (findFreeVoice (sound, midiChannel, midiNoteNumber, shouldStealNotes), - sound, midiChannel, midiNoteNumber, velocity); - } - } -} - -void Synthesiser::startVoice (SynthesiserVoice* const voice, - SynthesiserSound* const sound, - const int midiChannel, - const int midiNoteNumber, - const float velocity) -{ - if (voice != nullptr && sound != nullptr) - { - if (voice->currentlyPlayingSound != nullptr) - voice->stopNote (0.0f, false); - - voice->currentlyPlayingNote = midiNoteNumber; - voice->currentPlayingMidiChannel = midiChannel; - voice->noteOnTime = ++lastNoteOnCounter; - voice->currentlyPlayingSound = sound; - voice->setKeyDown (true); - voice->setSostenutoPedalDown (false); - voice->setSustainPedalDown (sustainPedalsDown[midiChannel]); - - voice->startNote (midiNoteNumber, velocity, sound, - lastPitchWheelValues [midiChannel - 1]); - } -} - -void Synthesiser::stopVoice (SynthesiserVoice* voice, float velocity, const bool allowTailOff) -{ - jassert (voice != nullptr); - - voice->stopNote (velocity, allowTailOff); - - // the subclass MUST call clearCurrentNote() if it's not tailing off! RTFM for stopNote()! - jassert (allowTailOff || (voice->getCurrentlyPlayingNote() < 0 && voice->getCurrentlyPlayingSound() == 0)); -} - -void Synthesiser::noteOff (const int midiChannel, - const int midiNoteNumber, - const float velocity, - const bool allowTailOff) -{ - const ScopedLock sl (lock); - - for (auto* voice : voices) - { - if (voice->getCurrentlyPlayingNote() == midiNoteNumber - && voice->isPlayingChannel (midiChannel)) - { - if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound()) - { - if (sound->appliesToNote (midiNoteNumber) - && sound->appliesToChannel (midiChannel)) - { - jassert (! voice->keyIsDown || voice->isSustainPedalDown() == sustainPedalsDown [midiChannel]); - - voice->setKeyDown (false); - - if (! (voice->isSustainPedalDown() || voice->isSostenutoPedalDown())) - stopVoice (voice, velocity, allowTailOff); - } - } - } - } -} - -void Synthesiser::allNotesOff (const int midiChannel, const bool allowTailOff) -{ - const ScopedLock sl (lock); - - for (auto* voice : voices) - if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) - voice->stopNote (1.0f, allowTailOff); - - sustainPedalsDown.clear(); -} - -void Synthesiser::handlePitchWheel (const int midiChannel, const int wheelValue) -{ - const ScopedLock sl (lock); - - for (auto* voice : voices) - if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) - voice->pitchWheelMoved (wheelValue); -} - -void Synthesiser::handleController (const int midiChannel, - const int controllerNumber, - const int controllerValue) -{ - switch (controllerNumber) - { - case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break; - case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break; - case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break; - default: break; - } - - const ScopedLock sl (lock); - - for (auto* voice : voices) - if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) - voice->controllerMoved (controllerNumber, controllerValue); -} - -void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue) -{ - const ScopedLock sl (lock); - - for (auto* voice : voices) - if (voice->getCurrentlyPlayingNote() == midiNoteNumber - && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))) - voice->aftertouchChanged (aftertouchValue); -} - -void Synthesiser::handleChannelPressure (int midiChannel, int channelPressureValue) -{ - const ScopedLock sl (lock); - - for (auto* voice : voices) - if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel)) - voice->channelPressureChanged (channelPressureValue); -} - -void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) -{ - jassert (midiChannel > 0 && midiChannel <= 16); - const ScopedLock sl (lock); - - if (isDown) - { - sustainPedalsDown.setBit (midiChannel); - - for (auto* voice : voices) - if (voice->isPlayingChannel (midiChannel) && voice->isKeyDown()) - voice->setSustainPedalDown (true); - } - else - { - for (auto* voice : voices) - { - if (voice->isPlayingChannel (midiChannel)) - { - voice->setSustainPedalDown (false); - - if (! (voice->isKeyDown() || voice->isSostenutoPedalDown())) - stopVoice (voice, 1.0f, true); - } - } - - sustainPedalsDown.clearBit (midiChannel); - } -} - -void Synthesiser::handleSostenutoPedal (int midiChannel, bool isDown) -{ - jassert (midiChannel > 0 && midiChannel <= 16); - const ScopedLock sl (lock); - - for (auto* voice : voices) - { - if (voice->isPlayingChannel (midiChannel)) - { - if (isDown) - voice->setSostenutoPedalDown (true); - else if (voice->isSostenutoPedalDown()) - stopVoice (voice, 1.0f, true); - } - } -} - -void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) -{ - ignoreUnused (midiChannel); - jassert (midiChannel > 0 && midiChannel <= 16); -} - -void Synthesiser::handleProgramChange (int midiChannel, int programNumber) -{ - ignoreUnused (midiChannel, programNumber); - jassert (midiChannel > 0 && midiChannel <= 16); -} - -//============================================================================== -SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, - int midiChannel, int midiNoteNumber, - const bool stealIfNoneAvailable) const -{ - const ScopedLock sl (lock); - - for (auto* voice : voices) - if ((! voice->isVoiceActive()) && voice->canPlaySound (soundToPlay)) - return voice; - - if (stealIfNoneAvailable) - return findVoiceToSteal (soundToPlay, midiChannel, midiNoteNumber); - - return nullptr; -} - -struct VoiceAgeSorter -{ - static int compareElements (SynthesiserVoice* v1, SynthesiserVoice* v2) noexcept - { - return v1->wasStartedBefore (*v2) ? -1 : (v2->wasStartedBefore (*v1) ? 1 : 0); - } -}; - -SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay, - int /*midiChannel*/, int midiNoteNumber) 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.isEmpty()); - - // 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 - - // this is a list of voices we can steal, sorted by how long they've been running - Array usableVoices; - usableVoices.ensureStorageAllocated (voices.size()); - - for (auto* voice : voices) - { - if (voice->canPlaySound (soundToPlay)) - { - jassert (voice->isVoiceActive()); // We wouldn't be here otherwise - - VoiceAgeSorter sorter; - usableVoices.addSorted (sorter, voice); - - if (! voice->isPlayingButReleased()) // Don't protect released notes - { - auto note = voice->getCurrentlyPlayingNote(); - - if (low == nullptr || note < low->getCurrentlyPlayingNote()) - low = voice; - - if (top == nullptr || note > top->getCurrentlyPlayingNote()) - top = voice; - } - } - } - - // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s) - if (top == low) - top = nullptr; - - // The oldest note that's playing with the target pitch is ideal.. - for (auto* voice : usableVoices) - if (voice->getCurrentlyPlayingNote() == midiNoteNumber) - return voice; - - // Oldest voice that has been released (no finger on it and not held by sustain pedal) - for (auto* voice : usableVoices) - if (voice != low && voice != top && voice->isPlayingButReleased()) - return voice; - - // Oldest voice that doesn't have a finger on it: - for (auto* voice : usableVoices) - if (voice != low && voice != top && ! voice->isKeyDown()) - return voice; - - // Oldest voice that isn't protected - for (auto* voice : usableVoices) - 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; -} - -} // namespace juce diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h deleted file mode 100644 index b8e50876f..000000000 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ /dev/null @@ -1,649 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library. - Copyright (c) 2017 - ROLI Ltd. - - JUCE is an open source library subject to commercial or open-source - licensing. - - The code included in this file is provided under the terms of the ISC license - http://www.isc.org/downloads/software-support-policy/isc-license. Permission - To use, copy, modify, and/or distribute this software for any purpose with or - without fee is hereby granted provided that the above copyright notice and - this permission notice appear in all copies. - - JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER - EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE - DISCLAIMED. - - ============================================================================== -*/ - -namespace juce -{ - -//============================================================================== -/** - Describes one of the sounds that a Synthesiser can play. - - A synthesiser can contain one or more sounds, and a sound can choose which - midi notes and channels can trigger it. - - The SynthesiserSound is a passive class that just describes what the sound is - - the actual audio rendering for a sound is done by a SynthesiserVoice. This allows - more than one SynthesiserVoice to play the same sound at the same time. - - @see Synthesiser, SynthesiserVoice -*/ -class JUCE_API SynthesiserSound : public ReferenceCountedObject -{ -protected: - //============================================================================== - SynthesiserSound(); - -public: - /** Destructor. */ - virtual ~SynthesiserSound(); - - //============================================================================== - /** Returns true if this sound should be played when a given midi note is pressed. - - The Synthesiser will use this information when deciding which sounds to trigger - for a given note. - */ - virtual bool appliesToNote (int midiNoteNumber) = 0; - - /** Returns true if the sound should be triggered by midi events on a given channel. - - The Synthesiser will use this information when deciding which sounds to trigger - for a given note. - */ - virtual bool appliesToChannel (int midiChannel) = 0; - - /** The class is reference-counted, so this is a handy pointer class for it. */ - typedef ReferenceCountedObjectPtr Ptr; - - -private: - //============================================================================== - JUCE_LEAK_DETECTOR (SynthesiserSound) -}; - - -//============================================================================== -/** - Represents a voice that a Synthesiser can use to play a SynthesiserSound. - - A voice plays a single sound at a time, and a synthesiser holds an array of - voices so that it can play polyphonically. - - @see Synthesiser, SynthesiserSound -*/ -class JUCE_API SynthesiserVoice -{ -public: - //============================================================================== - /** Creates a voice. */ - SynthesiserVoice(); - - /** Destructor. */ - virtual ~SynthesiserVoice(); - - //============================================================================== - /** Returns the midi note that this voice is currently playing. - Returns a value less than 0 if no note is playing. - */ - int getCurrentlyPlayingNote() const noexcept { return currentlyPlayingNote; } - - /** Returns the sound that this voice is currently playing. - Returns nullptr if it's not playing. - */ - SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept { return currentlyPlayingSound; } - - /** Must return true if this voice object is capable of playing the given sound. - - If there are different classes of sound, and different classes of voice, a voice can - choose which ones it wants to take on. - - A typical implementation of this method may just return true if there's only one type - of voice and sound, or it might check the type of the sound object passed-in and - see if it's one that it understands. - */ - virtual bool canPlaySound (SynthesiserSound*) = 0; - - /** Called to start a new note. - This will be called during the rendering callback, so must be fast and thread-safe. - */ - virtual void startNote (int midiNoteNumber, - float velocity, - SynthesiserSound* sound, - int currentPitchWheelPosition) = 0; - - /** Called to stop a note. - - This will be called during the rendering callback, so must be fast and thread-safe. - - The velocity indicates how quickly the note was released - 0 is slowly, 1 is quickly. - - 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 stopNote (float velocity, bool allowTailOff) = 0; - - /** Returns true if this voice is currently busy playing a sound. - By default this just checks the getCurrentlyPlayingNote() value, but can - be overridden for more advanced checking. - */ - virtual bool isVoiceActive() const; - - /** Called to let the voice know that the pitch wheel has been moved. - This will be called during the rendering callback, so must be fast and thread-safe. - */ - virtual void pitchWheelMoved (int newPitchWheelValue) = 0; - - /** Called to let the voice know that a midi controller has been moved. - This will be called during the rendering callback, so must be fast and thread-safe. - */ - virtual void controllerMoved (int controllerNumber, int newControllerValue) = 0; - - /** Called to let the voice know that the aftertouch has changed. - This will be called during the rendering callback, so must be fast and thread-safe. - */ - virtual void aftertouchChanged (int newAftertouchValue); - - /** Called to let the voice know that the channel pressure has changed. - This will be called during the rendering callback, so must be fast and thread-safe. - */ - virtual void channelPressureChanged (int newChannelPressureValue); - - //============================================================================== - /** 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& outputBuffer, - int startSample, - int numSamples) = 0; - - /** A double-precision version of renderNextBlock() */ - virtual void renderNextBlock (AudioBuffer& 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 setCurrentPlaybackSampleRate (double newRate); - - /** Returns true if the voice is currently playing a sound which is mapped to the given - midi channel. - - If it's not currently playing, this will return false. - */ - virtual bool isPlayingChannel (int midiChannel) const; - - /** 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 the key that triggered this voice is still held down. - Note that the voice may still be playing after the key was released (e.g because the - sostenuto pedal is down). - */ - bool isKeyDown() const noexcept { return keyIsDown; } - - /** Allows you to modify the flag indicating that the key that triggered this voice is still held down. - @see isKeyDown - */ - void setKeyDown (bool isNowDown) noexcept { keyIsDown = isNowDown; } - - /** Returns true if the sustain pedal is currently active for this voice. */ - bool isSustainPedalDown() const noexcept { return sustainPedalDown; } - - /** Modifies the sustain pedal flag. */ - void setSustainPedalDown (bool isNowDown) noexcept { sustainPedalDown = isNowDown; } - - /** Returns true if the sostenuto pedal is currently active for this voice. */ - bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } - - /** Modifies the sostenuto pedal flag. */ - void setSostenutoPedalDown (bool isNowDown) noexcept { sostenutoPedalDown = isNowDown; } - - /** Returns true if a voice is sounding in its release phase **/ - bool isPlayingButReleased() const noexcept - { - return isVoiceActive() && ! (isKeyDown() || isSostenutoPedalDown() || isSustainPedalDown()); - } - - /** Returns true if this voice started playing its current note before the other voice did. */ - bool wasStartedBefore (const SynthesiserVoice& 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(); - - -private: - //============================================================================== - friend class Synthesiser; - - double currentSampleRate = 44100.0; - int currentlyPlayingNote = -1, currentPlayingMidiChannel = 0; - uint32 noteOnTime = 0; - SynthesiserSound::Ptr currentlyPlayingSound; - bool keyIsDown = false, sustainPedalDown = false, sostenutoPedalDown = false; - - AudioBuffer tempBuffer; - - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note the new parameters for this method. - virtual int stopNote (bool) { return 0; } - #endif - - JUCE_LEAK_DETECTOR (SynthesiserVoice) -}; - - -//============================================================================== -/** - Base class for a musical device that can play sounds. - - To create a synthesiser, you'll need to create a subclass of SynthesiserSound - to describe each sound available to your synth, and a subclass of SynthesiserVoice - which can play back one of these sounds. - - Then you can use the addVoice() and addSound() methods to give the synthesiser a - set of sounds, and a set of voices it can use to play them. 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. Any midi - events that go in will be scanned for note on/off messages, and these are used to - start and stop the voices playing the appropriate sounds. - - While it's playing, you can also cause notes to be triggered by calling the noteOn(), - noteOff() and other controller methods. - - 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. -*/ -class JUCE_API Synthesiser -{ -public: - //============================================================================== - /** Creates a new synthesiser. - You'll need to add some sounds and voices before it'll make any sound. - */ - Synthesiser(); - - /** Destructor. */ - virtual ~Synthesiser(); - - //============================================================================== - /** 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. */ - SynthesiserVoice* 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. - */ - SynthesiserVoice* addVoice (SynthesiserVoice* newVoice); - - /** Deletes one of the voices. */ - void removeVoice (int index); - - //============================================================================== - /** Deletes all sounds. */ - void clearSounds(); - - /** Returns the number of sounds that have been added to the synth. */ - int getNumSounds() const noexcept { return sounds.size(); } - - /** Returns one of the sounds. */ - SynthesiserSound* getSound (int index) const noexcept { return sounds [index]; } - - /** Adds a new sound to the synthesiser. - - The object passed in is reference counted, so will be deleted when the - synthesiser and all voices are no longer using it. - */ - SynthesiserSound* addSound (const SynthesiserSound::Ptr& newSound); - - /** Removes and deletes one of the sounds. */ - void removeSound (int index); - - //============================================================================== - /** 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 setNoteStealingEnabled (bool shouldStealNotes); - - /** Returns true if note-stealing is enabled. - @see setNoteStealingEnabled - */ - bool isNoteStealingEnabled() const noexcept { return shouldStealNotes; } - - //============================================================================== - /** Triggers a note-on event. - - The default method here will find all the sounds that want to be triggered by - this note/channel. For each sound, it'll try to find a free voice, and use the - voice to start playing the sound. - - Subclasses might want to override this if they need a more complex algorithm. - - This method will be called automatically according to the midi data passed into - renderNextBlock(), but may be called explicitly too. - - The midiChannel parameter is the channel, between 1 and 16 inclusive. - */ - virtual void noteOn (int midiChannel, - int midiNoteNumber, - float velocity); - - /** Triggers a note-off event. - - This will turn off any voices that are playing a sound for the given note/channel. - - 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 will be called automatically according to the midi data passed into - renderNextBlock(), but may be called explicitly too. - - The midiChannel parameter is the channel, between 1 and 16 inclusive. - */ - virtual void noteOff (int midiChannel, - int midiNoteNumber, - float velocity, - bool allowTailOff); - - /** Turns off all notes. - - This will turn off any voices that are playing a sound on the given midi channel. - - If midiChannel is 0 or less, then all voices will be turned off, regardless of - which channel they're playing. Otherwise it represents a valid midi channel, from - 1 to 16 inclusive. - - 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 will be called automatically according to the midi data passed into - renderNextBlock(), but may be called explicitly too. - */ - virtual void allNotesOff (int midiChannel, - bool allowTailOff); - - /** Sends a pitch-wheel message to any active voices. - - This will send a pitch-wheel message to any voices that are playing sounds on - the given midi channel. - - This method will be called automatically according to the midi data passed into - renderNextBlock(), but may be called explicitly too. - - @param midiChannel the midi channel, from 1 to 16 inclusive - @param wheelValue the wheel position, from 0 to 0x3fff, as returned by MidiMessage::getPitchWheelValue() - */ - virtual void handlePitchWheel (int midiChannel, - int wheelValue); - - /** Sends a midi controller message to any active voices. - - This will send a midi controller message to any voices that are playing sounds on - the given midi channel. - - This method will be called automatically according to the midi data passed into - renderNextBlock(), but may be called explicitly too. - - @param midiChannel the midi channel, from 1 to 16 inclusive - @param controllerNumber the midi controller type, as returned by MidiMessage::getControllerNumber() - @param controllerValue the midi controller value, between 0 and 127, as returned by MidiMessage::getControllerValue() - */ - virtual void handleController (int midiChannel, - int controllerNumber, - int controllerValue); - - /** Sends an aftertouch message. - - This will send an aftertouch message to any voices that are playing sounds on - the given midi channel and note number. - - This method will be called automatically according to the midi data passed into - renderNextBlock(), but may be called explicitly too. - - @param midiChannel the midi channel, from 1 to 16 inclusive - @param midiNoteNumber the midi note number, 0 to 127 - @param aftertouchValue the aftertouch value, between 0 and 127, - as returned by MidiMessage::getAftertouchValue() - */ - virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); - - /** Sends a channel pressure message. - - This will send a channel pressure message to any voices that are playing sounds on - the given midi channel. - - This method will be called automatically according to the midi data passed into - renderNextBlock(), but may be called explicitly too. - - @param midiChannel the midi channel, from 1 to 16 inclusive - @param channelPressureValue the pressure value, between 0 and 127, as returned - by MidiMessage::getChannelPressureValue() - */ - virtual void handleChannelPressure (int midiChannel, int channelPressureValue); - - /** Handles a sustain pedal event. */ - virtual void handleSustainPedal (int midiChannel, bool isDown); - - /** Handles a sostenuto pedal event. */ - virtual void handleSostenutoPedal (int midiChannel, bool isDown); - - /** Can be overridden to handle soft pedal events. */ - virtual void handleSoftPedal (int midiChannel, bool isDown); - - /** Can be overridden to handle an incoming program change message. - The base class implementation of this has no effect, but you may want to make your - own synth react to program changes. - */ - virtual void handleProgramChange (int midiChannel, - int programNumber); - - //============================================================================== - /** Tells the synthesiser what the sample rate is for the audio it's being used to render. - - This value is propagated to the voices so that they can use it to render the correct - pitches. - */ - virtual void setCurrentPlaybackSampleRate (double sampleRate); - - /** Creates the next block of audio output. - - This will process the next numSamples of data from all the voices, and add that output - to the audio block supplied, starting from the offset specified. Note that the - data will be added to the current contents of the buffer, so you should clear it - before calling this method if necessary. - - The midi events in the inputMidi buffer are parsed for note and controller events, - and these are used to trigger the voices. Note that the startSample offset applies - both to the audio output buffer and the midi input buffer, so any midi events - with timestamps outside the specified region will be ignored. - */ - inline void renderNextBlock (AudioBuffer& outputAudio, - const MidiBuffer& inputMidi, - int startSample, - int numSamples) - { processNextBlock (outputAudio, inputMidi, startSample, numSamples); } - - inline void renderNextBlock (AudioBuffer& outputAudio, - const MidiBuffer& inputMidi, - int startSample, - int numSamples) - { processNextBlock (outputAudio, inputMidi, startSample, numSamples); } - - /** 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; } - - /** 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. - - If shouldBeStrict is true, the audio sub-blocks will strictly never be smaller than numSamples. - - If shouldBeStrict is false (default), the first audio sub-block in the buffer is allowed - to be smaller, to make sure that the first MIDI event in a buffer will always be sample-accurate - (this can sometimes help to avoid quantisation or phasing issues). - */ - void setMinimumRenderingSubdivisionSize (int numSamples, bool shouldBeStrict = false) noexcept; - -protected: - //============================================================================== - /** This is used to control access to the rendering callback and the note trigger methods. */ - CriticalSection lock; - - OwnedArray voices; - ReferenceCountedArray sounds; - - /** The last pitch-wheel values for each midi channel. */ - int lastPitchWheelValues [16]; - - /** Renders the voices for the given range. - By default this just calls renderNextBlock() on each voice, but you may need - to override it to handle custom cases. - */ - virtual void renderVoices (AudioBuffer& outputAudio, - int startSample, int numSamples); - virtual void renderVoices (AudioBuffer& outputAudio, - int startSample, int numSamples); - - /** Searches through the voices to find one that's not currently playing, and - which can play the given sound. - - Returns nullptr if all voices are busy and stealing isn't enabled. - - To implement a custom note-stealing algorithm, you can either override this - method, or (preferably) override findVoiceToSteal(). - */ - virtual SynthesiserVoice* findFreeVoice (SynthesiserSound* soundToPlay, - int midiChannel, - int midiNoteNumber, - bool stealIfNoneAvailable) const; - - /** Chooses a voice that is most suitable for being re-used. - 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. - */ - virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay, - int midiChannel, - int midiNoteNumber) const; - - /** Starts a specified voice playing a particular sound. - You'll probably never need to call this, it's used internally by noteOn(), but - may be needed by subclasses for custom behaviours. - */ - void startVoice (SynthesiserVoice* voice, - SynthesiserSound* sound, - int midiChannel, - int midiNoteNumber, - float velocity); - - /** Stops a given voice. - You should never need to call this, it's used internally by noteOff, but is protected - in case it's useful for some custom subclasses. It basically just calls through to - SynthesiserVoice::stopNote(), and has some assertions to sanity-check a few things. - */ - void stopVoice (SynthesiserVoice*, float velocity, bool allowTailOff); - - /** Can be overridden to do custom handling of incoming midi events. */ - virtual void handleMidiEvent (const MidiMessage&); - -private: - //============================================================================== - template - void processNextBlock (AudioBuffer& outputAudio, - const MidiBuffer& inputMidi, - int startSample, - int numSamples); - //============================================================================== - double sampleRate = 0; - uint32 lastNoteOnCounter = 0; - int minimumSubBlockSize = 32; - bool subBlockSubdivisionIsStrict = false; - bool shouldStealNotes = true; - BigInteger sustainPedalsDown; - - #if JUCE_CATCH_DEPRECATED_CODE_MISUSE - // Note the new parameters for these methods. - virtual int findFreeVoice (const bool) const { return 0; } - virtual int noteOff (int, int, int) { return 0; } - virtual int findFreeVoice (SynthesiserSound*, const bool) { return 0; } - virtual int findVoiceToSteal (SynthesiserSound*) const { return 0; } - #endif - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) -}; - -} // namespace juce diff --git a/source/native-plugins/audio-file.cpp b/source/native-plugins/audio-file.cpp index 0fd816e78..2a0f6ad22 100644 --- a/source/native-plugins/audio-file.cpp +++ b/source/native-plugins/audio-file.cpp @@ -127,7 +127,6 @@ protected: void process(float**, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override { const NativeTimeInfo* const timePos(getTimeInfo()); - const int iframes(static_cast(frames)); float* const out1(outBuffer[0]); float* const out2(outBuffer[1]); @@ -135,8 +134,8 @@ protected: if (fLength == 0 || ! fDoProcess) { //carla_stderr("P: no process"); - FloatVectorOperations::clear(out1, iframes); - FloatVectorOperations::clear(out2, iframes); + carla_zeroFloats(out1, iframes); + carla_zeroFloats(out2, iframes); return; } @@ -146,8 +145,8 @@ protected: if (! timePos->playing) { //carla_stderr("P: not playing"); - FloatVectorOperations::clear(out1, iframes); - FloatVectorOperations::clear(out2, iframes); + carla_zeroFloats(out1, iframes); + carla_zeroFloats(out2, iframes); const CarlaMutexLocker cml(fReaderMutex); @@ -167,8 +166,8 @@ protected: fReader->read(&fReaderBuffer, 0, iframes, nextReadPos, true, true); - FloatVectorOperations::copy(out1, fReaderBuffer.getReadPointer(0), iframes); - FloatVectorOperations::copy(out2, fReaderBuffer.getReadPointer(1), iframes); + carla_copyFloats(out1, fReaderBuffer.getReadPointer(0), frames); + carla_copyFloats(out2, fReaderBuffer.getReadPointer(1), frames); } // ------------------------------------------------------------------- diff --git a/source/native-plugins/bigmeter.cpp b/source/native-plugins/bigmeter.cpp index 2d7f6d700..f77e5c7aa 100644 --- a/source/native-plugins/bigmeter.cpp +++ b/source/native-plugins/bigmeter.cpp @@ -21,11 +21,9 @@ #include "CarlaNativeExtUI.hpp" #include "AppConfig.h" -#include "juce_audio_basics/juce_audio_basics.h" +#include "juce_core/juce_core.h" using juce::roundToIntAccurate; -using juce::FloatVectorOperations; -using juce::Range; // ----------------------------------------------------------------------- @@ -159,13 +157,8 @@ protected: void process(float** inputs, float**, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) override { - Range range; - - range = FloatVectorOperations::findMinAndMax(inputs[0], static_cast(frames)); - fOutLeft = carla_maxLimited(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f); - - range = FloatVectorOperations::findMinAndMax(inputs[1], static_cast(frames)); - fOutRight = carla_maxLimited(std::abs(range.getStart()), std::abs(range.getEnd()), 1.0f); + fOutLeft = carla_findMaxNormalizedFloat(inputs[0], frames); + fOutRight = carla_findMaxNormalizedFloat(inputs[1], frames); } private: diff --git a/source/native-plugins/midi-file.cpp b/source/native-plugins/midi-file.cpp index 3fcac5da1..82a42b9e8 100644 --- a/source/native-plugins/midi-file.cpp +++ b/source/native-plugins/midi-file.cpp @@ -19,8 +19,9 @@ #include "midi-base.hpp" #include "AppConfig.h" -#include "juce_audio_basics/juce_audio_basics.h" +#include "juce_core/juce_core.h" +#if 0 // ----------------------------------------------------------------------- class MidiFilePlugin : public NativePluginClass, @@ -238,13 +239,15 @@ static const NativePluginDescriptor midifileDesc = { // ----------------------------------------------------------------------- +#endif + CARLA_EXPORT void carla_register_native_plugin_midifile(); CARLA_EXPORT void carla_register_native_plugin_midifile() { - carla_register_native_plugin(&midifileDesc); + //carla_register_native_plugin(&midifileDesc); } // ----------------------------------------------------------------------- diff --git a/source/native-plugins/zita-at1.cpp b/source/native-plugins/zita-at1.cpp index f0964e6a7..661ac36f0 100644 --- a/source/native-plugins/zita-at1.cpp +++ b/source/native-plugins/zita-at1.cpp @@ -17,16 +17,15 @@ #include "CarlaNativeExtUI.hpp" #include "CarlaJuceUtils.hpp" - -#include "AppConfig.h" -#include "juce_audio_basics/juce_audio_basics.h" +#include "distrho/extra/ScopedPointer.hpp" #include "zita-at1/jclient.cc" #include "zita-at1/retuner.cc" +#include "AppConfig.h" +#include "juce_core/juce_core.h" + using juce::roundToIntAccurate; -using juce::FloatVectorOperations; -using juce::ScopedPointer; using namespace AT1; @@ -236,7 +235,7 @@ public: { if (! fJackClient.active) { - FloatVectorOperations::clear(outBuffer[0], static_cast(frames)); + carla_zeroFloats(outBuffer[0], frames); return; } diff --git a/source/native-plugins/zita-bls1.cpp b/source/native-plugins/zita-bls1.cpp index fa8758bcc..cdfa7c92a 100644 --- a/source/native-plugins/zita-bls1.cpp +++ b/source/native-plugins/zita-bls1.cpp @@ -17,18 +17,13 @@ #include "CarlaNativeExtUI.hpp" #include "CarlaJuceUtils.hpp" - -#include "AppConfig.h" -#include "juce_audio_basics/juce_audio_basics.h" +#include "distrho/extra/ScopedPointer.hpp" #include "zita-bls1/hp3filt.cc" #include "zita-bls1/jclient.cc" #include "zita-bls1/lfshelf2.cc" #include "zita-bls1/shuffler.cc" -using juce::FloatVectorOperations; -using juce::ScopedPointer; - using namespace BLS1; // ----------------------------------------------------------------------- @@ -196,10 +191,10 @@ public: { if (! fJackClient.active) { - const int iframes(static_cast(frames)); + const int iframes(frames); for (uint32_t i=0; i(frames)); + const int iframes(frames); for (uint32_t i=0; i(fBufferSize)); - efxoutl = new float[fBufferSize]; efxoutr = new float[fBufferSize]; - FloatVectorOperations::clear(efxoutl, ibufferSize); - FloatVectorOperations::clear(efxoutr, ibufferSize); + carla_zeroFloats(efxoutl, fBufferSize); + carla_zeroFloats(efxoutr, fBufferSize); std::memset(fParamsChanged, 0, sizeof(bool)*fParamCount); @@ -136,17 +133,15 @@ protected: void process(float** const inBuffer, float** const outBuffer, const uint32_t frames, const NativeMidiEvent* const, const uint32_t) final { - const int iframes(static_cast(frames)); - if (outBuffer[0] != inBuffer[0]) - FloatVectorOperations::copyWithMultiply(outBuffer[0], inBuffer[0], 0.5f, iframes); + carla_copyWithMultiply(outBuffer[0], inBuffer[0], 0.5f, frames); else - FloatVectorOperations::multiply(outBuffer[0], 0.5f, iframes); + carla_multiply(outBuffer[0], 0.5f, frames); if (outBuffer[1] != inBuffer[1]) - FloatVectorOperations::copyWithMultiply(outBuffer[1], inBuffer[1], 0.5f, iframes); + carla_copyWithMultiply(outBuffer[1], inBuffer[1], 0.5f, frames); else - FloatVectorOperations::multiply(outBuffer[1], 0.5f, iframes); + carla_multiply(outBuffer[1], 0.5f, frames); const int32_t nextProgram = fNextProgram; fNextProgram = -1; @@ -176,8 +171,8 @@ protected: fEffect->out(Stereo(inBuffer[0], inBuffer[1])); - FloatVectorOperations::addWithMultiply(outBuffer[0], efxoutl, 0.5f, iframes); - FloatVectorOperations::addWithMultiply(outBuffer[1], efxoutr, 0.5f, iframes); + carla_addWithMultiply(outBuffer[0], efxoutl, 0.5f, frames); + carla_addWithMultiply(outBuffer[1], efxoutr, 0.5f, frames); } // ------------------------------------------------------------------- @@ -189,14 +184,13 @@ protected: return; fBufferSize = bufferSize; - const int ibufferSize(static_cast(fBufferSize)); delete[] efxoutl; delete[] efxoutr; efxoutl = new float[bufferSize]; efxoutr = new float[bufferSize]; - FloatVectorOperations::clear(efxoutl, ibufferSize); - FloatVectorOperations::clear(efxoutr, ibufferSize); + carla_zeroFloats(efxoutl, bufferSize); + carla_zeroFloats(efxoutr, bufferSize); doReinit(false); } diff --git a/source/native-plugins/zynaddsubfx-synth.cpp b/source/native-plugins/zynaddsubfx-synth.cpp index e2d050f02..1bf3a3473 100644 --- a/source/native-plugins/zynaddsubfx-synth.cpp +++ b/source/native-plugins/zynaddsubfx-synth.cpp @@ -21,6 +21,7 @@ #include "LinkedList.hpp" #include "CarlaMathUtils.hpp" +#include "distrho/extra/ScopedPointer.hpp" #include "Misc/Master.h" #include "Misc/MiddleWare.h" @@ -32,11 +33,9 @@ #include #include "AppConfig.h" -#include "juce_audio_basics/juce_audio_basics.h" +#include "juce_core/juce_core.h" using juce::roundToIntAccurate; -using juce::FloatVectorOperations; -using juce::ScopedPointer; using namespace zyncarla; @@ -650,8 +649,8 @@ protected: { if (! isOffline()) { - FloatVectorOperations::clear(outBuffer[0], static_cast(frames)); - FloatVectorOperations::clear(outBuffer[1], static_cast(frames)); + carla_zeroFloats(outBuffer[0], frames); + carla_zeroFloats(outBuffer[1], frames); return; } diff --git a/source/plugin/Makefile b/source/plugin/Makefile index 98178183a..6cc1e84c4 100644 --- a/source/plugin/Makefile +++ b/source/plugin/Makefile @@ -50,7 +50,6 @@ endif LIBS = $(MODULEDIR)/carla_engine_plugin.a LIBS += $(MODULEDIR)/carla_plugin.a LIBS += $(MODULEDIR)/jackbridge.a -LIBS += $(MODULEDIR)/juce_audio_basics.a LIBS += $(MODULEDIR)/juce_core.a LIBS += $(MODULEDIR)/lilv.a LIBS += $(MODULEDIR)/native-plugins.a @@ -68,7 +67,6 @@ endif # Link flags LINK_FLAGS += $(JACKBRIDGE_LIBS) -LINK_FLAGS += $(JUCE_AUDIO_BASICS_LIBS) LINK_FLAGS += $(JUCE_CORE_LIBS) LINK_FLAGS += $(LILV_LIBS) LINK_FLAGS += $(NATIVE_PLUGINS_LIBS) diff --git a/source/plugin/carla-dssi.cpp b/source/plugin/carla-dssi.cpp deleted file mode 100644 index 5ad2170ab..000000000 --- a/source/plugin/carla-dssi.cpp +++ /dev/null @@ -1,1406 +0,0 @@ -/* - * Carla Native Plugins - * Copyright (C) 2013-2014 Filipe Coelho - * - * 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. - */ - -#define CARLA_NATIVE_PLUGIN_DSSI -#include "carla-native-base.cpp" - -#include "juce_audio_basics.h" -#include "juce_gui_basics.h" - -#include "CarlaString.hpp" - -#include "dssi/dssi.h" -#include "ladspa/ladspa.h" - -using namespace juce; - -// ----------------------------------------------------------------------- -// Juce Message Thread - -class JuceMessageThread : public Thread -{ -public: - JuceMessageThread() - : Thread("JuceMessageThread"), - fInitialised(false) - { - startThread(7); - - while (! fInitialised) - sleep(1); - } - - ~JuceMessageThread() - { - signalThreadShouldExit(); - JUCEApplication::quit(); - waitForThreadToExit(5000); - clearSingletonInstance(); - } - - void run() - { - initialiseJuce_GUI(); - fInitialised = true; - - MessageManager::getInstance()->setCurrentThreadAsMessageThread(); - - while ((! threadShouldExit()) && MessageManager::getInstance()->runDispatchLoopUntil(250)) - {} - } - - juce_DeclareSingleton(JuceMessageThread, false); - -private: - bool fInitialised; -}; - -juce_ImplementSingleton(JuceMessageThread) - -static Array gActivePlugins; - -// ----------------------------------------------------------------------- -// LV2 descriptor functions - -class NativePlugin : public LV2_External_UI_Widget -{ -public: - static const uint32_t kMaxMidiEvents = 512; - - NativePlugin(const NativePluginDescriptor* const desc, const double sampleRate, const char* const bundlePath, const LV2_Feature* const* features) - : fHandle(nullptr), - fDescriptor(desc), - fMidiEventCount(0), - fUiWasShown(false), - fIsProcessing(false), - fVolume(1.0f), - fDryWet(1.0f), - fBufferSize(0), - fSampleRate(sampleRate), - fUridMap(nullptr) - { - run = extui_run; - show = extui_show; - hide = extui_hide; - - CarlaString resourceDir(bundlePath); -#ifdef CARLA_OS_WIN - resourceDir += "\\resources\\"; -#else - resourceDir += "/resources/"; -#endif - - fHost.handle = this; - fHost.resourceDir = resourceDir.dup(); - fHost.uiName = nullptr; - - fHost.get_buffer_size = host_get_buffer_size; - fHost.get_sample_rate = host_get_sample_rate; - fHost.is_offline = host_is_offline; - fHost.get_time_info = host_get_time_info; - fHost.write_midi_event = host_write_midi_event; - fHost.ui_parameter_changed = host_ui_parameter_changed; - fHost.ui_custom_data_changed = host_ui_custom_data_changed; - fHost.ui_closed = host_ui_closed; - fHost.ui_open_file = host_ui_open_file; - fHost.ui_save_file = host_ui_save_file; - fHost.dispatcher = host_dispatcher; - - const LV2_Options_Option* options = nullptr; - const LV2_URID_Map* uridMap = nullptr; - const LV2_URID_Unmap* uridUnmap = nullptr; - - for (int i=0; features[i] != nullptr; ++i) - { - if (std::strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) - options = (const LV2_Options_Option*)features[i]->data; - else if (std::strcmp(features[i]->URI, LV2_URID__map) == 0) - uridMap = (const LV2_URID_Map*)features[i]->data; - else if (std::strcmp(features[i]->URI, LV2_URID__unmap) == 0) - uridUnmap = (const LV2_URID_Unmap*)features[i]->data; - } - - if (options == nullptr || uridMap == nullptr) - { - carla_stderr("Host doesn't provides option or urid-map features"); - return; - } - - for (int i=0; options[i].key != 0; ++i) - { - if (uridUnmap != nullptr) - { - carla_debug("Host option %i:\"%s\"", i, uridUnmap->unmap(uridUnmap->handle, options[i].key)); - } - - if (options[i].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength)) - { - if (options[i].type == uridMap->map(uridMap->handle, LV2_ATOM__Int)) - { - fBufferSize = *(const int*)options[i].value; - - if (fBufferSize == 0) - carla_stderr("Host provides maxBlockLength but has null value"); - } - else - carla_stderr("Host provides maxBlockLength but has wrong value type"); - - break; - } - } - - fUridMap = uridMap; - - if (fDescriptor->midiIns > 0) - fUI.portOffset += desc->midiIns; - else if (fDescriptor->hints & PLUGIN_USES_TIME) - fUI.portOffset += 1; - - fUI.portOffset += desc->midiOuts; - fUI.portOffset += 1; // freewheel - fUI.portOffset += desc->audioIns; - fUI.portOffset += desc->audioOuts; - } - - ~NativePlugin() - { - CARLA_ASSERT(fHandle == nullptr); - CARLA_ASSERT(! fUiWasShown); - - if (fHost.resourceDir != nullptr) - { - delete[] fHost.resourceDir; - fHost.resourceDir = nullptr; - } - } - - bool init() - { - if (fDescriptor->instantiate == nullptr || fDescriptor->process == nullptr) - { - carla_stderr("Plugin is missing something..."); - return false; - } - if (fBufferSize == 0) - { - carla_stderr("Host is missing bufferSize feature"); - //return false; - // as testing, continue for now - fBufferSize = 1024; - } - - fHandle = fDescriptor->instantiate(&fHost); - - if (fHandle == nullptr) - return false; - - carla_zeroStructs(fMidiEvents, kMaxMidiEvents*2); - carla_zeroStruct(fTimeInfo); - - fPorts.init(fDescriptor, fHandle); - fUris.map(fUridMap); - - return true; - } - - // ------------------------------------------------------------------- - // LV2 functions - - void lv2_connect_port(const uint32_t port, void* const dataLocation) - { - fPorts.connectPort(fDescriptor, port, dataLocation); - } - - void lv2_activate() - { - if (fDescriptor->activate != nullptr) - fDescriptor->activate(fHandle); - - carla_zeroStruct(fTimeInfo); - } - - void lv2_deactivate() - { - if (fDescriptor->deactivate != nullptr) - fDescriptor->deactivate(fHandle); - } - - void lv2_cleanup() - { - if (fDescriptor->cleanup != nullptr) - fDescriptor->cleanup(fHandle); - - fHandle = nullptr; - - if (fUiWasShown) - { - CARLA_SAFE_ASSERT_RETURN(gActivePlugins.contains(this),); - - gActivePlugins.removeFirstMatchingValue(this); - - JUCE_AUTORELEASEPOOL - { - MessageManagerLock mmLock; - - if (gActivePlugins.size() == 0) - { - JuceMessageThread::deleteInstance(); - shutdownJuce_GUI(); - } - } - - fUiWasShown = false; - } - } - - void lv2_run(const uint32_t frames) - { - if (frames == 0) - { - updateParameterOutputs(); - return; - } - - // Check for updated parameters - float curValue; - - for (uint32_t i=0; i < fPorts.paramCount; ++i) - { - CARLA_SAFE_ASSERT_CONTINUE(fPorts.paramsPtr[i] != nullptr) - - curValue = *fPorts.paramsPtr[i]; - - if (fPorts.paramsLast[i] != curValue && (fDescriptor->get_parameter_info(fHandle, i)->hints & PARAMETER_IS_OUTPUT) == 0) - { - fPorts.paramsLast[i] = curValue; - fDescriptor->set_parameter_value(fHandle, i, curValue); - } - } - - if (fDescriptor->midiIns > 0 || (fDescriptor->hints & PLUGIN_USES_TIME) != 0) - { - fMidiEventCount = 0; - carla_zeroStructs(fMidiEvents, kMaxMidiEvents*2); - - LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[0], iter) - { - const LV2_Atom_Event* const event((const LV2_Atom_Event*)iter); - - if (event == nullptr) - continue; - if (event->body.size > 4) - continue; - if (event->time.frames >= frames) - break; - - if (event->body.type == fUris.midiEvent) - { - if (fMidiEventCount >= kMaxMidiEvents*2) - continue; - - const uint8_t* const data((const uint8_t*)(event + 1)); - - fMidiEvents[fMidiEventCount].port = 0; - fMidiEvents[fMidiEventCount].time = (uint32_t)event->time.frames; - fMidiEvents[fMidiEventCount].size = (uint8_t)event->body.size; - - for (uint32_t i=0; i < event->body.size; ++i) - fMidiEvents[fMidiEventCount].data[i] = data[i]; - - fMidiEventCount += 1; - continue; - } - - if (event->body.type == fUris.atomBlank) - { - const LV2_Atom_Object* const obj((const LV2_Atom_Object*)&event->body); - - if (obj->body.otype != fUris.timePos) - continue; - - LV2_Atom* bar = nullptr; - LV2_Atom* barBeat = nullptr; - LV2_Atom* beatsPerBar = nullptr; - LV2_Atom* bpm = nullptr; - LV2_Atom* beatUnit = nullptr; - LV2_Atom* frame = nullptr; - LV2_Atom* speed = nullptr; - - lv2_atom_object_get(obj, - fUris.timeBar, &bar, - fUris.timeBarBeat, &barBeat, - fUris.timeBeatsPerBar, &beatsPerBar, - fUris.timeBeatsPerMinute, &bpm, - fUris.timeBeatUnit, &beatUnit, - fUris.timeFrame, &frame, - fUris.timeSpeed, &speed, - nullptr); - - if (bpm != nullptr && bpm->type == fUris.atomFloat) - { - fTimeInfo.bbt.beatsPerMinute = ((LV2_Atom_Float*)bpm)->body; - fTimeInfo.bbt.valid = true; - } - - if (beatsPerBar != nullptr && beatsPerBar->type == fUris.atomFloat) - { - float beatsPerBarValue = ((LV2_Atom_Float*)beatsPerBar)->body; - fTimeInfo.bbt.beatsPerBar = beatsPerBarValue; - - if (bar != nullptr && bar->type == fUris.atomLong) - { - //float barValue = ((LV2_Atom_Long*)bar)->body; - //curPosInfo.ppqPositionOfLastBarStart = barValue * beatsPerBarValue; - - if (barBeat != nullptr && barBeat->type == fUris.atomFloat) - { - //float barBeatValue = ((LV2_Atom_Float*)barBeat)->body; - //curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + barBeatValue; - } - } - } - - if (beatUnit != nullptr && beatUnit->type == fUris.atomFloat) - fTimeInfo.bbt.beatType = ((LV2_Atom_Float*)beatUnit)->body; - - if (frame != nullptr && frame->type == fUris.atomLong) - fTimeInfo.frame = ((LV2_Atom_Long*)frame)->body; - - if (speed != nullptr && speed->type == fUris.atomFloat) - fTimeInfo.playing = ((LV2_Atom_Float*)speed)->body == 1.0f; - - continue; - } - } - - for (uint32_t i=1; i < fDescriptor->midiIns; ++i) - { - LV2_ATOM_SEQUENCE_FOREACH(fPorts.eventsIn[i], iter) - { - const LV2_Atom_Event* const event((const LV2_Atom_Event*)iter); - - if (event == nullptr) - continue; - if (event->body.type != fUris.midiEvent) - continue; - if (event->body.size > 4) - continue; - if (event->time.frames >= frames) - break; - if (fMidiEventCount >= kMaxMidiEvents*2) - break; - - const uint8_t* const data((const uint8_t*)(event + 1)); - - fMidiEvents[fMidiEventCount].port = (uint8_t)i; - fMidiEvents[fMidiEventCount].size = (uint8_t)event->body.size; - fMidiEvents[fMidiEventCount].time = (uint32_t)event->time.frames; - - for (uint32_t j=0; j < event->body.size; ++j) - fMidiEvents[fMidiEventCount].data[j] = data[j]; - - fMidiEventCount += 1; - } - } - } - - fIsProcessing = true; - fDescriptor->process(fHandle, fPorts.audioIns, fPorts.audioOuts, frames, fMidiEvents, fMidiEventCount); - fIsProcessing = false; - - if (fDryWet != 1.0f && fDescriptor->audioIns == fDescriptor->audioOuts) - { - for (uint32_t i=0; i < fDescriptor->audioOuts; ++i) - { - FloatVectorOperations::multiply(fPorts.audioIns[i], fVolume*(1.0f-fDryWet), frames); - FloatVectorOperations::multiply(fPorts.audioOuts[i], fVolume*fDryWet, frames); - FloatVectorOperations::add(fPorts.audioOuts[i], fPorts.audioIns[i], frames); - } - } - else if (fVolume != 1.0f) - { - for (uint32_t i=0; i < fDescriptor->audioOuts; ++i) - FloatVectorOperations::multiply(fPorts.audioOuts[i], fVolume, frames); - } - - // TODO - midi out - - updateParameterOutputs(); - } - - // ------------------------------------------------------------------- - - uint32_t lv2_get_options(LV2_Options_Option* const /*options*/) const - { - // currently unused - return LV2_OPTIONS_SUCCESS; - } - - uint32_t lv2_set_options(const LV2_Options_Option* const options) - { - for (int i=0; options[i].key != 0; ++i) - { - if (options[i].key == fUridMap->map(fUridMap->handle, LV2_BUF_SIZE__maxBlockLength)) - { - if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Int)) - { - fBufferSize = *(const int*)options[i].value; - - if (fDescriptor->dispatcher != nullptr) - fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_BUFFER_SIZE_CHANGED, 0, fBufferSize, nullptr, 0.0f); - } - else - carla_stderr("Host changed maxBlockLength but with wrong value type"); - } - else if (options[i].key == fUridMap->map(fUridMap->handle, LV2_CORE__sampleRate)) - { - if (options[i].type == fUridMap->map(fUridMap->handle, LV2_ATOM__Double)) - { - fSampleRate = *(const double*)options[i].value; - - if (fDescriptor->dispatcher != nullptr) - fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, (float)fSampleRate); - } - else - carla_stderr("Host changed sampleRate but with wrong value type"); - } - } - - return LV2_OPTIONS_SUCCESS; - } - - const LV2_Program_Descriptor* lv2_get_program(const uint32_t index) - { - if (fDescriptor->category == PLUGIN_CATEGORY_SYNTH) - return nullptr; - if (fDescriptor->get_midi_program_count == nullptr) - return nullptr; - if (fDescriptor->get_midi_program_info == nullptr) - return nullptr; - if (index >= fDescriptor->get_midi_program_count(fHandle)) - return nullptr; - - const NativeMidiProgram* const midiProg(fDescriptor->get_midi_program_info(fHandle, index)); - - if (midiProg == nullptr) - return nullptr; - - fProgramDesc.bank = midiProg->bank; - fProgramDesc.program = midiProg->program; - fProgramDesc.name = midiProg->name; - - return &fProgramDesc; - } - - void lv2_select_program(uint32_t bank, uint32_t program) - { - if (fDescriptor->category == PLUGIN_CATEGORY_SYNTH) - return; - if (fDescriptor->set_midi_program == nullptr) - return; - - fDescriptor->set_midi_program(fHandle, 0, bank, program); - } - - LV2_State_Status lv2_save(const LV2_State_Store_Function store, const LV2_State_Handle handle, const uint32_t /*flags*/, const LV2_Feature* const* const /*features*/) const - { - if ((fDescriptor->hints & PLUGIN_USES_STATE) == 0 || fDescriptor->get_state == nullptr) - return LV2_STATE_ERR_NO_FEATURE; - - if (char* const state = fDescriptor->get_state(fHandle)) - { - store(handle, fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), state, std::strlen(state), fUris.atomString, LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); - std::free(state); - return LV2_STATE_SUCCESS; - } - - return LV2_STATE_ERR_UNKNOWN; - } - - LV2_State_Status lv2_restore(const LV2_State_Retrieve_Function retrieve, const LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* const /*features*/) const - { - if ((fDescriptor->hints & PLUGIN_USES_STATE) == 0 || fDescriptor->set_state == nullptr) - return LV2_STATE_ERR_NO_FEATURE; - - size_t size = 0; - uint32_t type = 0; - const void* data = retrieve(handle, fUridMap->map(fUridMap->handle, "http://kxstudio.sf.net/ns/carla/chunk"), &size, &type, &flags); - - if (size == 0) - return LV2_STATE_ERR_UNKNOWN; - if (type == 0) - return LV2_STATE_ERR_UNKNOWN; - if (data == nullptr) - return LV2_STATE_ERR_UNKNOWN; - if (type != fUris.atomString) - return LV2_STATE_ERR_BAD_TYPE; - - fDescriptor->set_state(fHandle, (const char*)data); - - return LV2_STATE_SUCCESS; - } - - // ------------------------------------------------------------------- - - bool lv2ui_instantiate(LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) - { - for (int i=0; features[i] != nullptr; ++i) - { - if (std::strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0 || - std::strcmp(features[i]->URI, LV2_EXTERNAL_UI_DEPRECATED_URI) == 0) - { - fUI.host = (const LV2_External_UI_Host*)features[i]->data; - break; - } - } - - if (fUI.host == nullptr) - return false; - - fUI.writeFunction = writeFunction; - fUI.controller = controller; - *widget = this; - - fHost.uiName = fUI.host->plugin_human_id; - - return true; - } - - void lv2ui_port_event(uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) const - { - if (format != 0 || bufferSize != sizeof(float) || buffer == nullptr) - return; - if (portIndex >= fUI.portOffset || ! fUI.isVisible) - return; - if (fDescriptor->ui_set_parameter_value == nullptr) - return; - - const float value(*(const float*)buffer); - fDescriptor->ui_set_parameter_value(fHandle, portIndex-fUI.portOffset, value); - } - - void lv2ui_cleanup() - { - fUI.host = nullptr; - fUI.writeFunction = nullptr; - fUI.controller = nullptr; - - if (! fUI.isVisible) - return; - - if (fDescriptor->ui_show != nullptr) - fDescriptor->ui_show(fHandle, false); - - fUI.isVisible = false; - } - - // ------------------------------------------------------------------- - - void lv2ui_select_program(uint32_t bank, uint32_t program) const - { - if (fDescriptor->category == PLUGIN_CATEGORY_SYNTH) - return; - if (fDescriptor->ui_set_midi_program == nullptr) - return; - - fDescriptor->ui_set_midi_program(fHandle, 0, bank, program); - } - - // ------------------------------------------------------------------- - -protected: - void handleUiRun() - { - if (fDescriptor->ui_idle != nullptr) - fDescriptor->ui_idle(fHandle); - } - - void handleUiShow() - { - if (fDescriptor->ui_show != nullptr) - { - if (fDescriptor->hints & PLUGIN_NEEDS_UI_JUCE) - { - JUCE_AUTORELEASEPOOL - { - if (gActivePlugins.size() == 0) - { - initialiseJuce_GUI(); - JuceMessageThread::getInstance(); - } - } - - fDescriptor->ui_show(fHandle, true); - - fUiWasShown = true; - gActivePlugins.add(this); - } - else - fDescriptor->ui_show(fHandle, true); - } - - fUI.isVisible = true; - } - - void handleUiHide() - { - if (fDescriptor->ui_show != nullptr) - fDescriptor->ui_show(fHandle, false); - - fUI.isVisible = false; - } - - // ------------------------------------------------------------------- - - uint32_t handleGetBufferSize() const - { - return fBufferSize; - } - - double handleGetSampleRate() const - { - return fSampleRate; - } - - bool handleIsOffline() const - { - CARLA_SAFE_ASSERT_RETURN(fIsProcessing, false); - - return (fPorts.freewheel != nullptr && *fPorts.freewheel >= 0.5f); - } - - const NativeTimeInfo* handleGetTimeInfo() const - { - CARLA_SAFE_ASSERT_RETURN(fIsProcessing, nullptr); - - return &fTimeInfo; - } - - bool handleWriteMidiEvent(const NativeMidiEvent* const event) - { - CARLA_SAFE_ASSERT_RETURN(fIsProcessing, false); - CARLA_SAFE_ASSERT_RETURN(fDescriptor->midiOuts > 0, false); - CARLA_SAFE_ASSERT_RETURN(event != nullptr, false); - CARLA_SAFE_ASSERT_RETURN(event->data[0] != 0, false); - - // reverse-find first free event, and put it there - for (uint32_t i=(kMaxMidiEvents*2)-1; i > fMidiEventCount; --i) - { - if (fMidiEvents[i].data[0] == 0) - { - std::memcpy(&fMidiEvents[i], event, sizeof(NativeMidiEvent)); - return true; - } - } - - return false; - } - - void handleUiParameterChanged(const uint32_t index, const float value) const - { - if (fUI.writeFunction != nullptr && fUI.controller != nullptr) - fUI.writeFunction(fUI.controller, index+fUI.portOffset, sizeof(float), 0, &value); - } - - void handleUiCustomDataChanged(const char* const /*key*/, const char* const /*value*/) const - { - //storeCustomData(key, value); - } - - void handleUiClosed() - { - if (fUI.host != nullptr && fUI.host->ui_closed != nullptr && fUI.controller != nullptr) - fUI.host->ui_closed(fUI.controller); - - fUI.host = nullptr; - fUI.writeFunction = nullptr; - fUI.controller = nullptr; - fUI.isVisible = false; - } - - const char* handleUiOpenFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const - { - // TODO - return nullptr; - } - - const char* handleUiSaveFile(const bool /*isDir*/, const char* const /*title*/, const char* const /*filter*/) const - { - // TODO - return nullptr; - } - - intptr_t handleDispatcher(const NativeHostDispatcherOpcode opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) - { - carla_debug("NativePlugin::handleDispatcher(%i, %i, " P_INTPTR ", %p, %f)", opcode, index, value, ptr, opt); - - intptr_t ret = 0; - - switch (opcode) - { - case HOST_OPCODE_NULL: - break; - case HOST_OPCODE_SET_VOLUME: - fVolume = opt; - break; - case HOST_OPCODE_SET_DRYWET: - fDryWet = opt; - break; - case HOST_OPCODE_SET_BALANCE_LEFT: - case HOST_OPCODE_SET_BALANCE_RIGHT: - case HOST_OPCODE_SET_PANNING: - // nothing - break; - case HOST_OPCODE_GET_PARAMETER_MIDI_CC: - case HOST_OPCODE_SET_PARAMETER_MIDI_CC: - case HOST_OPCODE_SET_PROCESS_PRECISION: - case HOST_OPCODE_UPDATE_PARAMETER: - case HOST_OPCODE_UPDATE_MIDI_PROGRAM: - case HOST_OPCODE_RELOAD_PARAMETERS: - case HOST_OPCODE_RELOAD_MIDI_PROGRAMS: - case HOST_OPCODE_RELOAD_ALL: - // nothing - break; - case HOST_OPCODE_UI_UNAVAILABLE: - handleUiClosed(); - break; - } - - return ret; - - // unused for now - (void)index; - (void)value; - (void)ptr; - } - - void updateParameterOutputs() - { - for (uint32_t i=0; i < fPorts.paramCount; ++i) - { - if (fDescriptor->get_parameter_info(fHandle, i)->hints & PARAMETER_IS_OUTPUT) - { - fPorts.paramsLast[i] = fDescriptor->get_parameter_value(fHandle, i); - - if (fPorts.paramsPtr[i] != nullptr) - *fPorts.paramsPtr[i] = fPorts.paramsLast[i]; - } - } - } - - // ------------------------------------------------------------------- - -private: - // Native data - NativePluginHandle fHandle; - NativeHostDescriptor fHost; - const NativePluginDescriptor* const fDescriptor; - LV2_Program_Descriptor fProgramDesc; - - uint32_t fMidiEventCount; - NativeMidiEvent fMidiEvents[kMaxMidiEvents*2]; - NativeTimeInfo fTimeInfo; - - bool fUiWasShown; - bool fIsProcessing; - float fVolume; - float fDryWet; - - // Lv2 host data - uint32_t fBufferSize; - double fSampleRate; - - const LV2_URID_Map* fUridMap; - - struct URIDs { - LV2_URID atomBlank; - LV2_URID atomFloat; - LV2_URID atomLong; - LV2_URID atomSequence; - LV2_URID atomString; - LV2_URID midiEvent; - LV2_URID timePos; - LV2_URID timeBar; - LV2_URID timeBarBeat; - LV2_URID timeBeatsPerBar; - LV2_URID timeBeatsPerMinute; - LV2_URID timeBeatUnit; - LV2_URID timeFrame; - LV2_URID timeSpeed; - - URIDs() - : atomBlank(0), - atomFloat(0), - atomLong(0), - atomSequence(0), - atomString(0), - midiEvent(0), - timePos(0), - timeBar(0), - timeBarBeat(0), - timeBeatsPerBar(0), - timeBeatsPerMinute(0), - timeBeatUnit(0), - timeFrame(0), - timeSpeed(0) {} - - void map(const LV2_URID_Map* const uridMap) - { - atomBlank = uridMap->map(uridMap->handle, LV2_ATOM__Blank); - atomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float); - atomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long); - atomSequence = uridMap->map(uridMap->handle, LV2_ATOM__Sequence); - atomString = uridMap->map(uridMap->handle, LV2_ATOM__String); - midiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent); - timePos = uridMap->map(uridMap->handle, LV2_TIME__Position); - timeBar = uridMap->map(uridMap->handle, LV2_TIME__bar); - timeBarBeat = uridMap->map(uridMap->handle, LV2_TIME__barBeat); - timeBeatUnit = uridMap->map(uridMap->handle, LV2_TIME__beatUnit); - timeFrame = uridMap->map(uridMap->handle, LV2_TIME__frame); - timeSpeed = uridMap->map(uridMap->handle, LV2_TIME__speed); - timeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar); - timeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute); - } - } fUris; - - struct UI { - const LV2_External_UI_Host* host; - LV2UI_Write_Function writeFunction; - LV2UI_Controller controller; - uint32_t portOffset; - bool isVisible; - - UI() - : host(nullptr), - writeFunction(nullptr), - controller(nullptr), - portOffset(0), - isVisible(false) {} - } fUI; - - struct Ports { - LV2_Atom_Sequence** eventsIn; - LV2_Atom_Sequence** midiOuts; - float** audioIns; - float** audioOuts; - float* freewheel; - uint32_t paramCount; - float* paramsLast; - float** paramsPtr; - - Ports() - : eventsIn(nullptr), - midiOuts(nullptr), - audioIns(nullptr), - audioOuts(nullptr), - freewheel(nullptr), - paramCount(0), - paramsLast(nullptr), - paramsPtr(nullptr) {} - - ~Ports() - { - if (eventsIn != nullptr) - { - delete[] eventsIn; - eventsIn = nullptr; - } - - if (midiOuts != nullptr) - { - delete[] midiOuts; - midiOuts = nullptr; - } - - if (audioIns != nullptr) - { - delete[] audioIns; - audioIns = nullptr; - } - - if (audioOuts != nullptr) - { - delete[] audioOuts; - audioOuts = nullptr; - } - - if (paramsLast != nullptr) - { - delete[] paramsLast; - paramsLast = nullptr; - } - - if (paramsPtr != nullptr) - { - delete[] paramsPtr; - paramsPtr = nullptr; - } - } - - void init(const NativePluginDescriptor* const desc, NativePluginHandle handle) - { - CARLA_SAFE_ASSERT_RETURN(desc != nullptr && handle != nullptr,) - - if (desc->midiIns > 0) - { - eventsIn = new LV2_Atom_Sequence*[desc->midiIns]; - - for (uint32_t i=0; i < desc->midiIns; ++i) - eventsIn[i] = nullptr; - } - else if (desc->hints & PLUGIN_USES_TIME) - { - eventsIn = new LV2_Atom_Sequence*[1]; - eventsIn[0] = nullptr; - } - - if (desc->midiOuts > 0) - { - midiOuts = new LV2_Atom_Sequence*[desc->midiOuts]; - - for (uint32_t i=0; i < desc->midiOuts; ++i) - midiOuts[i] = nullptr; - } - - if (desc->audioIns > 0) - { - audioIns = new float*[desc->audioIns]; - - for (uint32_t i=0; i < desc->audioIns; ++i) - audioIns[i] = nullptr; - } - - if (desc->audioOuts > 0) - { - audioOuts = new float*[desc->audioOuts]; - - for (uint32_t i=0; i < desc->audioOuts; ++i) - audioOuts[i] = nullptr; - } - - if (desc->get_parameter_count != nullptr && desc->get_parameter_info != nullptr && desc->get_parameter_value != nullptr && desc->set_parameter_value != nullptr) - { - paramCount = desc->get_parameter_count(handle); - - if (paramCount > 0) - { - paramsLast = new float[paramCount]; - paramsPtr = new float*[paramCount]; - - for (uint32_t i=0; i < paramCount; ++i) - { - paramsLast[i] = desc->get_parameter_value(handle, i); - paramsPtr[i] = nullptr; - } - } - } - } - - void connectPort(const NativePluginDescriptor* const desc, const uint32_t port, void* const dataLocation) - { - uint32_t index = 0; - - if (desc->midiIns > 0 || (desc->hints & PLUGIN_USES_TIME) != 0) - { - if (port == index++) - { - eventsIn[0] = (LV2_Atom_Sequence*)dataLocation; - return; - } - } - - for (uint32_t i=1; i < desc->midiIns; ++i) - { - if (port == index++) - { - eventsIn[i] = (LV2_Atom_Sequence*)dataLocation; - return; - } - } - - for (uint32_t i=0; i < desc->midiOuts; ++i) - { - if (port == index++) - { - midiOuts[i] = (LV2_Atom_Sequence*)dataLocation; - return; - } - } - - if (port == index++) - { - freewheel = (float*)dataLocation; - return; - } - - for (uint32_t i=0; i < desc->audioIns; ++i) - { - if (port == index++) - { - audioIns[i] = (float*)dataLocation; - return; - } - } - - for (uint32_t i=0; i < desc->audioOuts; ++i) - { - if (port == index++) - { - audioOuts[i] = (float*)dataLocation; - return; - } - } - - for (uint32_t i=0; i < paramCount; ++i) - { - if (port == index++) - { - paramsPtr[i] = (float*)dataLocation; - return; - } - } - } - } fPorts; - - // ------------------------------------------------------------------- - - #define handlePtr ((NativePlugin*)_this_) - - static void extui_run(LV2_External_UI_Widget* _this_) - { - handlePtr->handleUiRun(); - } - - static void extui_show(LV2_External_UI_Widget* _this_) - { - handlePtr->handleUiShow(); - } - - static void extui_hide(LV2_External_UI_Widget* _this_) - { - handlePtr->handleUiHide(); - } - - #undef handlePtr - - // ------------------------------------------------------------------- - - #define handlePtr ((NativePlugin*)handle) - - static uint32_t host_get_buffer_size(NativeHostHandle handle) - { - return handlePtr->handleGetBufferSize(); - } - - static double host_get_sample_rate(NativeHostHandle handle) - { - return handlePtr->handleGetSampleRate(); - } - - static bool host_is_offline(NativeHostHandle handle) - { - return handlePtr->handleIsOffline(); - } - - static const NativeTimeInfo* host_get_time_info(NativeHostHandle handle) - { - return handlePtr->handleGetTimeInfo(); - } - - static bool host_write_midi_event(NativeHostHandle handle, const NativeMidiEvent* event) - { - return handlePtr->handleWriteMidiEvent(event); - } - - static void host_ui_parameter_changed(NativeHostHandle handle, uint32_t index, float value) - { - handlePtr->handleUiParameterChanged(index, value); - } - - static void host_ui_custom_data_changed(NativeHostHandle handle, const char* key, const char* value) - { - handlePtr->handleUiCustomDataChanged(key, value); - } - - static void host_ui_closed(NativeHostHandle handle) - { - handlePtr->handleUiClosed(); - } - - static const char* host_ui_open_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter) - { - return handlePtr->handleUiOpenFile(isDir, title, filter); - } - - static const char* host_ui_save_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter) - { - return handlePtr->handleUiSaveFile(isDir, title, filter); - } - - static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt) - { - return handlePtr->handleDispatcher(opcode, index, value, ptr, opt); - } - - #undef handlePtr - - CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NativePlugin) -}; - -// ----------------------------------------------------------------------- -// LV2 plugin descriptor functions - -static LV2_Handle lv2_instantiate(const LV2_Descriptor* lv2Descriptor, double sampleRate, const char* bundlePath, const LV2_Feature* const* features) -{ - carla_debug("lv2_instantiate(%p, %g, %s, %p)", lv2Descriptor, sampleRate, bundlePath, features); - - const NativePluginDescriptor* pluginDesc = nullptr; - const char* pluginLabel = nullptr; - - if (std::strncmp(lv2Descriptor->URI, "http://kxstudio.sf.net/carla/plugins/", 37) == 0) - pluginLabel = lv2Descriptor->URI+37; - - if (pluginLabel == nullptr) - { - carla_stderr("Failed to find carla native plugin with URI \"%s\"", lv2Descriptor->URI); - return nullptr; - } - - carla_debug("lv2_instantiate() - looking up label \"%s\"", pluginLabel); - - PluginListManager& plm(PluginListManager::getInstance()); - - for (LinkedList::Itenerator it = plm.descs.begin(); it.valid(); it.next()) - { - const NativePluginDescriptor* const& tmpDesc(it.getValue()); - - if (std::strcmp(tmpDesc->label, pluginLabel) == 0) - { - pluginDesc = tmpDesc; - break; - } - } - - if (pluginDesc == nullptr) - { - carla_stderr("Failed to find carla native plugin with label \"%s\"", pluginLabel); - return nullptr; - } - - NativePlugin* const plugin(new NativePlugin(pluginDesc, sampleRate, bundlePath, features)); - - if (! plugin->init()) - { - carla_stderr("Failed to init plugin"); - delete plugin; - return nullptr; - } - - return (LV2_Handle)plugin; -} - -#define instancePtr ((NativePlugin*)instance) - -static void lv2_connect_port(LV2_Handle instance, uint32_t port, void* dataLocation) -{ - instancePtr->lv2_connect_port(port, dataLocation); -} - -static void lv2_activate(LV2_Handle instance) -{ - carla_debug("lv2_activate(%p)", instance); - instancePtr->lv2_activate(); -} - -static void lv2_run(LV2_Handle instance, uint32_t sampleCount) -{ - instancePtr->lv2_run(sampleCount); -} - -static void lv2_deactivate(LV2_Handle instance) -{ - carla_debug("lv2_deactivate(%p)", instance); - instancePtr->lv2_deactivate(); -} - -static void lv2_cleanup(LV2_Handle instance) -{ - carla_debug("lv2_cleanup(%p)", instance); - instancePtr->lv2_cleanup(); - delete instancePtr; -} - -static uint32_t lv2_get_options(LV2_Handle instance, LV2_Options_Option* options) -{ - carla_debug("lv2_get_options(%p, %p)", instance, options); - return instancePtr->lv2_get_options(options); -} - -static uint32_t lv2_set_options(LV2_Handle instance, const LV2_Options_Option* options) -{ - carla_debug("lv2_set_options(%p, %p)", instance, options); - return instancePtr->lv2_set_options(options); -} - -static const LV2_Program_Descriptor* lv2_get_program(LV2_Handle instance, uint32_t index) -{ - carla_debug("lv2_get_program(%p, %i)", instance, index); - return instancePtr->lv2_get_program(index); -} - -static void lv2_select_program(LV2_Handle instance, uint32_t bank, uint32_t program) -{ - carla_debug("lv2_select_program(%p, %i, %i)", instance, bank, program); - return instancePtr->lv2_select_program(bank, program); -} - -static LV2_State_Status lv2_save(LV2_Handle instance, LV2_State_Store_Function store, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features) -{ - carla_debug("lv2_save(%p, %p, %p, %i, %p)", instance, store, handle, flags, features); - return instancePtr->lv2_save(store, handle, flags, features); -} - -static LV2_State_Status lv2_restore(LV2_Handle instance, LV2_State_Retrieve_Function retrieve, LV2_State_Handle handle, uint32_t flags, const LV2_Feature* const* features) -{ - carla_debug("lv2_restore(%p, %p, %p, %i, %p)", instance, retrieve, handle, flags, features); - return instancePtr->lv2_restore(retrieve, handle, flags, features); -} - -#undef instancePtr - -// ----------------------------------------------------------------------- -// LV2 UI descriptor functions - -static LV2UI_Handle lv2ui_instantiate(const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction, - LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) -{ - carla_debug("lv2ui_instantiate(..., %p, %p, %p)", writeFunction, controller, widget, features); - - NativePlugin* plugin = nullptr; - - for (int i=0; features[i] != nullptr; ++i) - { - if (std::strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0) - { - plugin = (NativePlugin*)features[i]->data; - break; - } - } - - if (plugin == nullptr) - { - carla_stderr("Host doesn't support instance-access, cannot show UI"); - return nullptr; - } - - if (! plugin->lv2ui_instantiate(writeFunction, controller, widget, features)) - { - carla_stderr("Host doesn't support external UI"); - return nullptr; - } - - return (LV2UI_Handle)plugin; -} - -#define uiPtr ((NativePlugin*)ui) - -static void lv2ui_port_event(LV2UI_Handle ui, uint32_t portIndex, uint32_t bufferSize, uint32_t format, const void* buffer) -{ - carla_debug("lv2ui_port_event(%p, %i, %i, %i, %p)", ui, portIndex, bufferSize, format, buffer); - uiPtr->lv2ui_port_event(portIndex, bufferSize, format, buffer); -} - -static void lv2ui_cleanup(LV2UI_Handle ui) -{ - carla_debug("lv2ui_cleanup(%p)", ui); - uiPtr->lv2ui_cleanup(); -} - -static void lv2ui_select_program(LV2UI_Handle ui, uint32_t bank, uint32_t program) -{ - carla_debug("lv2ui_select_program(%p, %i, %i)", ui, bank, program); - uiPtr->lv2ui_select_program(bank, program); -} - -#undef uiPtr - -// ----------------------------------------------------------------------- -// Startup code - -CARLA_EXPORT -const DSSI_Descriptor* dssi_descriptor(ulong index) -{ - carla_debug("dssi_descriptor(%i)", index); - - PluginListManager& plm(PluginListManager::getInstance()); - - if (index >= plm.descs.count()) - { - carla_debug("dssi_descriptor(%i) - out of bounds", index); - return nullptr; - } - if (index < plm.dssiDescs.count()) - { - carla_debug("lv2_descriptor(%i) - found previously allocated", index); - return plm.dssiDescs.getAt(index, nullptr); - } - - const NativePluginDescriptor* const pluginDesc(plm.descs.getAt(index, nullptr)); - CARLA_SAFE_ASSERT_RETURN(pluginDesc != nullptr, nullptr); - - CarlaString tmpURI; - tmpURI = "http://kxstudio.sf.net/carla/plugins/"; - tmpURI += pluginDesc->label; - - carla_debug("lv2_descriptor(%i) - not found, allocating new with uri \"%s\"", index, (const char*)tmpURI); - - const DSSI_Descriptor dssiDescTmp = { - /* URI */ carla_strdup(tmpURI), - /* instantiate */ lv2_instantiate, - /* connect_port */ lv2_connect_port, - /* activate */ lv2_activate, - /* run */ lv2_run, - /* deactivate */ lv2_deactivate, - /* cleanup */ lv2_cleanup, - /* extension_data */ lv2_extension_data - }; - - DSSI_Descriptor* const dssiDesc(new DSSI_Descriptor); - std::memcpy(dssiDesc, &dssiDescTmp, sizeof(DSSI_Descriptor)); - - plm.dssiDescs.append(dssiDesc); - - return dssiDesc; -} - -CARLA_EXPORT -const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index) -{ - carla_debug("lv2ui_descriptor(%i)", index); - - static const LV2UI_Descriptor lv2UiDesc = { - /* URI */ "http://kxstudio.sf.net/carla/ui", - /* instantiate */ lv2ui_instantiate, - /* cleanup */ lv2ui_cleanup, - /* port_event */ lv2ui_port_event, - /* extension_data */ lv2ui_extension_data - }; - - return (index == 0) ? &lv2UiDesc : nullptr; -} - -// ----------------------------------------------------------------------- diff --git a/source/utils/CarlaUtils.hpp b/source/utils/CarlaUtils.hpp index 32a2547f6..b5ba77890 100644 --- a/source/utils/CarlaUtils.hpp +++ b/source/utils/CarlaUtils.hpp @@ -410,6 +410,22 @@ void carla_add(T dest[], const T src[], const std::size_t count) noexcept *dest++ += *src++; } +/* + * Add array values to another array, with a multiplication factor. + */ +template +static inline +void carla_addWithMultiply(T dest[], const T src[], const T& multiplier, const std::size_t count) noexcept +{ + CARLA_SAFE_ASSERT_RETURN(dest != nullptr,); + CARLA_SAFE_ASSERT_RETURN(src != nullptr,); + CARLA_SAFE_ASSERT_RETURN(dest != src,); + CARLA_SAFE_ASSERT_RETURN(count > 0,); + + for (std::size_t i=0; i +static inline +void carla_copyWithMultiply(T dest[], const T src[], const T& multiplier, const std::size_t count) noexcept +{ + CARLA_SAFE_ASSERT_RETURN(dest != nullptr,); + CARLA_SAFE_ASSERT_RETURN(src != nullptr,); + CARLA_SAFE_ASSERT_RETURN(dest != src,); + CARLA_SAFE_ASSERT_RETURN(count > 0,); + + for (std::size_t i=0; i +static inline +void carla_multiply(T data[], const T& multiplier, const std::size_t count) noexcept +{ + CARLA_SAFE_ASSERT_RETURN(data != nullptr,); + CARLA_SAFE_ASSERT_RETURN(count > 0,); + + if (multiplier == 0) + { + std::memset(data, 0, count*sizeof(T)); + } + else + { + for (std::size_t i=0; i