parent
e7f926e2c3
commit
f6dcd845fc
@ -0,0 +1,59 @@ |
|||||||
|
#include "audio_element.hpp" |
||||||
|
|
||||||
|
namespace audio { |
||||||
|
|
||||||
|
IAudioElement::IAudioElement() |
||||||
|
: input_events_(xQueueCreate(kEventQueueSize, sizeof(StreamEvent))), |
||||||
|
output_events_(nullptr), |
||||||
|
unprocessed_output_chunks_(0), |
||||||
|
buffered_output_(), |
||||||
|
current_state_(STATE_RUN) {} |
||||||
|
|
||||||
|
IAudioElement::~IAudioElement() { |
||||||
|
// Ensure we don't leak any memory from events leftover in the queue.
|
||||||
|
while (uxQueueSpacesAvailable(input_events_) < kEventQueueSize) { |
||||||
|
StreamEvent* event; |
||||||
|
if (xQueueReceive(input_events_, &event, 0)) { |
||||||
|
free(event); |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
// Technically there's a race here if someone is still adding to the queue,
|
||||||
|
// but hopefully the whole pipeline is stopped if an element is being
|
||||||
|
// destroyed.
|
||||||
|
vQueueDelete(input_events_); |
||||||
|
} |
||||||
|
|
||||||
|
auto IAudioElement::SendOrBufferEvent(std::unique_ptr<StreamEvent> event) |
||||||
|
-> bool { |
||||||
|
if (event->tag == StreamEvent::CHUNK_DATA) { |
||||||
|
unprocessed_output_chunks_++; |
||||||
|
} |
||||||
|
if (!buffered_output_.empty()) { |
||||||
|
// To ensure we send data in order, don't try to send if we've already
|
||||||
|
// failed to send something.
|
||||||
|
buffered_output_.push_back(std::move(event)); |
||||||
|
return false; |
||||||
|
} |
||||||
|
StreamEvent* raw_event = event.release(); |
||||||
|
if (!xQueueSend(output_events_, raw_event, 0)) { |
||||||
|
buffered_output_.emplace_front(raw_event); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
auto IAudioElement::FlushBufferedOutput() -> bool { |
||||||
|
while (!buffered_output_.empty()) { |
||||||
|
StreamEvent* raw_event = buffered_output_.front().release(); |
||||||
|
buffered_output_.pop_front(); |
||||||
|
if (!xQueueSend(output_events_, raw_event, 0)) { |
||||||
|
buffered_output_.emplace_front(raw_event); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace audio
|
@ -0,0 +1,57 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
|
||||||
|
#include "freertos/FreeRTOS.h" |
||||||
|
#include "freertos/queue.h" |
||||||
|
|
||||||
|
#include "span.hpp" |
||||||
|
#include "stream_info.hpp" |
||||||
|
|
||||||
|
namespace audio { |
||||||
|
|
||||||
|
struct StreamEvent { |
||||||
|
static auto CreateStreamInfo(QueueHandle_t source, |
||||||
|
std::unique_ptr<StreamInfo> payload) |
||||||
|
-> std::unique_ptr<StreamEvent>; |
||||||
|
static auto CreateChunkData(QueueHandle_t source, std::size_t chunk_size) |
||||||
|
-> std::unique_ptr<StreamEvent>; |
||||||
|
static auto CreateChunkNotification(QueueHandle_t source) |
||||||
|
-> std::unique_ptr<StreamEvent>; |
||||||
|
|
||||||
|
StreamEvent(); |
||||||
|
~StreamEvent(); |
||||||
|
StreamEvent(StreamEvent&&); |
||||||
|
|
||||||
|
QueueHandle_t source; |
||||||
|
|
||||||
|
enum { |
||||||
|
UNINITIALISED, |
||||||
|
STREAM_INFO, |
||||||
|
CHUNK_DATA, |
||||||
|
CHUNK_NOTIFICATION, |
||||||
|
} tag; |
||||||
|
|
||||||
|
union { |
||||||
|
std::unique_ptr<StreamInfo> stream_info; |
||||||
|
|
||||||
|
// Scott Meyers says:
|
||||||
|
// `About the only situation I can conceive of when a std::unique_ptr<T[]>
|
||||||
|
// would make sense would be when you’re using a C-like API that returns a
|
||||||
|
// raw pointer to a heap array that you assume ownership of.`
|
||||||
|
// :-)
|
||||||
|
|
||||||
|
struct { |
||||||
|
std::unique_ptr<std::byte*> raw_bytes; |
||||||
|
cpp::span<std::byte> bytes; |
||||||
|
} chunk_data; |
||||||
|
|
||||||
|
// FIXME: It would be nice to also support a pointer to himem data here, to
|
||||||
|
// save a little ordinary heap space.
|
||||||
|
}; |
||||||
|
|
||||||
|
StreamEvent(const StreamEvent&) = delete; |
||||||
|
StreamEvent& operator=(const StreamEvent&) = delete; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace audio
|
@ -0,0 +1,75 @@ |
|||||||
|
#include "stream_event.hpp" |
||||||
|
#include <cstddef> |
||||||
|
#include <memory> |
||||||
|
|
||||||
|
namespace audio { |
||||||
|
|
||||||
|
auto StreamEvent::CreateStreamInfo(QueueHandle_t source, |
||||||
|
std::unique_ptr<StreamInfo> payload) |
||||||
|
-> std::unique_ptr<StreamEvent> { |
||||||
|
auto event = std::make_unique<StreamEvent>(); |
||||||
|
event->tag = StreamEvent::STREAM_INFO; |
||||||
|
event->source = source; |
||||||
|
event->stream_info = std::move(payload); |
||||||
|
return event; |
||||||
|
} |
||||||
|
|
||||||
|
auto StreamEvent::CreateChunkData(QueueHandle_t source, std::size_t chunk_size) |
||||||
|
-> std::unique_ptr<StreamEvent> { |
||||||
|
auto event = std::make_unique<StreamEvent>(); |
||||||
|
event->tag = StreamEvent::CHUNK_DATA; |
||||||
|
event->source = source; |
||||||
|
|
||||||
|
auto raw_bytes = |
||||||
|
static_cast<std::byte*>(heap_caps_malloc(chunk_size, MALLOC_CAP_SPIRAM)); |
||||||
|
|
||||||
|
event->chunk_data.raw_bytes = std::make_unique<std::byte*>(raw_bytes); |
||||||
|
event->chunk_data.bytes = cpp::span<std::byte>(raw_bytes, chunk_size); |
||||||
|
|
||||||
|
return event; |
||||||
|
} |
||||||
|
|
||||||
|
auto StreamEvent::CreateChunkNotification(QueueHandle_t source) |
||||||
|
-> std::unique_ptr<StreamEvent> { |
||||||
|
auto event = std::make_unique<StreamEvent>(); |
||||||
|
event->tag = StreamEvent::CHUNK_NOTIFICATION; |
||||||
|
event->source = source; |
||||||
|
return event; |
||||||
|
} |
||||||
|
|
||||||
|
StreamEvent::StreamEvent() : tag(StreamEvent::UNINITIALISED) {} |
||||||
|
|
||||||
|
StreamEvent::~StreamEvent() { |
||||||
|
switch (tag) { |
||||||
|
case UNINITIALISED: |
||||||
|
break; |
||||||
|
case STREAM_INFO: |
||||||
|
stream_info.reset(); |
||||||
|
break; |
||||||
|
case CHUNK_DATA: |
||||||
|
chunk_data.raw_bytes.reset(); |
||||||
|
break; |
||||||
|
case CHUNK_NOTIFICATION: |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StreamEvent::StreamEvent(StreamEvent&& other) { |
||||||
|
tag = other.tag; |
||||||
|
source = other.source; |
||||||
|
switch (tag) { |
||||||
|
case UNINITIALISED: |
||||||
|
break; |
||||||
|
case STREAM_INFO: |
||||||
|
stream_info = std::move(other.stream_info); |
||||||
|
break; |
||||||
|
case CHUNK_DATA: |
||||||
|
chunk_data = std::move(other.chunk_data); |
||||||
|
break; |
||||||
|
case CHUNK_NOTIFICATION: |
||||||
|
break; |
||||||
|
} |
||||||
|
other.tag = StreamEvent::UNINITIALISED; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace audio
|
Loading…
Reference in new issue