Phase 3 (L8). Pure statement-for-statement relocation into dsp/ratio_seq.hpp, dsp/seq_clock.hpp and core/event_queue.hpp. AUDIT CORRECTION: L8 says "breakor and elysiamorf duplicate ratio_seq". That is false — ElysiamorfEngine has no ratio_seq at all; it triggers continuously via FM operators. The real duplicate pair is BreakOrEngine and MEMLCeliumEngine, whose copies are byte-for-byte identical. Elysiamorf did share the clock and event-queue machinery, so it uses those. memlcelium now includes the shared ratio_seq too, which is what actually closes this finding. Deliberately NOT folded into core/ring_buffer.hpp: RingBuffer is an atomics-based cross-core SPSC channel (its header says so), whereas the engines' event queue is produced and drained on one thread. Reusing it would have meant paying for atomics to serve a single-threaded FIFO. The distinction is now recorded in MAP.md so the next audit does not read them as duplicates. Bit-exactness: verified the MIDI-clock tick and bar-phasor tick preserve the original operation order with no floating-point re-association, and that EventQueue keeps the original `% N` indexing rather than adopting RingBuffer's bitmask. The golden suite (nisps_golden_tests) and the native<->WASM parity blob both pass unchanged — they are the check, and they were not re-baselined.
73 lines
2.6 KiB
C++
73 lines
2.6 KiB
C++
// nisps/core/event_queue.hpp — same-thread, batch-drain FIFO for engine
|
|
// output events (NoteOn/NoteOff/Clock/CC, ...).
|
|
//
|
|
// This is NOT a replacement for RingBuffer (nisps/core/ring_buffer.hpp).
|
|
// RingBuffer is an atomics-based SPSC channel for genuine cross-thread /
|
|
// cross-core hand-off (its own header: "the inter-core hand-off can wrap
|
|
// this OR use queue_t directly"). Sequencer engines push events from inside
|
|
// `process()` and the mode layer drains them via `pop_events()` right after
|
|
// — same call chain, same thread, never concurrent — so there is nothing to
|
|
// synchronize. Reusing RingBuffer here would add atomic load/store traffic
|
|
// to the audio-hot `process()` path for no correctness benefit, and would
|
|
// still need a wrapping loop to get the "drain up to N in one call" batch
|
|
// shape `pop_events()` callers rely on (RingBuffer::try_pop is one element
|
|
// at a time). EventQueue is deliberately the plain, non-atomic version of
|
|
// that shape.
|
|
//
|
|
// Extracted from the byte-for-byte-identical event-queue member blocks
|
|
// previously duplicated in nisps/engines/breakor.hpp and
|
|
// nisps/engines/elysiamorf.hpp (2026-07 simplification audit, finding L8).
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <span>
|
|
#include <type_traits>
|
|
|
|
#include "perf.hpp"
|
|
|
|
namespace nisps {
|
|
|
|
template <typename T, std::size_t N>
|
|
class EventQueue {
|
|
static_assert(N > 0u, "EventQueue capacity must be > 0");
|
|
static_assert(std::is_trivially_copyable_v<T>,
|
|
"EventQueue element type must be trivially copyable");
|
|
|
|
public:
|
|
static constexpr std::size_t capacity() noexcept { return N; }
|
|
|
|
// Enqueues one event. Drops silently on overflow (matches the engines'
|
|
// original push_event behaviour — a full event queue on a stalled
|
|
// consumer should not stall or branch the audio-hot producer).
|
|
NISPS_FORCE_INLINE void push(const T& e) noexcept {
|
|
if (count_ >= N) return;
|
|
buf_[write_] = e;
|
|
write_ = (write_ + 1u) % N;
|
|
++count_;
|
|
}
|
|
|
|
// Drains up to `out.size()` queued events into `out`. Returns the number
|
|
// actually copied.
|
|
std::size_t pop(std::span<T> out) noexcept {
|
|
std::size_t n = 0u;
|
|
while (n < out.size() && count_ > 0u) {
|
|
out[n++] = buf_[read_];
|
|
read_ = (read_ + 1u) % N;
|
|
--count_;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
std::size_t size() const noexcept { return count_; }
|
|
bool empty() const noexcept { return count_ == 0u; }
|
|
|
|
private:
|
|
std::array<T, N> buf_{};
|
|
std::size_t read_ = 0u;
|
|
std::size_t write_ = 0u;
|
|
std::size_t count_ = 0u;
|
|
};
|
|
|
|
} // namespace nisps
|