#include "consumption_diagnostic.h" #pragma once #include "third_party/yeney-core/core/player.h" #include "pcm_pack.h" #include #include #include #include #include #include #include namespace timing_probe { class TimingTap; } // Only the feeder may wait for an HTTP consumer. Event-loop callbacks only // copy bounded batches and update state under short-lived locks. class CoreSonosSink : public yeney::Sink { struct Batch { std::vector pcm; uint64_t first, generation; size_t frames; bool boundary, continuous; unsigned rate; uint64_t shmGeneration=0, shmFirst=0; bool exported=false; }; static constexpr size_t capacity = 48000; mutable std::mutex mutex; std::condition_variable changed; std::deque queue; std::deque starts; std::thread feeder; std::atomic running{true}; std::atomic generation{0}; PcmDither dither; size_t staged = 0; bool rateChangedStream = false; mutable bool awaitingStream = false; uint64_t heldPosition = 0, heldAt = 0; unsigned heldRate = 0; bool paused = false, pending = false, continuous = false, inflight = false; unsigned rate = 44100, streamRate = 0; uint64_t next = 0, streamBase = 0, handed = 0, started = 0; mutable uint64_t audible = 0; std::unique_ptr tap; uint64_t (*clockMs)(); void feed(); public: explicit CoreSonosSink(const uint8_t* mac=nullptr, uint64_t (*clock)()=nullptr); ~CoreSonosSink() override; void command(const yeney::Command&); uint32_t maxSampleRate() const override; unsigned outputRate() const { std::lock_guard lock(mutex); return streamRate; } void trackBoundary(uint64_t, const yeney::Format&, bool) override; size_t write(const yeney::Frame*, size_t) override; size_t write(const yeney::Frame*, size_t, bool processed) override; void pause() override; void resume() override; void stop() override; void flush() override; uint64_t audibleFrames() const override; uint64_t startedFrames() const override; bool drained(uint64_t) const override; bool outputEmpty(uint64_t) const override; void volume(uint32_t, uint32_t) override {} void power(bool) override {} // Remote Sonos has no local pacer schedule. Keep the core's optional // playTiming callback unset; encoder/HTTP handoff time is not audible time. void contentPosition(uint64_t frame, bool known) override { bridge::consumptionContent().set(frame, known); } bool paced() const override { return false; } }; void runCoreClient(const char* server, const uint8_t* mac, const char* name); extern "C" int sonos_output_running();