summaryrefslogtreecommitdiff
path: root/src/audio/chunk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio/chunk.cpp')
-rw-r--r--src/audio/chunk.cpp115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/audio/chunk.cpp b/src/audio/chunk.cpp
new file mode 100644
index 00000000..a8864930
--- /dev/null
+++ b/src/audio/chunk.cpp
@@ -0,0 +1,115 @@
+#include "chunk.hpp"
+
+#include "cbor_encoder.hpp"
+#include "cbor_decoder.hpp"
+#include <string.h>
+#include <cstdint>
+#include "esp-idf/components/cbor/tinycbor/src/cbor.h"
+#include "stream_message.hpp"
+
+namespace audio {
+
+/*
+ * The maximum size that we expect a header to take up.
+ */
+// TODO: tune this.
+static const size_t kMaxHeaderSize = 64;
+
+auto WriteChunksToStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeWriteResult {
+ while (1) {
+ // First, ask the callback for some data to write.
+ size_t chunk_size =
+ callback(
+ working_buffer + kMaxHeaderSize,
+ working_buffer_length - kMaxHeaderSize);
+
+ if (chunk_size == 0) {
+ // They had nothing for us, so bail out.
+ return CHUNK_OUT_OF_DATA;
+ }
+
+ // Put together a header.
+ cbor::Encoder encoder(cbor::CONTAINER_ARRAY, 3, working_buffer, working_buffer_length);
+ encoder.WriteUnsigned(TYPE_CHUNK_HEADER);
+ // Note here that we need to write the offset of the chunk into the header.
+ // We could be smarter here and write the actual header size, allowing us to
+ // pack slightly more data into each message, but this is hard so I haven't
+ // done it. Please make my code better for me.
+ encoder.WriteUnsigned(kMaxHeaderSize);
+ encoder.WriteUnsigned(chunk_size);
+ if (encoder.Finish().has_error()) {
+ return CHUNK_ENCODING_ERROR;
+ };
+
+ // Try to write to the buffer. Note the return type here will be either 0 or
+ // kMaxHeaderSize + chunk_size, as MessageBuffer doesn't allow partial
+ // writes.
+ size_t actual_write_size =
+ xMessageBufferSend(
+ *stream, working_buffer, kMaxHeaderSize + chunk_size, max_wait);
+
+ if (actual_write_size == 0) {
+ // We failed to write in time, so bail out. This is techinically data loss
+ // unless the caller wants to go and parse our working buffer, but we
+ // assume the caller has a good reason to time us out.
+ return CHUNK_WRITE_TIMEOUT;
+ }
+ }
+}
+
+auto ReadChunksFromStream(MessageBufferHandle_t *stream, uint8_t *working_buffer, size_t working_buffer_length, std::function<size_t(uint8_t*,size_t)> callback, TickType_t max_wait) -> EncodeReadResult {
+ // Spillover if the previous iteration did not consume all of the input.
+ size_t leftover_bytes = 0;
+ while (1) {
+ // First, wait for a message to arrive over the buffer.
+ size_t read_size =
+ xMessageBufferReceive(
+ *stream, working_buffer + leftover_bytes, working_buffer_length - leftover_bytes, max_wait);
+
+ if (read_size == 0) {
+ return CHUNK_READ_TIMEOUT;
+ }
+
+ auto decoder = cbor::MapDecoder::Create(working_buffer + leftover_bytes, read_size);
+ if (decoder.has_error()) {
+ // Weird; this implies someone is shoving invalid data into the buffer.
+ return CHUNK_DECODING_ERROR;
+ }
+
+ MessageType type = decoder.value().ParseUnsigned().value_or(TYPE_UNKNOWN);
+ if (type != TYPE_CHUNK_HEADER) {
+ // This message wasn't for us, so put it in a consistent place and let the
+ // caller handle it.
+ memmove(working_buffer, working_buffer + leftover_bytes, read_size);
+ return CHUNK_STREAM_ENDED;
+ }
+
+ // Work the size and position of the chunk (don't assume it's at
+ // kMaxHeaderSize offset for future-proofing).
+ header_length = decoder.ParseUnsigned().value_or(0);
+ chunk_length = decoder.ParseUnsigned().value_or(0);
+ if (decoder.Failed()) {
+ return CHUNK_DECODING_ERROR;
+ }
+
+ // Now we need to stick the end of the last chunk (if it exists) onto the
+ // front of the new chunk. Do it this way around bc we assume the old chunk
+ // is shorter, and therefore faster to move.
+ uint8_t *combined_buffer = working_buffer + header_length - leftover_bytes;
+ size_t combined_buffer_size = leftover_bytes + chunk_length;
+ if (leftover_bytes > 0) {
+ memmove(combined_buffer, working_buffer, leftover_bytes);
+ }
+
+ // Tell the callback about the new data.
+ size_t amount_processed = callback(combined_buffer, combined_buffer_size);
+
+ // Prepare for the next iteration.
+ leftover_bytes = combined_buffer_size - amount_processed;
+ if (leftover_bytes > 0) {
+ memmove(working_buffer, combined_buffer + amount_processed, leftover_bytes);
+ }
+ }
+}
+
+} // namespace audio