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