diff --git a/.gitmodules b/.gitmodules index 7aaaa741d..f292e1f92 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "deps/juce"] path = deps/juce url = https://github.com/juce-framework/JUCE.git -[submodule "deps/lvtk"] - path = deps/lvtk - url = https://github.com/lvtk/lvtk [submodule "deps/clap-juce-extensions"] path = deps/clap-juce-extensions url = https://github.com/free-audio/clap-juce-extensions diff --git a/deps/lvtk b/deps/lvtk deleted file mode 160000 index 6e5f8d725..000000000 --- a/deps/lvtk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6e5f8d72513f76cc8b11f26a7aeb6a77dbe52923 diff --git a/include/element/aligneddata.hpp b/include/element/aligneddata.hpp deleted file mode 100644 index e195321ab..000000000 --- a/include/element/aligneddata.hpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2023 Kushview, LLC -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -namespace element { - -/** A template class for managing memory with specific alignment requirements. - - AlignedData allocates a block of memory and ensures that the usable portion - is aligned to a specific boundary. This is useful for SIMD operations, cache - line optimization, or interfacing with hardware that requires specific memory - alignment. - - @tparam Alignment The required alignment boundary in bytes (must be a power of 2) - - Example usage: - @code - // Create 1024 bytes aligned to 16-byte boundary - AlignedData<16> buffer(1024); - void* ptr = buffer.data(); - size_t size = buffer.size(); - @endcode -*/ -template -class AlignedData { -public: - /** Constructs an empty AlignedData object with no allocated memory. */ - AlignedData() = default; - - /** Allocates memory with the specified size and alignment. - - @param size The number of bytes to allocate (actual allocation may be larger - to accommodate alignment requirements) - */ - explicit AlignedData (size_t size) - : _data (new char[size + Alignment]), - _ptr (_data.get()), - _space (size + Alignment) - { - _ptr = std::align (Alignment, size, _ptr, _space); - } - - /** Move assignment operator. - - Transfers ownership of the allocated memory from another AlignedData object. - - @param o The source object to move from - @return Reference to this object - */ - AlignedData& operator= (AlignedData&& o) noexcept - { - _data = std::move (o._data); - _ptr = std::move (o._ptr); - _space = std::move (o._space); - return *this; - } - - /** Move constructor. - - Creates a new AlignedData by transferring ownership from another object. - - @param o The source object to move from - */ - AlignedData (AlignedData&& o) noexcept - { - *this = std::move (o); - } - - /** Returns a pointer to the aligned memory region. - - @return A pointer to the aligned data, or nullptr if no memory is allocated - */ - constexpr void* data() const noexcept { return _ptr; } - - /** Returns the size of the usable aligned memory region. - - Note: This may be less than the originally requested size due to alignment. - - @return The size in bytes of the aligned region - */ - constexpr size_t size() const noexcept { return _space; } - - /** Releases the allocated memory and resets the object to an empty state. */ - void reset() noexcept - { - _ptr = nullptr; - _data.reset(); - _space = 0; - } - - /** Swaps the contents of this AlignedData with another. - - @param b The AlignedData object to swap with - */ - inline void swap (AlignedData& b) noexcept - { - _data.swap (b._data); - std::swap (_ptr, b._ptr); - std::swap (_space, b._space); - } - -private: - std::unique_ptr _data; - void* _ptr = nullptr; - size_t _space = 0; -}; - -} // namespace element diff --git a/include/element/atombuffer.hpp b/include/element/atombuffer.hpp deleted file mode 100644 index 282d91102..000000000 --- a/include/element/atombuffer.hpp +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2023 Kushview, LLC -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace juce { -class MidiMessage; -class MidiBuffer; -} // namespace juce - -namespace element { - -class AtomBuffer final { -public: - AtomBuffer(); - ~AtomBuffer(); - - /** Set URID types from a URID map. */ - void setTypes (LV2_URID_Map* map); - /** Set URID types directly. */ - void setTypes (uint32_t atomSequence, uint32_t midiEvent); - - /** Clear the buffer. */ - void clear(); - void clear (int, int) { clear(); } - - /** Prepare for connecting to an lv2:OutputPort, atom:AtomPort */ - void prepare(); - - /** Insert event data at the given frame. */ - void insert (int64_t frames, uint32_t size, uint32_t type, const void* data); - - /** Insert a juce MidiMessage into the buffer. */ - void insert (juce::MidiMessage& msg, int frame); - - /** Add the contents of another atom buffer into this one. */ - void add (const AtomBuffer& other); - - /** Add the contents of a juce MidiBuffer into this one. */ - void add (juce::MidiBuffer& midi); - - /** Returns the total allocated memory. */ - inline constexpr uint32_t capacity() const noexcept { return _capacity; } - - /** Returns the underlying data. */ - inline constexpr void* data() noexcept { return _ptrs.raw; } - /** Returns the underlying data. */ - inline constexpr const void* data() const noexcept { return _ptrs.raw; } - - /** Returns the atom used in this buffer. */ - inline constexpr const LV2_Atom* atom() const noexcept { return _ptrs.atom; } - /** Returns the sequence used in this buffer. */ - inline constexpr const LV2_Atom_Sequence* sequence() const noexcept { return _ptrs.seq; } - - inline AtomBuffer (AtomBuffer&& o) noexcept - { - *this = std::move (o); - } - - inline AtomBuffer& operator= (AtomBuffer&& o) noexcept - { - _data = std::move (o._data); - _ptrs = std::move (o._ptrs); - _capacity = std::move (o._capacity); - MidiEvent = std::move (o.MidiEvent); - return *this; - } - - /** Swap two AtomBuffers. */ - inline void swap (AtomBuffer& b) noexcept - { - _data.swap (b._data); - std::swap (_ptrs.raw, b._ptrs.raw); - std::swap (_capacity, b._capacity); - std::swap (MidiEvent, b.MidiEvent); - } - -private: - AlignedData<8> _data; - union { - void* raw { nullptr }; - LV2_Atom* atom; - LV2_Atom_Sequence* seq; - } _ptrs; - - uint32_t _capacity { 0 }; - uint32_t MidiEvent { 0 }; -}; - -using AtomPipe = DataPipe; - -} // namespace element diff --git a/include/element/context.hpp b/include/element/context.hpp index 430dd541d..66783ca0a 100644 --- a/include/element/context.hpp +++ b/include/element/context.hpp @@ -20,7 +20,6 @@ class Log; class PluginManager; class PresetManager; class Settings; -class SymbolMap; class Context { public: @@ -40,7 +39,6 @@ class Context { SessionPtr session(); Settings& settings(); - SymbolMap& symbols(); //========================================================================= void openModule (const std::string& path); diff --git a/include/element/processor.hpp b/include/element/processor.hpp index abc6a736c..c8be71fe9 100644 --- a/include/element/processor.hpp +++ b/include/element/processor.hpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -25,7 +24,6 @@ namespace GraphRender { class ProcessBufferOp; } -class AtomBuffer; class Editor; class GraphNode; class ProcessBufferOp; @@ -34,24 +32,8 @@ struct RenderContext { juce::AudioSampleBuffer audio; juce::AudioSampleBuffer cv; MidiPipe midi; - AtomPipe atom; // clang-format off - RenderContext (float* const *audioData, - int numAudio, - float* const *cvData, - int numCV, - const juce::OwnedArray& sharedMidi, - const juce::Array& midiIndexes, - const juce::OwnedArray& sharedAtom, - const juce::Array& atomIndexes, - int numSamples) - : audio (audioData, numAudio, numSamples), - cv (cvData, numCV, numSamples), - midi (sharedMidi, midiIndexes), - atom (sharedAtom, atomIndexes) - {} - RenderContext (float* const *audioData, int numAudio, float* const *cvData, @@ -67,14 +49,11 @@ struct RenderContext { RenderContext (AudioSampleBuffer& audioRef, AudioSampleBuffer& cvRef, MidiBuffer& midiRef, - AtomBuffer& atomRef, int numSamples) : audio (audioRef.getArrayOfWritePointers(), audioRef.getNumChannels(), numSamples), cv (cvRef.getArrayOfWritePointers(), cvRef.getNumChannels(), numSamples), - midi (midiRef), - atom (atomRef) - { - } + midi (midiRef) + {} // clang-format on }; diff --git a/include/element/spinlock.hpp b/include/element/spinlock.hpp new file mode 100644 index 000000000..fa45b47f2 --- /dev/null +++ b/include/element/spinlock.hpp @@ -0,0 +1,42 @@ +// Copyright 2026 Kushview, LLC +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +#include + +namespace element { + +/** A simple spin lock using std::atomic. */ +class EL_API SpinLock { +public: + /** Initialize unlocked. */ + inline SpinLock() = default; + inline ~SpinLock() = default; + + /** Lock or yield until locked. (non realtime) */ + void lock() const noexcept; + /** Lock immediately or return false. (realtime)*/ + inline bool tryLock() const noexcept { return tryLock (0, 1); } + /** Unlock the mutex. Note this does not check lock status. */ + inline void unlock() const noexcept { _lock = 0; } + /** Returns true if the mutex is locked. */ + inline bool locked() const noexcept { return _lock.load() == 1; } + +private: + mutable std::atomic _lock { 0 }; + /** @internal */ + inline bool tryLock (int c, int v) const noexcept + { + return _lock.compare_exchange_strong (c, v); + } + + SpinLock (const SpinLock&) = delete; + SpinLock& operator= (const SpinLock&) = delete; + SpinLock (SpinLock&&) = delete; + SpinLock& operator= (SpinLock&&) = delete; +}; + +} // namespace element diff --git a/include/element/symbolmap.hpp b/include/element/symbolmap.hpp deleted file mode 100644 index 221c6db90..000000000 --- a/include/element/symbolmap.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2023 Kushview, LLC -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - -#include - -namespace element { - -class SymbolMap final { -public: - SymbolMap() noexcept {} - ~SymbolMap() {} - - const uint32_t map (const char* str) noexcept { return _sym.map (str); } - const char* unmap (uint32_t urid) noexcept { return _sym.unmap (urid); } - - inline auto mapPtr() const noexcept { return (LV2_URID_Map*) _sym.map_feature()->data; } - inline auto mapFeature() const noexcept { return _sym.map_feature(); } - inline auto unmapPtr() const noexcept { return (LV2_URID_Unmap*) _sym.unmap_feature()->data; } - inline auto unmapFeature() const noexcept { return _sym.unmap_feature(); } - - inline operator LV2_URID_Map*() const noexcept { return mapPtr(); } - -private: - lvtk::Symbols _sym; -}; - -} // namespace element diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 18ec7cef8..603502356 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,7 +13,6 @@ target_include_directories(element "${PROJECT_SOURCE_DIR}/src/lua/src" "${PROJECT_SOURCE_DIR}/deps/clap-juce-extensions/clap-libs/clap/include" "${PROJECT_SOURCE_DIR}/deps/clap-juce-extensions/clap-libs/clap-helpers/include" - "${PROJECT_SOURCE_DIR}/deps/lvtk/include" "${Boost_INCLUDE_DIRS}" ) diff --git a/src/atombuffer.cpp b/src/atombuffer.cpp deleted file mode 100644 index 3daa8aaaa..000000000 --- a/src/atombuffer.cpp +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2023 Kushview, LLC -// SPDX-License-Identifier: GPL-3.0-or-later - -#include - -#include -#include - -#include -#include - -namespace element { - -AtomBuffer::AtomBuffer() - : _data (8192) -{ - _capacity = _data.size(); - _ptrs.raw = _data.data(); - clear(); -} - -AtomBuffer::~AtomBuffer() -{ - _capacity = 0; - _ptrs.raw = nullptr; - _data.reset(); -} - -void AtomBuffer::setTypes (LV2_URID_Map* map) -{ - setTypes (map->map (map->handle, LV2_ATOM__Sequence), - map->map (map->handle, LV2_MIDI__MidiEvent)); -} - -void AtomBuffer::setTypes (uint32_t as, uint32_t me) -{ - _ptrs.atom->type = as; - MidiEvent = me; -} - -void AtomBuffer::clear() -{ - _ptrs.atom->size = sizeof (LV2_Atom_Sequence_Body); -} - -void AtomBuffer::prepare() -{ - _ptrs.atom->size = _capacity - sizeof (LV2_Atom_Sequence_Body); -} - -void AtomBuffer::insert (int64_t frames, uint32_t size, uint32_t type, const void* data) -{ - if (sizeof (LV2_Atom) + _ptrs.atom->size + lv2_atom_pad_size (size) > _capacity) - return; - - const auto size_needed = lv2_atom_pad_size (sizeof (LV2_Atom_Event) + size); - LV2_Atom_Event* ev = (LV2_Atom_Event*) ((uint8_t*) _ptrs.seq + lv2_atom_total_size (&_ptrs.seq->atom)); - - LV2_ATOM_SEQUENCE_FOREACH (_ptrs.seq, i) - { - if (i->time.frames > frames) - { - std::memmove (((uint8_t*) i) + size_needed, - i, - (uint8_t*) ev - (uint8_t*) i); - ev = i; - break; - } - } - - assert (ev != nullptr); - - ev->time.frames = frames; - ev->body.size = size; - ev->body.type = type; - std::memcpy (ev + 1, data, size); - - _ptrs.atom->size += size_needed; -} - -void AtomBuffer::insert (juce::MidiMessage& msg, int frame) -{ - insert (frame, - static_cast (msg.getRawDataSize()), - MidiEvent, - msg.getRawData()); -} - -void AtomBuffer::add (const AtomBuffer& other) -{ - LV2_ATOM_SEQUENCE_FOREACH (other._ptrs.seq, i) - { - insert (i->time.frames, - i->body.size, - i->body.type, - LV2_ATOM_BODY (&i->body)); - } -} - -void AtomBuffer::add (juce::MidiBuffer& midi) -{ - for (const auto& i : midi) - { - insert (i.samplePosition, - static_cast (i.numBytes), - MidiEvent, - i.data); - } -} - -} // namespace element diff --git a/src/context.cpp b/src/context.cpp index 0233d0f2d..204a1e630 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #include "engine/audioprocessorfactory.hpp" @@ -57,7 +56,6 @@ class Context::Impl AudioEnginePtr engine; SessionPtr session; - std::unique_ptr symbols; std::unique_ptr devices; std::unique_ptr plugins; std::unique_ptr settings; @@ -73,7 +71,6 @@ class Context::Impl void init() { - symbols.reset (new SymbolMap()); log.reset (new Log()); devices.reset (new DeviceManager()); settings.reset (new Settings()); @@ -265,6 +262,5 @@ void Context::addModulePath (const std::string& path) } void Context::discoverModules() { impl->modules->discover(); } -SymbolMap& Context::symbols() { return *impl->symbols; } } // namespace element diff --git a/src/engine/audioengine.cpp b/src/engine/audioengine.cpp index 9c5e1d47d..ebcfe4f77 100644 --- a/src/engine/audioengine.cpp +++ b/src/engine/audioengine.cpp @@ -148,7 +148,7 @@ struct RootGraphRender : public AsyncUpdater } { - RenderContext rc (audioTemp, cvTemp, midiTemp, atomTemp, numSamples); + RenderContext rc (audioTemp, cvTemp, midiTemp, numSamples); const ScopedLock sl (graph->getPropertyLock()); if (graph->isSuspended()) { @@ -272,7 +272,6 @@ struct RootGraphRender : public AsyncUpdater int numOutputChans = -1; AudioSampleBuffer audioOut, audioTemp, cvTemp; MidiBuffer midiOut, midiTemp; - AtomBuffer atomTemp; void updateIndexes() { diff --git a/src/engine/clapprovider.cpp b/src/engine/clapprovider.cpp index 0e7f85417..2b9d0fd7e 100644 --- a/src/engine/clapprovider.cpp +++ b/src/engine/clapprovider.cpp @@ -17,7 +17,7 @@ #include #include -#include "lv2/messages.hpp" +#include #include "appinfo.hpp" #include "engine/clapprovider.hpp" @@ -518,6 +518,42 @@ class CLAPHost final : public CLAPBaseHost #endif //============================================================================== +struct TryLockAndCall +{ + template + void operator() (SpinLock& mutex, Fn&& fn) + { + if (mutex.tryLock()) + { + fn(); + mutex.unlock(); + } + } +}; + +struct LockAndCall +{ + template + void operator() (SpinLock& mutex, Fn&& fn) + { + mutex.lock(); + fn(); + mutex.unlock(); + } +}; + +struct RealtimeReadTrait +{ + using Read = TryLockAndCall; + using Write = LockAndCall; +}; + +struct RealtimeWriteTrait +{ + using Read = LockAndCall; + using Write = TryLockAndCall; +}; + template class CLAPEventQueue final { @@ -563,7 +599,7 @@ class CLAPEventQueue final Write write; static constexpr auto initialSize = 8192; - lvtk::SpinLock mutex; + SpinLock mutex; std::vector data; }; @@ -812,7 +848,7 @@ struct CLAPModule final : public ReferenceCountedObject //============================================================================== class CLAPParameter : public Parameter { - using Queue = CLAPEventQueue; + using Queue = CLAPEventQueue; Queue& _queue; const clap_plugin_t* _plugin; const clap_plugin_params_t* _params; @@ -1547,8 +1583,8 @@ class CLAPProcessor : public Processor AudioBuffer _tmpAudio; clap::helpers::EventList _eventIn, _eventOut; - CLAPEventQueue _queueIn; - CLAPEventQueue _queueOut; + CLAPEventQueue _queueIn; + CLAPEventQueue _queueOut; CLAPProcessor (CLAPModule::Ptr m, const String& i) : Processor (0), ID (i), _module (m) diff --git a/src/engine/graphnode.cpp b/src/engine/graphnode.cpp index e8f960b39..3edb05111 100644 --- a/src/engine/graphnode.cpp +++ b/src/engine/graphnode.cpp @@ -6,13 +6,10 @@ #include #include #include -#include #include "engine/graphbuilder.hpp" #include "engine/ionode.hpp" #include "nodes/audioprocessor.hpp" -#include "engine/miditranspose.hpp" -#include "nodes/nodetypes.hpp" #include "engine/graphnode.hpp" #ifndef EL_GRAPH_NODE_NAME @@ -435,18 +432,12 @@ void GraphNode::buildRenderingSequence() const ScopedLock sl (getPropertyLock()); renderingBuffers.setSize (numRenderingBuffersNeeded, 4096); renderingBuffers.clear(); - for (auto ab : atomBuffers) - ab->clear(); + for (int i = midiBuffers.size(); --i >= 0;) midiBuffers.getUnchecked (i)->clear(); while (midiBuffers.size() < numMidiBuffersNeeded) midiBuffers.add (new MidiBuffer()); - while (atomBuffers.size() < numAtomBuffersNeeded) - { - auto ab = atomBuffers.add (new AtomBuffer()); - ab->setTypes (_context.symbols()); - } } ScopedLock sl (seqLock); diff --git a/src/engine/graphnode.hpp b/src/engine/graphnode.hpp index b8131b353..e6203a2ac 100644 --- a/src/engine/graphnode.hpp +++ b/src/engine/graphnode.hpp @@ -214,7 +214,6 @@ class GraphNode : public Processor, uint32 lastNodeId; AudioSampleBuffer renderingBuffers; OwnedArray midiBuffers; - OwnedArray atomBuffers; Array renderingOps; bool _prepared = false; diff --git a/src/engine/processor.cpp b/src/engine/processor.cpp index 4ae1c89d9..823f5cc2d 100644 --- a/src/engine/processor.cpp +++ b/src/engine/processor.cpp @@ -628,7 +628,6 @@ void Processor::renderBypassed (RenderContext& rc) rc.audio.clear (0, numSamples); rc.cv.clear (0, numSamples); rc.midi.clear (0, numSamples); - rc.atom.clear (0, numSamples); } //============================================================================= diff --git a/src/lv2/messages.cpp b/src/lv2/messages.cpp deleted file mode 100644 index 05889a554..000000000 --- a/src/lv2/messages.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2023 Michael Fisher -// SPDX-License-Identifier: ISC - -#include "lv2/messages.hpp" -#include diff --git a/src/lv2/messages.hpp b/src/lv2/messages.hpp deleted file mode 100644 index a3f862a95..000000000 --- a/src/lv2/messages.hpp +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2022 Michael Fisher -// SPDX-License-Identifier: ISC - -#pragma once - -#include - -#include -#include - -#include -#include - -namespace lvtk { - -struct TryLockAndCall -{ - template - void operator() (SpinLock& mutex, Fn&& fn) - { - if (mutex.try_lock()) - { - fn(); - mutex.unlock(); - } - } -}; - -struct LockAndCall -{ - template - void operator() (SpinLock& mutex, Fn&& fn) - { - mutex.lock(); - fn(); - mutex.unlock(); - } -}; - -struct RealtimeReadTrait -{ - using Read = TryLockAndCall; - using Write = LockAndCall; -}; - -struct RealtimeWriteTrait -{ - using Read = LockAndCall; - using Write = TryLockAndCall; -}; - -struct MessageHeader -{ - uint32_t portIndex; - uint32_t protocol; -}; - -template -struct MessageBuffer -{ - virtual ~MessageBuffer() = default; - virtual void push_message (Header header, uint32_t size, const void* buffer) = 0; -}; - -template -class Messages : public MessageBuffer
-{ -public: - Messages() { data.reserve (initial_size); } - - void push_message (Header header, uint32_t size, const void* buffer) override - { - write (mutex, [&] { - const auto chars = to_chars (FullHeader { header, size }); - const auto charbuf = static_cast (buffer); - data.insert (data.end(), chars.begin(), chars.end()); - data.insert (data.end(), charbuf, charbuf + size); - }); - } - - template - void read_all (Callback&& callback) - { - read (mutex, [&] { - if (data.empty()) - return; - - const auto end = data.data() + data.size(); - for (auto ptr = data.data(); ptr < end;) - { - const auto header = read_unaligned (ptr); - callback (header.header, header.size, ptr + sizeof (header)); - ptr += sizeof (header) + header.size; - } - - data.clear(); - }); - } - -private: - using Read = typename LockTraits::Read; - Read read; - - using Write = typename LockTraits::Write; - Write write; - - struct FullHeader - { - Header header; - uint32_t size; - }; - - static constexpr auto initial_size = 8192; - SpinLock mutex; - std::vector data; -}; - -//============================================================================== -class LambdaTimer : private juce::Timer -{ -public: - explicit LambdaTimer (std::function c) : callback (c) {} - ~LambdaTimer() noexcept override { stopTimer(); } - - using Timer::startTimer; - using Timer::startTimerHz; - using Timer::stopTimer; - -private: - void timerCallback() override { callback(); } - std::function callback; -}; - -struct UiEventListener : public MessageBuffer -{ - virtual int idle() = 0; -}; - -struct UiMessageHeader -{ - UiEventListener* listener; - MessageHeader header; -}; - -class ProcessorToUi : public MessageBuffer -{ -public: - ProcessorToUi() { timer.startTimerHz (60); } - - void addUi (UiEventListener& l) - { - JUCE_ASSERT_MESSAGE_THREAD; - activeUis.insert (&l); - } - - void removeUi (UiEventListener& l) - { - JUCE_ASSERT_MESSAGE_THREAD; - activeUis.erase (&l); - } - - void push_message (UiMessageHeader header, uint32_t size, const void* buffer) override - { - processorToUi.push_message (header, size, buffer); - } - -private: - Messages processorToUi; - std::set activeUis; - LambdaTimer timer { [this] { - for (auto* l : activeUis) - if (l->idle() != 0) - return; - - processorToUi.read_all ([&] (const UiMessageHeader& header, uint32_t size, const char* data) { - if (activeUis.find (header.listener) != activeUis.cend()) - header.listener->push_message (header.header, size, data); - }); - } }; -}; - -} // namespace lvtk diff --git a/src/spinlock.cpp b/src/spinlock.cpp new file mode 100644 index 000000000..d399a3bc7 --- /dev/null +++ b/src/spinlock.cpp @@ -0,0 +1,38 @@ +// Copyright 2026 Kushview, LLC +// SPDX-License-Identifier: GPL-3.0-or-later + +#include + +#if _WIN32 +#define WINDOWS_LWAN +#define WIN32_LEAN_AND_MEAN 1 +#include +#undef WIN32_LEAN_AND_MEAN +#undef min +#undef max +#else +#include +#endif + +namespace element { + +void SpinLock::lock() const noexcept +{ + if (tryLock()) + return; + + for (int i = 20; --i >= 0;) + if (tryLock()) + return; + + while (! tryLock()) + { +#if _WIN32 + Sleep (0); +#else + sched_yield(); +#endif + } +} + +} // namespace element diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bf8927d53..399199f7f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,8 +15,6 @@ target_compile_definitions(test_element EL_TEST_SOURCE_ROOT="${CMAKE_SOURCE_DIR}") # Register all test suites with CTest -add_test(NAME "AlignedDataTests" COMMAND test_element --run_test=AlignedDataTests) -add_test(NAME "AtomTests" COMMAND test_element --run_test=AtomTests) add_test(NAME "AtomicValueTests" COMMAND test_element --run_test=AtomicValueTests) add_test(NAME "AtomicLockTests" COMMAND test_element --run_test=AtomicLockTests) add_test(NAME "AudioRoutingTests" COMMAND test_element --run_test=AudioRoutingTests) diff --git a/test/GraphNodeTests.cpp b/test/GraphNodeTests.cpp index 9e35a9ed7..27317ae78 100644 --- a/test/GraphNodeTests.cpp +++ b/test/GraphNodeTests.cpp @@ -4,20 +4,16 @@ #include #include -#include #include #include #include "fixture/PreparedGraph.h" #include "fixture/TestNode.h" -#include "fixture/AtomTestNode.h" #include "fixture/MidiGeneratorNode.h" #include "fixture/MidiCaptureNode.h" -#include "fixture/AtomCaptureNode.h" #include "engine/graphnode.hpp" #include "engine/ionode.hpp" -#include "utils.hpp" using namespace element; @@ -127,10 +123,8 @@ BOOST_AUTO_TEST_CASE (MultiMidiToSingleMidi) // Render the graph AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify all 6 events arrived at the MIDI destination @@ -169,10 +163,8 @@ BOOST_AUTO_TEST_CASE (SingleMidiToMultiMidi) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Both destinations should receive all 3 events @@ -219,10 +211,8 @@ BOOST_AUTO_TEST_CASE (MidiIsolation) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Each destination should only receive events from its connected source @@ -251,10 +241,8 @@ BOOST_AUTO_TEST_CASE (DisconnectedMidiNode) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Destination should receive no events @@ -295,10 +283,7 @@ BOOST_AUTO_TEST_CASE (MidiThroughIONodes) midi.addEvent (MidiMessage::noteOn (1, 64, 0.8f), 10); midi.addEvent (MidiMessage::noteOff (1, 60), 20); - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Processor should receive the 3 MIDI events from graph input @@ -371,10 +356,7 @@ BOOST_AUTO_TEST_CASE (ComplexMidiRouting) midi.addEvent (MidiMessage::noteOn (1, 48, 0.9f), 8); midi.addEvent (MidiMessage::noteOff (1, 48), 18); - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify event counts @@ -460,10 +442,8 @@ BOOST_AUTO_TEST_CASE (MidiChainWithBranching) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Dst1 gets src1 only: 2 events @@ -556,10 +536,8 @@ BOOST_AUTO_TEST_CASE (MidiMultiLevelMerging) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify hierarchical merging and distribution @@ -628,10 +606,7 @@ BOOST_AUTO_TEST_CASE (MidiIOPlusGenerators) gen2->addEvent (MidiMessage::noteOn (1, 71, 0.7f), 18); gen2->addEvent (MidiMessage::noteOff (1, 67), 28); - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify routing with I/O + generators @@ -665,10 +640,8 @@ BOOST_AUTO_TEST_CASE (MidiOutputIOUnused) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify unused MIDI output doesn't interfere @@ -703,10 +676,8 @@ BOOST_AUTO_TEST_CASE (MidiOutputIOConnected) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify internal routing still works with MIDI output connected @@ -752,10 +723,7 @@ BOOST_AUTO_TEST_CASE (MidiFullIOChain) gen->addEvent (MidiMessage::noteOff (1, 60), 15); gen->addEvent (MidiMessage::noteOn (1, 62, 0.8f), 25); - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify routing: gen events go to dst1, midiIn events go to dst2 @@ -801,10 +769,8 @@ BOOST_AUTO_TEST_CASE (MidiMultipleOutputs) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify dst1 only gets gen2 events, not gen1 diff --git a/test/MidiProgramMapTests.cpp b/test/MidiProgramMapTests.cpp index 1ce4e9f78..2b5bdf27d 100644 --- a/test/MidiProgramMapTests.cpp +++ b/test/MidiProgramMapTests.cpp @@ -40,7 +40,6 @@ static void testMappings (ProcessorPtr node) static void testMidiStream (ProcessorPtr node, const String& name = "Renders mappings") { - AtomBuffer atoms; MidiBuffer midi; AudioSampleBuffer audio, cv; audio.setSize (2, 1024, false, true, false); @@ -50,7 +49,7 @@ static void testMidiStream (ProcessorPtr node, const String& name = "Renders map midi.addEvent (MidiMessage::noteOn (1, 12, static_cast (50)), 300); midi.addEvent (MidiMessage::noteOff (1, 12), 300); - RenderContext rc (audio, cv, midi, atoms, audio.getNumSamples()); + RenderContext rc (audio, cv, midi, audio.getNumSamples()); node->render (rc); int index = 0; diff --git a/test/aligneddatatests.cpp b/test/aligneddatatests.cpp deleted file mode 100644 index 6ead60a38..000000000 --- a/test/aligneddatatests.cpp +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2023 Kushview, LLC -// SPDX-License-Identifier: GPL-3.0-or-later - -#include -#include -#include - -using element::AlignedData; - -BOOST_AUTO_TEST_SUITE (AlignedDataTests) - -BOOST_AUTO_TEST_CASE (default_constructor) -{ - AlignedData<16> data; - BOOST_REQUIRE (data.data() == nullptr); - BOOST_REQUIRE_EQUAL (data.size(), 0); -} - -BOOST_AUTO_TEST_CASE (constructor_with_size) -{ - AlignedData<16> data (1024); - BOOST_REQUIRE (data.data() != nullptr); - BOOST_REQUIRE (data.size() >= 1024); -} - -BOOST_AUTO_TEST_CASE (alignment_16_bytes) -{ - AlignedData<16> data (1024); - void* ptr = data.data(); - - // Check that pointer is aligned to 16-byte boundary - uintptr_t address = reinterpret_cast (ptr); - BOOST_REQUIRE_EQUAL (address % 16, 0); -} - -BOOST_AUTO_TEST_CASE (alignment_32_bytes) -{ - AlignedData<32> data (512); - void* ptr = data.data(); - - // Check that pointer is aligned to 32-byte boundary - uintptr_t address = reinterpret_cast (ptr); - BOOST_REQUIRE_EQUAL (address % 32, 0); -} - -BOOST_AUTO_TEST_CASE (alignment_64_bytes) -{ - AlignedData<64> data (256); - void* ptr = data.data(); - - // Check that pointer is aligned to 64-byte boundary - uintptr_t address = reinterpret_cast (ptr); - BOOST_REQUIRE_EQUAL (address % 64, 0); -} - -BOOST_AUTO_TEST_CASE (alignment_4_bytes) -{ - AlignedData<4> data (100); - void* ptr = data.data(); - - // Check that pointer is aligned to 4-byte boundary - uintptr_t address = reinterpret_cast (ptr); - BOOST_REQUIRE_EQUAL (address % 4, 0); -} - -BOOST_AUTO_TEST_CASE (move_constructor) -{ - AlignedData<16> data1 (1024); - void* originalPtr = data1.data(); - size_t originalSize = data1.size(); - - AlignedData<16> data2 (std::move (data1)); - - BOOST_REQUIRE_EQUAL (data2.data(), originalPtr); - BOOST_REQUIRE_EQUAL (data2.size(), originalSize); - // Note: The implementation doesn't clear the moved-from object's pointer -} - -BOOST_AUTO_TEST_CASE (move_assignment) -{ - AlignedData<16> data1 (1024); - void* originalPtr = data1.data(); - size_t originalSize = data1.size(); - - AlignedData<16> data2; - data2 = std::move (data1); - - BOOST_REQUIRE_EQUAL (data2.data(), originalPtr); - BOOST_REQUIRE_EQUAL (data2.size(), originalSize); - // Note: The implementation doesn't clear the moved-from object's pointer -} - -BOOST_AUTO_TEST_CASE (reset) -{ - AlignedData<16> data (1024); - BOOST_REQUIRE (data.data() != nullptr); - BOOST_REQUIRE (data.size() > 0); - - data.reset(); - - BOOST_REQUIRE (data.data() == nullptr); - BOOST_REQUIRE_EQUAL (data.size(), 0); -} - -BOOST_AUTO_TEST_CASE (swap) -{ - AlignedData<16> data1 (512); - AlignedData<16> data2 (1024); - - void* ptr1 = data1.data(); - void* ptr2 = data2.data(); - size_t size1 = data1.size(); - size_t size2 = data2.size(); - - data1.swap (data2); - - BOOST_REQUIRE_EQUAL (data1.data(), ptr2); - BOOST_REQUIRE_EQUAL (data1.size(), size2); - BOOST_REQUIRE_EQUAL (data2.data(), ptr1); - BOOST_REQUIRE_EQUAL (data2.size(), size1); -} - -BOOST_AUTO_TEST_CASE (swap_with_empty) -{ - AlignedData<16> data1 (1024); - AlignedData<16> data2; - - void* ptr1 = data1.data(); - size_t size1 = data1.size(); - - data1.swap (data2); - - BOOST_REQUIRE (data1.data() == nullptr); - BOOST_REQUIRE_EQUAL (data1.size(), 0); - BOOST_REQUIRE_EQUAL (data2.data(), ptr1); - BOOST_REQUIRE_EQUAL (data2.size(), size1); -} - -BOOST_AUTO_TEST_CASE (small_allocation) -{ - AlignedData<16> data (1); - BOOST_REQUIRE (data.data() != nullptr); - BOOST_REQUIRE (data.size() >= 1); - - uintptr_t address = reinterpret_cast (data.data()); - BOOST_REQUIRE_EQUAL (address % 16, 0); -} - -BOOST_AUTO_TEST_CASE (large_allocation) -{ - AlignedData<16> data (1024 * 1024); // 1 MB - BOOST_REQUIRE (data.data() != nullptr); - BOOST_REQUIRE (data.size() >= 1024 * 1024); - - uintptr_t address = reinterpret_cast (data.data()); - BOOST_REQUIRE_EQUAL (address % 16, 0); -} - -BOOST_AUTO_TEST_CASE (write_and_read_data) -{ - AlignedData<16> data (100); - char* ptr = static_cast (data.data()); - - // Write data - for (size_t i = 0; i < 100; ++i) - { - ptr[i] = static_cast (i); - } - - // Read and verify data - for (size_t i = 0; i < 100; ++i) - { - BOOST_REQUIRE_EQUAL (ptr[i], static_cast (i)); - } -} - -BOOST_AUTO_TEST_CASE (different_alignments_coexist) -{ - AlignedData<4> data4 (100); - AlignedData<8> data8 (100); - AlignedData<16> data16 (100); - AlignedData<32> data32 (100); - - uintptr_t addr4 = reinterpret_cast (data4.data()); - uintptr_t addr8 = reinterpret_cast (data8.data()); - uintptr_t addr16 = reinterpret_cast (data16.data()); - uintptr_t addr32 = reinterpret_cast (data32.data()); - - BOOST_REQUIRE_EQUAL (addr4 % 4, 0); - BOOST_REQUIRE_EQUAL (addr8 % 8, 0); - BOOST_REQUIRE_EQUAL (addr16 % 16, 0); - BOOST_REQUIRE_EQUAL (addr32 % 32, 0); -} - -BOOST_AUTO_TEST_CASE (multiple_move_operations) -{ - AlignedData<16> data1 (1024); - void* originalPtr = data1.data(); - - AlignedData<16> data2 (std::move (data1)); - AlignedData<16> data3 (std::move (data2)); - AlignedData<16> data4 (std::move (data3)); - - BOOST_REQUIRE_EQUAL (data4.data(), originalPtr); - // Note: The implementation doesn't clear moved-from object pointers -} - -BOOST_AUTO_TEST_CASE (reset_after_move) -{ - AlignedData<16> data1 (1024); - AlignedData<16> data2 (std::move (data1)); - - // Should be safe to reset moved-from object - data1.reset(); - - BOOST_REQUIRE (data1.data() == nullptr); - BOOST_REQUIRE_EQUAL (data1.size(), 0); - BOOST_REQUIRE (data2.data() != nullptr); -} - -BOOST_AUTO_TEST_CASE (alignment_128_bytes) -{ - AlignedData<128> data (256); - void* ptr = data.data(); - - // Check that pointer is aligned to 128-byte boundary (common cache line size) - uintptr_t address = reinterpret_cast (ptr); - BOOST_REQUIRE_EQUAL (address % 128, 0); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/atomtests.cpp b/test/atomtests.cpp deleted file mode 100644 index e7d781da5..000000000 --- a/test/atomtests.cpp +++ /dev/null @@ -1,705 +0,0 @@ -// Copyright 2023 Kushview, LLC -// SPDX-License-Identifier: GPL-3.0-or-later - -#include - -#include -#include -#include -#include - -#include - -#include -#include - -using AtomBuffer = element::AtomBuffer; -using MidiBuffer = juce::MidiBuffer; -using MidiMessage = juce::MidiMessage; - -struct urids final { - enum { - none = 0, - atom_eventTransfer, - atom_Double, - atom_Float, - atom_Object, - midi_MidiEvent, - time_Position, - time_speed, - time_frame - }; - - static constexpr uint32_t begin() noexcept { return 1; }; - static constexpr uint32_t end() noexcept { return midi_MidiEvent + 1; } - - inline static auto makeMap() - { - lvtk::Symbols::map_type init; - init[""] = none; - for (uint32_t urid = urids::begin(); urid < urids::end(); ++urid) - init[urids::uri (urid)] = urid; - return init; - } - - template - inline static const char* uri (URID&& urid) - { - switch (static_cast (urid)) { - case atom_eventTransfer: - return LV2_ATOM__eventTransfer; - break; - case atom_Double: - return LV2_ATOM__Double; - break; - case atom_Float: - return LV2_ATOM__Float; - break; - case atom_Object: - return LV2_ATOM__Object; - break; - case midi_MidiEvent: - return LV2_MIDI__MidiEvent; - break; - case time_Position: - return LV2_TIME__Position; - break; - case time_speed: - return LV2_TIME__speed; - break; - case time_frame: - return LV2_TIME__frame; - break; - case none: - default: - return ""; - break; - } - } -}; - -static uint32_t writeTime (LV2_Atom_Forge& forge, AtomBuffer& port) -{ - uint8_t timeBuf[512] = { 0 }; - lv2_atom_forge_set_buffer (&forge, timeBuf, sizeof (timeBuf)); - LV2_Atom_Forge_Frame frame; - auto ref = (LV2_Atom*) lv2_atom_forge_object (&forge, &frame, 0, urids::time_Position); - - lv2_atom_forge_key (&forge, urids::time_speed); - lv2_atom_forge_float (&forge, 1.0f); - - lv2_atom_forge_key (&forge, urids::time_frame); - lv2_atom_forge_long (&forge, 1234); - - lv2_atom_forge_pop (&forge, &frame); - - const auto total_size = ref->size; - port.insert (0, ref->size, ref->type, LV2_ATOM_BODY (ref)); - return total_size; -} - -BOOST_AUTO_TEST_SUITE (AtomTests) - -BOOST_AUTO_TEST_CASE (Symbols_initializer) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - - BOOST_REQUIRE_EQUAL (sym.map (LV2_MIDI__MidiEvent), urids::midi_MidiEvent); - BOOST_REQUIRE_EQUAL (sym.map (LV2_ATOM__Object), urids::atom_Object); - BOOST_REQUIRE_EQUAL (sym.unmap (urids::atom_Float), LV2_ATOM__Float); - BOOST_REQUIRE_NE (sym.map (LV2_MIDI__MidiEvent), urids::atom_Object); -} - -BOOST_AUTO_TEST_CASE (insert) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - - AtomBuffer buffer; - LV2_Atom_Forge forge; - - auto map = (LV2_URID_Map*) sym.map_feature()->data; - lv2_atom_forge_init (&forge, map); - buffer.setTypes (map); - - auto msg2 = MidiMessage::noteOn (1, 60, 0.6f); - auto msg1 = MidiMessage::noteOff (1, 60); - buffer.insert (100, msg1.getRawDataSize(), urids::midi_MidiEvent, msg1.getRawData()); - buffer.insert (50, msg2.getRawDataSize(), urids::midi_MidiEvent, msg2.getRawData()); - buffer.insert (1, msg1.getRawDataSize(), urids::midi_MidiEvent, msg1.getRawData()); - const auto timeSize = writeTime (forge, buffer); - - int index = 0; - - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - if (index == 0) { - BOOST_REQUIRE_EQUAL (ev->body.type, urids::atom_Object); - BOOST_REQUIRE_EQUAL (ev->body.size, timeSize); - auto obj = (const LV2_Atom_Object*) &ev->body; - BOOST_REQUIRE_EQUAL (obj->body.otype, urids::time_Position); - } else if (index == 1) { - // BOOST_REQUIRE_EQUAL (ev->body.type, urids::midi_MidiEvent); - BOOST_REQUIRE_EQUAL (ev->body.size, 3U); - BOOST_REQUIRE_EQUAL (1, ev->time.frames); - MidiMessage t (LV2_ATOM_BODY (&ev->body), ev->body.size); - BOOST_REQUIRE (t.isNoteOff()); - BOOST_REQUIRE_EQUAL (t.getNoteNumber(), 60); - BOOST_REQUIRE_EQUAL (t.getChannel(), 1); - } else if (index == 2) { - BOOST_REQUIRE_EQUAL (ev->body.type, urids::midi_MidiEvent); - BOOST_REQUIRE_EQUAL (ev->body.size, 3U); - BOOST_REQUIRE_EQUAL (50, ev->time.frames); - MidiMessage t (LV2_ATOM_BODY (&ev->body), ev->body.size); - BOOST_REQUIRE (t.isNoteOn()); - BOOST_REQUIRE_EQUAL (t.getNoteNumber(), 60); - BOOST_REQUIRE_EQUAL (t.getChannel(), 1); - } else if (index == 3) { - BOOST_REQUIRE_EQUAL (ev->body.type, urids::midi_MidiEvent); - BOOST_REQUIRE_EQUAL (ev->body.size, 3U); - BOOST_REQUIRE_EQUAL (100, ev->time.frames); - MidiMessage t (LV2_ATOM_BODY (&ev->body), ev->body.size); - BOOST_REQUIRE (t.isNoteOff()); - BOOST_REQUIRE_EQUAL (t.getNoteNumber(), 60); - BOOST_REQUIRE_EQUAL (t.getChannel(), 1); - } - - ++index; - } - - BOOST_REQUIRE_EQUAL (index, 4); -} - -BOOST_AUTO_TEST_CASE (swapping) -{ - juce::OwnedArray array; - { - auto& b1 = *array.add (new AtomBuffer()); - const auto capacity = b1.capacity(); - - float value = 1.f; - b1.insert (0, sizeof (float), 0, &value); - auto a1 = b1.atom(); - auto d1 = b1.data(); - - auto& b2 = *array.add (new AtomBuffer()); - auto a2 = b2.atom(); - auto d2 = b2.data(); - - BOOST_REQUIRE_EQUAL (b1.capacity(), b2.capacity()); - BOOST_REQUIRE_NE (d1, d2); - - BOOST_REQUIRE_EQUAL (a1, array[0]->atom()); - BOOST_REQUIRE_EQUAL (a2, array[1]->atom()); - - b1.swap (b2); - BOOST_REQUIRE_EQUAL (b1.atom(), a2); - BOOST_REQUIRE_EQUAL (b2.atom(), a1); - BOOST_REQUIRE_EQUAL (0, std::memcmp (b1.data(), d2, capacity)); - BOOST_REQUIRE_EQUAL (0, std::memcmp (b2.data(), d1, capacity)); - - int count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b1.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); - - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b2.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 1); - - b1.swap (b2); - BOOST_REQUIRE_EQUAL (b1.atom(), a1); - BOOST_REQUIRE_EQUAL (b2.atom(), a2); - BOOST_REQUIRE_EQUAL (0, std::memcmp (b1.data(), d1, capacity)); - BOOST_REQUIRE_EQUAL (0, std::memcmp (b2.data(), d2, capacity)); - } - - array.clear (true); -} - -BOOST_AUTO_TEST_CASE (pipe) -{ - juce::OwnedArray array; - { - auto b1 = array.add (new AtomBuffer()); - auto b2 = array.add (new AtomBuffer()); - auto b3 = array.add (new AtomBuffer()); - - juce::Array channels = { 0, 2, 1 }; - element::AtomPipe pipe (array, channels); - BOOST_REQUIRE_EQUAL (pipe.size(), array.size()); - BOOST_REQUIRE_EQUAL (pipe.writeBuffer (0), b1); - BOOST_REQUIRE_EQUAL (pipe.writeBuffer (1), b3); - BOOST_REQUIRE_EQUAL (pipe.writeBuffer (2), b2); - } - array.clear (true); -} - -BOOST_AUTO_TEST_CASE (capacity_checking) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer buffer; - buffer.setTypes (map); - - const auto initial_capacity = buffer.capacity(); - BOOST_REQUIRE_GT (initial_capacity, 8000U); // Should be ~8192, but aligned - - // Fill buffer near capacity with large events - const size_t large_event_size = 1024; - uint8_t large_data[large_event_size]; - memset (large_data, 0xAB, large_event_size); - - int events_inserted = 0; - for (int frame = 0; frame < 1000; ++frame) { - const auto size_before = buffer.sequence()->atom.size; - buffer.insert (frame, large_event_size, urids::atom_Object, large_data); - const auto size_after = buffer.sequence()->atom.size; - - if (size_after > size_before) - ++events_inserted; - else - break; // Buffer full, insert silently failed - } - - BOOST_REQUIRE_GT (events_inserted, 0); - BOOST_REQUIRE_LT (events_inserted, 1000); // Should hit capacity before 1000 events - - // Verify silent failure on overflow - const auto size_at_capacity = buffer.sequence()->atom.size; - buffer.insert (9999, large_event_size, urids::atom_Object, large_data); - BOOST_REQUIRE_EQUAL (buffer.sequence()->atom.size, size_at_capacity); // No change -} - -BOOST_AUTO_TEST_CASE (clear_functionality) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer buffer; - buffer.setTypes (map); - - // Add some events - auto msg = MidiMessage::noteOn (1, 60, 0.8f); - buffer.insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - buffer.insert (100, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - - int count = 0; - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 2); - - // Clear and verify empty - buffer.clear(); - BOOST_REQUIRE_EQUAL (buffer.sequence()->atom.size, sizeof (LV2_Atom_Sequence_Body)); - - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); - - // Test clear(int, int) signature - buffer.insert (50, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - buffer.clear (0, 512); // Should behave same as clear() - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); -} - -BOOST_AUTO_TEST_CASE (prepare_for_output) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer buffer; - buffer.setTypes (map); - - // prepare() sets size to capacity - header - buffer.prepare(); - const auto prepared_size = buffer.sequence()->atom.size; - BOOST_REQUIRE_EQUAL (prepared_size, buffer.capacity() - sizeof (LV2_Atom_Sequence_Body)); - - // After clear, size should be minimal - buffer.clear(); - BOOST_REQUIRE_EQUAL (buffer.sequence()->atom.size, sizeof (LV2_Atom_Sequence_Body)); -} - -BOOST_AUTO_TEST_CASE (add_from_midi_buffer) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer buffer; - buffer.setTypes (map); - - // Create JUCE MidiBuffer with events - MidiBuffer midi; - midi.addEvent (MidiMessage::noteOn (1, 60, 0.5f), 0); - midi.addEvent (MidiMessage::noteOn (1, 64, 0.6f), 50); - midi.addEvent (MidiMessage::noteOff (1, 60), 100); - midi.addEvent (MidiMessage::noteOff (1, 64), 150); - - buffer.add (midi); - - int count = 0; - int64_t prev_frame = -1; - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - BOOST_REQUIRE_EQUAL (ev->body.type, urids::midi_MidiEvent); - BOOST_REQUIRE_EQUAL (ev->body.size, 3U); - BOOST_REQUIRE_GT (ev->time.frames, prev_frame); // Verify ordering - prev_frame = ev->time.frames; - ++count; - } - BOOST_REQUIRE_EQUAL (count, 4); -} - -BOOST_AUTO_TEST_CASE (add_from_atom_buffer) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer src, dst; - src.setTypes (map); - dst.setTypes (map); - - // Add events to source - auto msg1 = MidiMessage::noteOn (1, 60, 0.7f); - auto msg2 = MidiMessage::noteOff (1, 60); - src.insert (10, msg1.getRawDataSize(), urids::midi_MidiEvent, msg1.getRawData()); - src.insert (50, msg2.getRawDataSize(), urids::midi_MidiEvent, msg2.getRawData()); - - // Add source to destination - dst.add (src); - - int count = 0; - LV2_ATOM_SEQUENCE_FOREACH (dst.sequence(), ev) - { - BOOST_REQUIRE_EQUAL (ev->body.type, urids::midi_MidiEvent); - ++count; - } - BOOST_REQUIRE_EQUAL (count, 2); - - // Add again to test merging - dst.add (src); - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (dst.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 4); -} - -BOOST_AUTO_TEST_CASE (insert_midi_message_convenience) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer buffer; - buffer.setTypes (map); - - auto msg1 = MidiMessage::noteOn (2, 72, 0.9f); - auto msg2 = MidiMessage::controllerEvent (2, 7, 100); - - buffer.insert (msg1, 25); - buffer.insert (msg2, 75); - - int count = 0; - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - BOOST_REQUIRE_EQUAL (ev->body.type, urids::midi_MidiEvent); - - if (count == 0) { - BOOST_REQUIRE_EQUAL (ev->time.frames, 25); - MidiMessage decoded (LV2_ATOM_BODY (&ev->body), ev->body.size); - BOOST_REQUIRE (decoded.isNoteOn()); - BOOST_REQUIRE_EQUAL (decoded.getNoteNumber(), 72); - } else if (count == 1) { - BOOST_REQUIRE_EQUAL (ev->time.frames, 75); - MidiMessage decoded (LV2_ATOM_BODY (&ev->body), ev->body.size); - BOOST_REQUIRE (decoded.isController()); - BOOST_REQUIRE_EQUAL (decoded.getControllerNumber(), 7); - BOOST_REQUIRE_EQUAL (decoded.getControllerValue(), 100); - } - - ++count; - } - BOOST_REQUIRE_EQUAL (count, 2); -} - -BOOST_AUTO_TEST_CASE (move_semantics) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer src; - src.setTypes (map); - - auto msg = MidiMessage::noteOn (1, 60, 0.5f); - src.insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - - const auto src_data_ptr = src.data(); - const auto src_capacity = src.capacity(); - - // Move construct - AtomBuffer moved (std::move (src)); - BOOST_REQUIRE_EQUAL (moved.data(), src_data_ptr); - BOOST_REQUIRE_EQUAL (moved.capacity(), src_capacity); - - int count = 0; - LV2_ATOM_SEQUENCE_FOREACH (moved.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 1); - - // Move assign - AtomBuffer assigned; - const auto moved_data = moved.data(); - assigned = std::move (moved); - BOOST_REQUIRE_EQUAL (assigned.data(), moved_data); - - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (assigned.sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 1); -} - -BOOST_AUTO_TEST_CASE (data_accessors) -{ - AtomBuffer buffer; - - const auto raw_ptr = buffer.data(); - const auto atom_ptr = buffer.atom(); - const auto seq_ptr = buffer.sequence(); - - BOOST_REQUIRE_NE (raw_ptr, nullptr); - BOOST_REQUIRE_NE (atom_ptr, nullptr); - BOOST_REQUIRE_NE (seq_ptr, nullptr); - BOOST_REQUIRE_EQUAL (raw_ptr, (const void*) atom_ptr); - BOOST_REQUIRE_EQUAL (raw_ptr, (const void*) seq_ptr); - - // Test const version - const AtomBuffer& const_ref = buffer; - BOOST_REQUIRE_EQUAL (const_ref.data(), raw_ptr); -} - -BOOST_AUTO_TEST_CASE (event_ordering) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer buffer; - buffer.setTypes (map); - - // Insert events out of order - auto msg = MidiMessage::noteOn (1, 60, 0.5f); - buffer.insert (100, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - buffer.insert (200, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - buffer.insert (50, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - buffer.insert (150, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - buffer.insert (25, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - - // Verify events are time-ordered - int count = 0; - int64_t expected_frames[] = { 25, 50, 100, 150, 200 }; - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - BOOST_REQUIRE_EQUAL (ev->time.frames, expected_frames[count]); - ++count; - } - BOOST_REQUIRE_EQUAL (count, 5); -} - -BOOST_AUTO_TEST_CASE (mixed_event_types) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - AtomBuffer buffer; - LV2_Atom_Forge forge; - lv2_atom_forge_init (&forge, map); - buffer.setTypes (map); - - // Insert MIDI - auto midi_msg = MidiMessage::noteOn (1, 60, 0.5f); - buffer.insert (0, midi_msg.getRawDataSize(), urids::midi_MidiEvent, midi_msg.getRawData()); - - // Insert Object (time position) - writeTime (forge, buffer); - - // Insert more MIDI - auto midi_msg2 = MidiMessage::noteOff (1, 60); - buffer.insert (100, midi_msg2.getRawDataSize(), urids::midi_MidiEvent, midi_msg2.getRawData()); - - // Insert Float atom - float float_value = 3.14159f; - buffer.insert (50, sizeof (float), urids::atom_Float, &float_value); - - int midi_count = 0, object_count = 0, float_count = 0; - LV2_ATOM_SEQUENCE_FOREACH (buffer.sequence(), ev) - { - if (ev->body.type == urids::midi_MidiEvent) - ++midi_count; - else if (ev->body.type == urids::atom_Object) - ++object_count; - else if (ev->body.type == urids::atom_Float) - ++float_count; - } - - BOOST_REQUIRE_EQUAL (midi_count, 2); - BOOST_REQUIRE_EQUAL (object_count, 1); - BOOST_REQUIRE_EQUAL (float_count, 1); -} - -BOOST_AUTO_TEST_CASE (pipe_single_buffer) -{ - AtomBuffer buffer; - element::AtomPipe pipe (buffer); - - BOOST_REQUIRE_EQUAL (pipe.size(), 1); - BOOST_REQUIRE_EQUAL (pipe.writeBuffer (0), &buffer); - BOOST_REQUIRE_EQUAL (pipe.readBuffer (0), &buffer); -} - -BOOST_AUTO_TEST_CASE (pipe_array_constructor) -{ - AtomBuffer b1, b2, b3; - AtomBuffer* buffers[] = { &b1, &b2, &b3 }; - - element::AtomPipe pipe (buffers, 3); - - BOOST_REQUIRE_EQUAL (pipe.size(), 3); - BOOST_REQUIRE_EQUAL (pipe.writeBuffer (0), &b1); - BOOST_REQUIRE_EQUAL (pipe.writeBuffer (1), &b2); - BOOST_REQUIRE_EQUAL (pipe.writeBuffer (2), &b3); - BOOST_REQUIRE_EQUAL (pipe.readBuffer (0), &b1); - BOOST_REQUIRE_EQUAL (pipe.readBuffer (1), &b2); - BOOST_REQUIRE_EQUAL (pipe.readBuffer (2), &b3); -} - -BOOST_AUTO_TEST_CASE (pipe_clear) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - juce::OwnedArray array; - auto b1 = array.add (new AtomBuffer()); - auto b2 = array.add (new AtomBuffer()); - - b1->setTypes (map); - b2->setTypes (map); - - auto msg = MidiMessage::noteOn (1, 60, 0.5f); - b1->insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - b2->insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - - juce::Array channels = { 0, 1 }; - element::AtomPipe pipe (array, channels); - - // Clear all buffers - pipe.clear(); - - int count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b1->sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); - - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b2->sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); - - // Test clear with range (should behave same as clear()) - b1->insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - b2->insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - pipe.clear (0, 512); - - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b1->sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); - - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b2->sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); - - array.clear (true); -} - -BOOST_AUTO_TEST_CASE (pipe_clear_single_channel) -{ - auto init = urids::makeMap(); - lvtk::Symbols sym (init); - auto map = (LV2_URID_Map*) sym.map_feature()->data; - - juce::OwnedArray array; - auto b1 = array.add (new AtomBuffer()); - auto b2 = array.add (new AtomBuffer()); - - b1->setTypes (map); - b2->setTypes (map); - - auto msg = MidiMessage::noteOn (1, 60, 0.5f); - b1->insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - b2->insert (0, msg.getRawDataSize(), urids::midi_MidiEvent, msg.getRawData()); - - juce::Array channels = { 0, 1 }; - element::AtomPipe pipe (array, channels); - - // Clear only channel 0 - pipe.clear (0, 0, 512); - - int count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b1->sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 0); - - count = 0; - LV2_ATOM_SEQUENCE_FOREACH (b2->sequence(), ev) - { - ++count; - } - BOOST_REQUIRE_EQUAL (count, 1); // b2 should still have event - - array.clear (true); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/audioroutingtests.cpp b/test/audioroutingtests.cpp index 06cf5e367..17b0f62f5 100644 --- a/test/audioroutingtests.cpp +++ b/test/audioroutingtests.cpp @@ -4,13 +4,11 @@ #include #include -#include #include "fixture/PreparedGraph.h" #include "fixture/TestNode.h" #include "engine/graphnode.hpp" #include "engine/ionode.hpp" -#include "utils.hpp" using namespace element; @@ -208,19 +206,19 @@ class AudioMixerNode : public TestNode { tempBuffer.setSize (1, numSamples, false, false, false); // For mixing with replace-processing: sum all inputs into temp buffer - const int maxInputs = jmin(numIns, totalChans); + const int maxInputs = jmin (numIns, totalChans); const int outputChan = 0; // Write result to first channel (replace-processing) // Clear temp buffer - float* temp = tempBuffer.getWritePointer(0); - FloatVectorOperations::clear(temp, numSamples); - + float* temp = tempBuffer.getWritePointer (0); + FloatVectorOperations::clear (temp, numSamples); + // Sum all inputs into temp buffer for (int inCh = 0; inCh < maxInputs; ++inCh) { const float* input = rc.audio.getReadPointer (inCh); FloatVectorOperations::add (temp, input, numSamples); } - + // Copy result to output channel float* output = rc.audio.getWritePointer (outputChan); FloatVectorOperations::copy (output, temp, numSamples); @@ -253,10 +251,8 @@ BOOST_AUTO_TEST_CASE (SingleAudioToSingleAudio) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify audio was received @@ -292,10 +288,8 @@ BOOST_AUTO_TEST_CASE (MultiAudioToSingleAudio) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify signals merged (3 x 0.25 = ~0.75) @@ -333,10 +327,8 @@ BOOST_AUTO_TEST_CASE (SingleAudioToMultiAudio) AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify all captures received identical audio @@ -377,10 +369,8 @@ BOOST_AUTO_TEST_CASE (AudioIsolation) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify each capture only received its corresponding generator @@ -413,10 +403,8 @@ BOOST_AUTO_TEST_CASE (DisconnectedAudioNode) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify capture received silence @@ -456,10 +444,8 @@ BOOST_AUTO_TEST_CASE (AudioThroughIONodes) } MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify both channels received external audio @@ -522,10 +508,8 @@ BOOST_AUTO_TEST_CASE (ComplexAudioRouting) data[i] = 0.2f; MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify each destination received correct sum @@ -588,10 +572,8 @@ BOOST_AUTO_TEST_CASE (AudioChainWithBranching) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify branching worked correctly @@ -656,10 +638,8 @@ BOOST_AUTO_TEST_CASE (AudioMultiLevelMerging) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify hierarchical merging @@ -715,10 +695,8 @@ BOOST_AUTO_TEST_CASE (AudioIOPlusGenerators) data[i] = 0.4f; MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify routing with I/O + generators @@ -750,10 +728,8 @@ BOOST_AUTO_TEST_CASE (AudioOutputIOUnused) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify unused audio output doesn't interfere @@ -785,10 +761,8 @@ BOOST_AUTO_TEST_CASE (AudioOutputIOConnected) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify internal routing still works with audio output connected @@ -833,10 +807,8 @@ BOOST_AUTO_TEST_CASE (AudioFullIOChain) data[i] = 0.35f; MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify routing: gen signal to dst1, audioIn signal to dst2 @@ -876,10 +848,8 @@ BOOST_AUTO_TEST_CASE (AudioMultipleOutputs) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify dst1 only gets gen2 signal, not gen1 @@ -909,10 +879,8 @@ BOOST_AUTO_TEST_CASE (AudioStereoRouting) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify both channels received audio @@ -946,10 +914,8 @@ BOOST_AUTO_TEST_CASE (AudioChannelCrossover) graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - RenderContext rc (audio, audio, midi, atom, 512); + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify crossover worked - channels should be swapped @@ -965,7 +931,7 @@ BOOST_AUTO_TEST_CASE (AudioChannelCrossover) BOOST_AUTO_TEST_CASE (AudioMixerNodeTest) { // Test dedicated mixer node summing multiple inputs - + PreparedGraph fix; GraphNode& graph = fix.graph; @@ -987,17 +953,15 @@ BOOST_AUTO_TEST_CASE (AudioMixerNodeTest) BOOST_REQUIRE (graph.connectChannels (PortType::Audio, gen1->nodeId, 0, mixer->nodeId, 0)); BOOST_REQUIRE (graph.connectChannels (PortType::Audio, gen2->nodeId, 0, mixer->nodeId, 1)); BOOST_REQUIRE (graph.connectChannels (PortType::Audio, gen3->nodeId, 0, mixer->nodeId, 2)); - + // Connect mixer output to destination BOOST_REQUIRE (graph.connectChannels (PortType::Audio, mixer->nodeId, 0, dst->nodeId, 0)); graph.rebuild(); AudioSampleBuffer audio (2, 512); MidiBuffer midi; - AtomBuffer atom; - atom.setTypes (element::test::context()->symbols().mapPtr()); - - RenderContext rc (audio, audio, midi, atom, 512); + + RenderContext rc (audio, audio, midi, 512); graph.render (rc); // Verify mixer summed all inputs (0.2 + 0.3 + 0.25 = 0.75) @@ -1039,7 +1003,7 @@ BOOST_AUTO_TEST_CASE (IONodeMinimumAudioPortCount) { // Test that Audio I/O nodes get a minimum port count when added to a graph with zero audio ports // This prevents the bug where Audio Input/Output nodes appear with zero ports - + PreparedGraph fix; GraphNode& graph = fix.graph; @@ -1050,13 +1014,13 @@ BOOST_AUTO_TEST_CASE (IONodeMinimumAudioPortCount) // Verify graph starts with zero audio ports configured BOOST_REQUIRE_EQUAL (graph.getNumPorts (PortType::Audio, true), 0); BOOST_REQUIRE_EQUAL (graph.getNumPorts (PortType::Audio, false), 0); - + // Add Audio Input node - should set graph audio inputs to minimum 2 (stereo) auto* audioIn = new IONode (IONode::audioInputNode); graph.addNode (audioIn); BOOST_REQUIRE_EQUAL (graph.getNumPorts (PortType::Audio, true), 2); BOOST_REQUIRE_EQUAL (audioIn->getNumPorts(), 2); - + // Add Audio Output node - should set graph audio outputs to minimum 2 (stereo) auto* audioOut = new IONode (IONode::audioOutputNode); graph.addNode (audioOut); diff --git a/test/fixture/AtomCaptureNode.h b/test/fixture/AtomCaptureNode.h deleted file mode 100644 index b7010574f..000000000 --- a/test/fixture/AtomCaptureNode.h +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (C) Kushview, LLC. -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "AtomTestNode.h" -#include -#include -#include - -namespace element { - -/** Captures atom events for verification */ -class AtomCaptureNode : public AtomTestNode { -public: - AtomCaptureNode() : AtomTestNode (0, 0, 1, 0) {} // 1 Atom input - - void render (RenderContext& rc) override - { - eventCount = 0; - - if (rc.atom.size() > 0) { - auto* atomBuf = rc.atom.readBuffer (0); - auto seq = atomBuf->sequence(); - - LV2_ATOM_SEQUENCE_FOREACH (seq, ev) - { - eventCount++; - } - } - } - - int getEventCount() const { return eventCount; } - void reset() { eventCount = 0; } - -private: - int eventCount = 0; -}; - -} // namespace element diff --git a/test/fixture/AtomTestNode.h b/test/fixture/AtomTestNode.h index 2417fefcc..722a2b44e 100644 --- a/test/fixture/AtomTestNode.h +++ b/test/fixture/AtomTestNode.h @@ -4,7 +4,6 @@ #pragma once #include -#include namespace element {