diff --git a/examples/delay/source/Plugin.cpp b/examples/delay/source/Plugin.cpp index 814d4ee..e1cc1ac 100644 --- a/examples/delay/source/Plugin.cpp +++ b/examples/delay/source/Plugin.cpp @@ -4,7 +4,6 @@ #include "Plugin.h" #include "PluginEditor.h" -#include namespace examples::delay { std::vector> createParams() { @@ -34,7 +33,7 @@ namespace examples::delay { m_delay.process(buffer, m_parameters); }; - mostly_harmless::runBlockDispatch(buffer, context, std::move(onEvent), std::move(onAudio)); + mostly_harmless::Plugin::runBlockDispatch(buffer, context, std::move(onEvent), std::move(onAudio)); } void Plugin::flushParams(mostly_harmless::events::InputEventContext context) noexcept { diff --git a/examples/delay/source/PluginEditor.cpp b/examples/delay/source/PluginEditor.cpp index 43f3f7a..534f5f3 100644 --- a/examples/delay/source/PluginEditor.cpp +++ b/examples/delay/source/PluginEditor.cpp @@ -4,7 +4,7 @@ #include "PluginEditor.h" #include "Parameters.h" -#include +#include namespace examples::delay { PluginEditor::PluginEditor(std::uint32_t width, std::uint32_t height) : mostly_harmless::gui::WebviewEditor(width, height, mostly_harmless::gui::Colour{ 0xFF89CC04 }) { std::stringstream initialDataStream; diff --git a/examples/gain/source/GainEditor.cpp b/examples/gain/source/GainEditor.cpp index 58875ac..083182a 100644 --- a/examples/gain/source/GainEditor.cpp +++ b/examples/gain/source/GainEditor.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include "Gain.h" #include diff --git a/include/mostly_harmless/CMakeLists.txt b/include/mostly_harmless/CMakeLists.txt index f86b3ae..11474f1 100644 --- a/include/mostly_harmless/CMakeLists.txt +++ b/include/mostly_harmless/CMakeLists.txt @@ -19,6 +19,5 @@ set(MOSTLYHARMLESS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/gui/mostlyharmless_Colour.h ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_TaskThread.h ${CMAKE_CURRENT_SOURCE_DIR}/utils/mostlyharmless_Timer.h - ${CMAKE_CURRENT_SOURCE_DIR}/audio/mostlyharmless_AudioHelpers.h ${PLATFORM_HEADERS} PARENT_SCOPE) \ No newline at end of file diff --git a/include/mostly_harmless/audio/mostlyharmless_AudioHelpers.h b/include/mostly_harmless/audio/mostlyharmless_AudioHelpers.h deleted file mode 100644 index 4fba64b..0000000 --- a/include/mostly_harmless/audio/mostlyharmless_AudioHelpers.h +++ /dev/null @@ -1,73 +0,0 @@ -// -// Created by Syl on 19/08/2024. -// - -#ifndef MOSTLYHARMLESS_MOSTLYHARMLESS_AUDIOHELPERS_H -#define MOSTLYHARMLESS_MOSTLYHARMLESS_AUDIOHELPERS_H -#include "mostly_harmless/mostlyharmless_Plugin.h" -#include "mostly_harmless/events/mostlyharmless_InputEventContext.h" -#include "marvin/containers/marvin_BufferView.h" -#include -namespace mostly_harmless { - /** - * \brief Splits an input buffer into chunks and dispatches them, allowing for block based processing. - * - * As parameter and note events are sample accurate, and passed via a single queue, naively doing block based processing with the entire - * input buffer would mean only having one param / note update event per block. To get around this, this function handles chunking the audio - * between events, and calling some process function with that chunk. - * For example: - * ```cpp - * void Plugin::process(marvin::containers::ByfferView buffer, mostly_harmless::events::InputEventContext context) { - * runBlockDispatch(buffer, context, - * [this](const clap_event_header* event) -> void { - * handleEvent(event); - * }, - * [this](marvin::containers::BufferView chunk) -> void { - * // assuming a member called m_processor - * m_processor.process(chunk); - * } - * ); - * } - * ``` - * \param buffer The entire buffer for this block - * \param eventCallback The input event queue for this block. - * \param eventCallback A callback to invoke on a new event. - * \param blockCallback A callback to invoke on a new chunk. - */ - template - void runBlockDispatch(marvin::containers::BufferView buffer, - mostly_harmless::events::InputEventContext eventContext, - std::function&& eventCallback, - std::function)>&& blockCallback) { - const auto numChannels = buffer.getNumChannels(); - auto** channelArray = static_cast(alloca(sizeof(SampleType*) * numChannels)); - size_t lastEventIndex{ 0 }; - auto* const* rawBuff = buffer.getArrayOfWritePointers(); - for (size_t i = 0; i < buffer.getNumSamples(); ++i) { - if (eventContext.next() && eventContext.next()->time == static_cast(i)) { - while (eventContext.next() && eventContext.next()->time == static_cast(i)) { - eventCallback(eventContext.next()); - ++eventContext; - } - for (size_t channel = 0; channel < numChannels; ++channel) { - auto* offsetChannelPtr = rawBuff[channel] + static_cast(lastEventIndex); - channelArray[channel] = offsetChannelPtr; - } - const auto numSamples = i - lastEventIndex; - marvin::containers::BufferView slice{ channelArray, buffer.getNumChannels(), numSamples }; - blockCallback(slice); - lastEventIndex = i; - } - } - const auto remaining = static_cast(buffer.getNumSamples()) - static_cast(lastEventIndex); - if (remaining <= 0) return; - for (size_t channel = 0; channel < numChannels; ++channel) { - auto* offsetChannelPtr = rawBuff[channel] + static_cast(lastEventIndex); - channelArray[channel] = offsetChannelPtr; - } - marvin::containers::BufferView slice{ channelArray, buffer.getNumChannels(), static_cast(remaining) }; - blockCallback(slice); - } -} // namespace mostly_harmless - -#endif // MOSTLYHARMLESS_MOSTLYHARMLESS_AUDIOHELPERS_H diff --git a/include/mostly_harmless/mostlyharmless_Plugin.h b/include/mostly_harmless/mostlyharmless_Plugin.h index 4e8b9f3..11ddfd5 100644 --- a/include/mostly_harmless/mostlyharmless_Plugin.h +++ b/include/mostly_harmless/mostlyharmless_Plugin.h @@ -112,6 +112,7 @@ namespace mostly_harmless { void paramsFlush(const clap_input_events* in, const clap_output_events* out) noexcept override; protected: + void runBlockDispatch(marvin::containers::BufferView bufferView, events::InputEventContext eventContext, std::function&& eventCallback, std::function)>&& blockCallback) noexcept; /** * Retrieves a non owning flat view into the internal params struct - useful for iterating over params, etc etc * \return A non owning view into the internal params. @@ -224,6 +225,7 @@ namespace mostly_harmless { private: + std::vector m_channelScratchVec; std::vector> m_indexedParams; std::unordered_map*> m_idParams; std::optional m_lastTransportState{}; diff --git a/source/mostlyharmless_Plugin.cpp b/source/mostlyharmless_Plugin.cpp index 2201944..f7755d1 100644 --- a/source/mostlyharmless_Plugin.cpp +++ b/source/mostlyharmless_Plugin.cpp @@ -26,6 +26,7 @@ namespace mostly_harmless { runOnMainThread(std::move(messageThreadCallback)); }; m_guiDispatchThread.action = std::move(guiDispatchCallback); + m_channelScratchVec.reserve(64); } template @@ -218,6 +219,37 @@ namespace mostly_harmless { default: break; } } + template + void Plugin::runBlockDispatch(marvin::containers::BufferView bufferView, events::InputEventContext eventContext, std::function&& eventCallback, std::function)>&& blockCallback) noexcept { + const auto numChannels = bufferView.getNumChannels(); + m_channelScratchVec.resize(numChannels); + size_t lastEventIndex{ 0 }; + auto* const* rawBuff = bufferView.getArrayOfWritePointers(); + for (size_t i = 0; i < bufferView.getNumSamples(); ++i) { + if (eventContext.next() && eventContext.next()->time == static_cast(i)) { + while (eventContext.next() && eventContext.next()->time == static_cast(i)) { + eventCallback(eventContext.next()); + ++eventContext; + } + for (size_t channel = 0; channel < numChannels; ++channel) { + auto* offsetChannelPtr = rawBuff[channel] + static_cast(lastEventIndex); + m_channelScratchVec[channel] = offsetChannelPtr; + } + const auto numSamples = i - lastEventIndex; + marvin::containers::BufferView slice{ m_channelScratchVec.data(), numChannels, numSamples }; + blockCallback(slice); + lastEventIndex = i; + } + } + const auto remaining = static_cast(bufferView.getNumSamples()) - static_cast(lastEventIndex); + if (remaining <= 0) return; + for (size_t channel = 0; channel < numChannels; ++channel) { + auto* offsetChannelPtr = rawBuff[channel] + static_cast(lastEventIndex); + m_channelScratchVec[channel] = offsetChannelPtr; + } + marvin::containers::BufferView slice{ m_channelScratchVec.data(), bufferView.getNumChannels(), static_cast(remaining) }; + blockCallback(slice); + } template std::span> Plugin::getParamView() noexcept {