A module-audio/Audio/AbstractStream.hpp => module-audio/Audio/AbstractStream.hpp +235 -0
@@ 0,0 1,235 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "AudioFormat.hpp"
+
+#include <cstdint>
+
+namespace audio
+{
+ /**
+ * @brief An abstract interface for classes implementing data connection
+ * between two endpoints. The data is stored in blocks and accessed just
+ * like in a regular FIFO. It provides an API to implement a zero-copy
+ * mechanism.
+ *
+ */
+ class AbstractStream
+ {
+ public:
+ /**
+ * @brief Events that are broadcasted to listeners by an AbstractStream
+ *
+ */
+ enum class Event
+ {
+ NoEvent,
+ StreamFull,
+ StreamHalfUsed,
+ StreamEmpty,
+ StreamOverflow,
+ StreamUnderFlow
+ };
+
+ /**
+ * @brief An abstract interface for AbstractStream's event listeners
+ *
+ */
+ class EventListener
+ {
+ public:
+ /**
+ * @brief Called on event by AbstractStream
+ *
+ * @param stream - stream reporting the event
+ * @param event - type of event
+ */
+ virtual void onEvent(AbstractStream *stream, Event event) = 0;
+ };
+
+ /**
+ * @brief A structure describing a range of raw data using a pair of
+ * pointer and the size of data.
+ */
+ struct Span
+ {
+ /**
+ * @brief Pointer to raw data.
+ */
+ std::uint8_t *data = nullptr;
+
+ /**
+ * @brief Size of data
+ */
+ std::size_t dataSize = 0;
+
+ /**
+ * @brief Calculate pointer to the end of the raw buffer
+ *
+ * @return pointer to the end of data.
+ */
+ constexpr std::uint8_t *dataEnd() const noexcept
+ {
+ return data + dataSize;
+ }
+
+ /**
+ * @brief Resets the stored value to Null
+ *
+ */
+ constexpr void reset() noexcept
+ {
+ data = nullptr;
+ dataSize = 0;
+ }
+
+ inline bool operator==(const Span &other) const noexcept
+ {
+ return data == other.data && dataSize == other.dataSize;
+ }
+
+ inline bool operator!=(const Span &other) const noexcept
+ {
+ return !operator==(other);
+ }
+ };
+
+ /**
+ * @brief A structure describing characteristics of an AbstractStream's
+ * endpoint.
+ *
+ */
+ struct Traits
+ {
+ /**
+ * @brief the size of data to read/write in a single operation
+ */
+ std::size_t blockSize = 0;
+
+ /**
+ * @brief the format of data stored in the AbstractStream
+ */
+ AudioFormat format = nullFormat;
+ };
+
+ virtual ~AbstractStream() = default;
+
+ /**
+ * @brief Registers events listener
+ *
+ * @param listener to register
+ */
+ virtual void registerListener(EventListener *listener) = 0;
+
+ /**
+ * @brief Unregisters events listener
+ *
+ * @param listener to unregister
+ */
+ virtual void unregisterListeners(EventListener *listener) = 0;
+
+ /**
+ * @brief Fills single block with data provided
+ *
+ * @param data - a pointer to raw data to be copied from
+ * @param dataSize - size of the data
+ * @return true if operation succeded, false otherwise
+ * @return false
+ */
+ virtual bool push(void *data, std::size_t dataSize) = 0;
+
+ /**
+ * @brief Fills single block with data provided
+ *
+ * @param span describing block of data to be copied
+ *
+ * @return true if operation succeded, false otherwise
+ */
+ virtual bool push(const Span &span) = 0;
+
+ /**
+ * @brief Fills single block with empty data
+ *
+ * @return true if operation succeded, false otherwise
+ */
+ virtual bool push() = 0;
+
+ /**
+ * @brief Copies one block of data to a buffer provided as an argument
+ *
+ * @param span - a place to copy stream's data to
+ *
+ * @return true if operation succeded, false otherwise
+ */
+ virtual bool pop(Span &span) = 0;
+
+ /**
+ * @brief Reserves a block in a stream for writing
+ *
+ * @param span - a space reserved within the stream
+ * @return true if operation succeded, false otherwise
+ */
+ virtual bool reserve(Span &span) = 0;
+
+ /**
+ * @brief Marks reserved block as ready and writes it to the stream.
+ */
+ virtual void commit() = 0;
+
+ /**
+ * @brief Discards data written to the reserved block
+ */
+ virtual void release() = 0;
+
+ /**
+ * @brief Gets pointer and size of data ready to read without copying.
+ *
+ * @param span
+ *
+ * @return true if operation succeded, false otherwise
+ */
+ virtual bool peek(Span &span) = 0;
+
+ /**
+ * @brief Marks peeked data as read.
+ */
+ virtual void consume() = 0;
+
+ /**
+ * @brief Reset peek state without consuming it.
+ */
+ virtual void unpeek() = 0;
+
+ /**
+ * @brief Resets the state of a stream; discards all data
+ */
+ virtual void reset() = 0;
+
+ /**
+ * @brief Get the traits of the stream's input.
+ *
+ * @return Traits
+ */
+ [[nodiscard]] virtual auto getInputTraits() const noexcept -> Traits = 0;
+
+ /**
+ * @brief Get the traits of the stream's input.
+ *
+ * @return Traits
+ */
+ [[nodiscard]] virtual auto getOutputTraits() const noexcept -> Traits = 0;
+
+ /**
+ * @brief Checks if stream is empty.
+ */
+ [[nodiscard]] virtual bool isEmpty() const noexcept = 0;
+
+ /**
+ * @brief Checks if stream is full.
+ */
+ [[nodiscard]] virtual bool isFull() const noexcept = 0;
+ };
+
+}; // namespace audio
M module-audio/Audio/Operation/RouterOperation.hpp => module-audio/Audio/Operation/RouterOperation.hpp +1 -0
@@ 10,6 10,7 @@
#include <Audio/AudioCommon.hpp>
#include <Audio/Profiles/Profile.hpp>
#include <Audio/Endpoint.hpp>
+#include <Audio/Stream.hpp>
#include <mutex.hpp>
M module-audio/Audio/Stream.cpp => module-audio/Audio/Stream.cpp +19 -18
@@ 1,17 1,15 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "Stream.hpp"
-#include <macros.h>
-
#include <algorithm>
#include <iterator>
using namespace audio;
-Stream::Stream(Allocator &allocator, std::size_t blockSize, unsigned int bufferingSize)
- : _allocator(allocator), _blockSize(blockSize), _blockCount(bufferingSize),
+Stream::Stream(AudioFormat format, Allocator &allocator, std::size_t blockSize, unsigned int bufferingSize)
+ : _allocator(allocator), _blockSize(blockSize), _blockCount(bufferingSize), _format(format),
_buffer(_allocator.allocate(_blockSize * _blockCount)), _emptyBuffer(_allocator.allocate(_blockSize)),
_dataStart(_buffer.get(), _blockSize * _blockCount, _buffer.get(), _blockSize), _dataEnd(_dataStart),
_peekPosition(_dataStart), _writeReservationPosition(_dataStart)
@@ 169,21 167,31 @@ void Stream::release()
_writeReservationPosition = _dataEnd;
}
-std::size_t Stream::getBlockSize() const noexcept
+auto Stream::getInputTraits() const noexcept -> Traits
{
LockGuard lock;
+ return getIOTraits();
+}
- return _blockSize;
+auto Stream::getOutputTraits() const noexcept -> Traits
+{
+ LockGuard lock;
+ return getIOTraits();
}
-void Stream::registerListener(EventListener *listener)
+auto Stream::getIOTraits() const noexcept -> Traits
+{
+ return Traits{.blockSize = _blockSize, .format = _format};
+}
+
+void Stream::registerListener(AbstractStream::EventListener *listener)
{
LockGuard lock;
listeners.push_back(std::ref(listener));
}
-void Stream::unregisterListeners(Stream::EventListener *listener)
+void Stream::unregisterListeners(AbstractStream::EventListener *listener)
{
LockGuard lock;
@@ 195,10 203,8 @@ void Stream::unregisterListeners(Stream::EventListener *listener)
void Stream::broadcastEvent(Event event)
{
- auto eventMode = isIRQ() ? EventSourceMode::ISR : EventSourceMode::Thread;
-
for (auto listener : listeners) {
- listener->onEvent(this, event, eventMode);
+ listener->onEvent(this, event);
}
}
@@ 251,7 257,7 @@ bool Stream::isFull() const noexcept
bool Stream::blocksAvailable() const noexcept
{
- return !isEmpty();
+ return !isFull();
}
void Stream::reset()
@@ 338,11 344,6 @@ Stream::Span Stream::RawBlockIterator::operator*()
return Stream::Span{.data = _curPos, .dataSize = _stepSize};
}
-std::uint8_t *Stream::Span::dataEnd() const noexcept
-{
- return data + dataSize;
-}
-
Stream::Span Stream::getNullSpan() const noexcept
{
return Span{.data = _emptyBuffer.get(), .dataSize = _blockSize};
M module-audio/Audio/Stream.hpp => module-audio/Audio/Stream.hpp +32 -51
@@ 1,8 1,11 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
+#include "AbstractStream.hpp"
+#include "AudioFormat.hpp"
+
#include <memory/NonCachedMemAllocator.hpp>
#include <CriticalSectionGuard.hpp>
@@ 15,18 18,11 @@
namespace audio
{
- class Stream
+ class Stream : public AbstractStream
{
public:
using UniqueStreamBuffer = std::unique_ptr<std::uint8_t[], std::function<void(uint8_t[])>>;
-
- struct Span
- {
- std::uint8_t *data = nullptr;
- std::size_t dataSize = 0;
-
- std::uint8_t *dataEnd() const noexcept;
- };
+ using AbstractStream::Span;
class RawBlockIterator
{
@@ 54,72 50,56 @@ namespace audio
virtual UniqueStreamBuffer allocate(std::size_t size) = 0;
};
- enum class Event
- {
- NoEvent,
- StreamFull,
- StreamHalfUsed,
- StreamEmpty,
- StreamOverflow,
- StreamUnderFlow
- };
-
- enum class EventSourceMode
- {
- ISR,
- Thread
- };
-
- class EventListener
- {
- public:
- virtual void onEvent(Stream *stream, Event event, EventSourceMode source) = 0;
- };
-
static constexpr auto defaultBufferingSize = 4U;
- Stream(Allocator &allocator, std::size_t blockSize, unsigned int bufferingSize = defaultBufferingSize);
+ Stream(AudioFormat format,
+ Allocator &allocator,
+ std::size_t blockSize,
+ unsigned int bufferingSize = defaultBufferingSize);
+
+ void registerListener(AbstractStream::EventListener *listener) override;
+ void unregisterListeners(AbstractStream::EventListener *listener) override;
/// push
- bool push(void *data, std::size_t dataSize);
- bool push(const Span &span);
- bool push();
+ bool push(void *data, std::size_t dataSize) override;
+ bool push(const Span &span) override;
+ bool push() override;
/// pop
- bool pop(Span &span);
+ bool pop(Span &span) override;
/// zero copy write
- bool reserve(Span &span);
- void commit();
- void release();
+ bool reserve(Span &span) override;
+ void commit() override;
+ void release() override;
/// zero copy read
- bool peek(Span &span);
- void consume();
- void unpeek();
+ bool peek(Span &span) override;
+ void consume() override;
+ void unpeek() override;
+
+ void reset() override;
/// get empty data span
Span getNullSpan() const noexcept;
- void reset();
+ [[nodiscard]] auto getInputTraits() const noexcept -> Traits override;
+ [[nodiscard]] auto getOutputTraits() const noexcept -> Traits override;
+ [[nodiscard]] bool isEmpty() const noexcept override;
+ [[nodiscard]] bool isFull() const noexcept override;
- [[nodiscard]] std::size_t getBlockSize() const noexcept;
[[nodiscard]] std::size_t getBlockCount() const noexcept;
[[nodiscard]] std::size_t getUsedBlockCount() const noexcept;
[[nodiscard]] std::size_t getPeekedCount() const noexcept;
[[nodiscard]] std::size_t getReservedCount() const noexcept;
- [[nodiscard]] bool isEmpty() const noexcept;
- [[nodiscard]] bool isFull() const noexcept;
[[nodiscard]] bool blocksAvailable() const noexcept;
- void registerListener(EventListener *listener);
- void unregisterListeners(EventListener *listener);
-
private:
using LockGuard = cpp_freertos::CriticalSectionGuard;
void broadcastEvent(Event event);
void broadcastStateEvents();
+ auto getIOTraits() const noexcept -> Traits;
Allocator &_allocator;
std::size_t _blockSize = 0;
@@ 127,9 107,10 @@ namespace audio
std::size_t _blocksUsed = 0;
std::size_t _peekCount = 0;
std::size_t _reserveCount = 0;
+ AudioFormat _format = nullFormat;
UniqueStreamBuffer _buffer;
UniqueStreamBuffer _emptyBuffer;
- std::list<EventListener *> listeners;
+ std::list<AbstractStream::EventListener *> listeners;
RawBlockIterator _dataStart;
RawBlockIterator _dataEnd;
M module-audio/Audio/StreamFactory.cpp => module-audio/Audio/StreamFactory.cpp +4 -2
@@ 18,11 18,13 @@ StreamFactory::StreamFactory(Endpoint::Capabilities factoryCaps, unsigned int bu
: caps(std::move(factoryCaps)), bufferingSize(bufferingSize)
{}
-auto StreamFactory::makeStream(const Source &source, const Sink &sink) -> std::unique_ptr<Stream>
+auto StreamFactory::makeStream(Source &source, Sink &sink) -> std::unique_ptr<Stream>
{
auto negotiatedCaps = negotiateCaps({source, sink});
+ auto format = source.getSourceFormat();
- return std::make_unique<Stream>(getAllocator(negotiatedCaps.usesDMA), negotiatedCaps.maxBlockSize, bufferingSize);
+ return std::make_unique<Stream>(
+ format, getAllocator(negotiatedCaps.usesDMA), negotiatedCaps.maxBlockSize, bufferingSize);
}
auto StreamFactory::negotiateCaps(std::vector<std::reference_wrapper<const Endpoint>> v) -> Endpoint::Capabilities
M module-audio/Audio/StreamFactory.hpp => module-audio/Audio/StreamFactory.hpp +2 -1
@@ 4,6 4,7 @@
#pragma once
#include "Endpoint.hpp"
+#include "Stream.hpp"
#include <memory>
#include <vector>
@@ 16,7 17,7 @@ namespace audio
public:
explicit StreamFactory(Endpoint::Capabilities factoryCaps,
unsigned int bufferingSize = Stream::defaultBufferingSize);
- auto makeStream(const Source &source, const Sink &sink) -> std::unique_ptr<Stream>;
+ auto makeStream(Source &source, Sink &sink) -> std::unique_ptr<Stream>;
private:
auto negotiateCaps(std::vector<std::reference_wrapper<const Endpoint>> v) -> Endpoint::Capabilities;
A module-audio/Audio/StreamProxy.cpp => module-audio/Audio/StreamProxy.cpp +100 -0
@@ 0,0 1,100 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "StreamProxy.hpp"
+#include "AbstractStream.hpp"
+
+using audio::StreamProxy;
+
+StreamProxy::StreamProxy(AbstractStream &wrappedStream) noexcept : wrappedStream(wrappedStream)
+{}
+
+bool StreamProxy::push(void *data, std::size_t dataSize)
+{
+ return wrappedStream.push(data, dataSize);
+}
+
+bool StreamProxy::push(const Span &span)
+{
+ return wrappedStream.push(span);
+}
+
+bool StreamProxy::push()
+{
+ return wrappedStream.push();
+}
+
+bool StreamProxy::pop(Span &span)
+{
+ return wrappedStream.pop(span);
+}
+
+bool StreamProxy::reserve(Span &span)
+{
+ return wrappedStream.reserve(span);
+}
+
+void StreamProxy::commit()
+{
+ wrappedStream.commit();
+}
+
+void StreamProxy::release()
+{
+ wrappedStream.release();
+}
+
+bool StreamProxy::peek(Span &span)
+{
+ return wrappedStream.peek(span);
+}
+
+void StreamProxy::consume()
+{
+ wrappedStream.consume();
+}
+
+void StreamProxy::unpeek()
+{
+ wrappedStream.unpeek();
+}
+
+void StreamProxy::reset()
+{
+ wrappedStream.reset();
+}
+
+void StreamProxy::registerListener(EventListener *listener)
+{
+ wrappedStream.registerListener(listener);
+}
+
+void StreamProxy::unregisterListeners(EventListener *listener)
+{
+ wrappedStream.unregisterListeners(listener);
+}
+
+auto StreamProxy::getOutputTraits() const noexcept -> Traits
+{
+ return wrappedStream.getOutputTraits();
+}
+
+auto StreamProxy::getInputTraits() const noexcept -> Traits
+{
+ return wrappedStream.getInputTraits();
+}
+
+bool StreamProxy::isEmpty() const noexcept
+{
+ return wrappedStream.isEmpty();
+}
+
+bool StreamProxy::isFull() const noexcept
+{
+ return wrappedStream.isFull();
+}
+
+auto StreamProxy::getWrappedStream() -> AbstractStream &
+{
+ return wrappedStream;
+}
A module-audio/Audio/StreamProxy.hpp => module-audio/Audio/StreamProxy.hpp +61 -0
@@ 0,0 1,61 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "AbstractStream.hpp"
+
+namespace audio
+{
+
+ /**
+ * @brief Proxy for the AbstractStream class allowing to wrap its methods
+ * with custom logic.
+ */
+ class StreamProxy : public AbstractStream
+ {
+ public:
+ /**
+ * @brief Construct a new Stream Proxy object
+ *
+ * @param wrappedStream - stream to wrap.
+ */
+ explicit StreamProxy(AbstractStream &wrappedStream) noexcept;
+
+ // listener registration
+ void registerListener(EventListener *listener) override;
+ void unregisterListeners(EventListener *listener) override;
+
+ /// push
+ bool push(void *data, std::size_t dataSize) override;
+ bool push(const Span &span) override;
+ bool push() override;
+
+ /// pop
+ bool pop(Span &span) override;
+
+ /// zero copy write
+ bool reserve(Span &span) override;
+ void commit() override;
+ void release() override;
+
+ /// zero copy read
+ bool peek(Span &span) override;
+ void consume() override;
+ void unpeek() override;
+
+ void reset() override;
+
+ [[nodiscard]] auto getInputTraits() const noexcept -> Traits override;
+ [[nodiscard]] auto getOutputTraits() const noexcept -> Traits override;
+ [[nodiscard]] bool isEmpty() const noexcept override;
+ [[nodiscard]] bool isFull() const noexcept override;
+
+ protected:
+ auto getWrappedStream() -> AbstractStream &;
+
+ private:
+ AbstractStream &wrappedStream;
+ };
+
+} // namespace audio
M module-audio/Audio/StreamQueuedEventsListener.cpp => module-audio/Audio/StreamQueuedEventsListener.cpp +8 -4
@@ 1,20 1,24 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "StreamQueuedEventsListener.hpp"
-using namespace audio;
+#include <macros.h>
+
+#include <utility>
+
+using audio::StreamQueuedEventsListener;
StreamQueuedEventsListener::StreamQueuedEventsListener(std::shared_ptr<cpp_freertos::Queue> eventsQueue)
: queue(eventsQueue)
{}
-void StreamQueuedEventsListener::onEvent(Stream *stream, Stream::Event event, Stream::EventSourceMode source)
+void StreamQueuedEventsListener::onEvent(AbstractStream *stream, Stream::Event event)
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
EventStorage newStorage = {stream, event};
- if (source == Stream::EventSourceMode::ISR) {
+ if (isIRQ()) {
queue->EnqueueFromISR(&newStorage, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken) {
taskYIELD();
M module-audio/Audio/StreamQueuedEventsListener.hpp => module-audio/Audio/StreamQueuedEventsListener.hpp +6 -6
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
@@ 18,18 18,18 @@ namespace audio
private:
struct EventStorage
{
- Stream *stream = nullptr;
- Stream::Event event = Stream::Event::NoEvent;
+ AbstractStream *stream = nullptr;
+ AbstractStream::Event event = AbstractStream::Event::NoEvent;
};
public:
using queueInfo = std::pair<QueueHandle_t, std::string>;
- using queuedEvent = std::pair<Stream *, Stream::Event>;
+ using queuedEvent = std::pair<AbstractStream *, AbstractStream::Event>;
static constexpr auto listenerElementSize = sizeof(EventStorage);
- StreamQueuedEventsListener(std::shared_ptr<cpp_freertos::Queue> eventsQueue);
+ explicit StreamQueuedEventsListener(std::shared_ptr<cpp_freertos::Queue> eventsQueue);
- void onEvent(Stream *stream, Stream::Event event, Stream::EventSourceMode source);
+ void onEvent(AbstractStream *stream, Stream::Event event) override;
queuedEvent waitForEvent();
queuedEvent getEvent();
M module-audio/Audio/decoder/DecoderWorker.cpp => module-audio/Audio/decoder/DecoderWorker.cpp +5 -4
@@ 1,16 1,17 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "DecoderWorker.hpp"
-#include "Audio/decoder/Decoder.hpp"
+#include <Audio/AbstractStream.hpp>
+#include <Audio/decoder/Decoder.hpp>
-audio::DecoderWorker::DecoderWorker(Stream *audioStreamOut,
+audio::DecoderWorker::DecoderWorker(audio::AbstractStream *audioStreamOut,
Decoder *decoder,
EndOfFileCallback endOfFileCallback,
ChannelMode mode)
: sys::Worker(DecoderWorker::workerName, DecoderWorker::workerPriority, stackDepth), audioStreamOut(audioStreamOut),
decoder(decoder), endOfFileCallback(endOfFileCallback),
- bufferSize(audioStreamOut->getBlockSize() / sizeof(BufferInternalType)), channelMode(mode)
+ bufferSize(audioStreamOut->getInputTraits().blockSize / sizeof(BufferInternalType)), channelMode(mode)
{}
audio::DecoderWorker::~DecoderWorker()
M module-audio/Audio/decoder/DecoderWorker.hpp => module-audio/Audio/decoder/DecoderWorker.hpp +9 -5
@@ 1,9 1,10 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#pragma once
-#include "Audio/StreamQueuedEventsListener.hpp"
+#include <Audio/StreamQueuedEventsListener.hpp>
+#include <Audio/AbstractStream.hpp>
#include <Service/Worker.hpp>
#include <semaphore.hpp>
@@ 27,7 28,10 @@ namespace audio
ForceStereo
};
- DecoderWorker(Stream *audioStreamOut, Decoder *decoder, EndOfFileCallback endOfFileCallback, ChannelMode mode);
+ DecoderWorker(AbstractStream *audioStreamOut,
+ Decoder *decoder,
+ EndOfFileCallback endOfFileCallback,
+ ChannelMode mode);
~DecoderWorker() override;
virtual auto init(std::list<sys::WorkerQueueInfo> queues = std::list<sys::WorkerQueueInfo>()) -> bool override;
@@ 49,8 53,8 @@ namespace audio
static constexpr auto listenerQueueName = "DecoderWorkerQueue";
static constexpr auto listenerQueueCapacity = 1024;
- Stream *audioStreamOut = nullptr;
- Decoder *decoder = nullptr;
+ AbstractStream *audioStreamOut = nullptr;
+ Decoder *decoder = nullptr;
EndOfFileCallback endOfFileCallback;
std::unique_ptr<StreamQueuedEventsListener> queueListener;
bool playbackEnabled = false;
M module-audio/Audio/test/CMakeLists.txt => module-audio/Audio/test/CMakeLists.txt +2 -0
@@ 25,7 25,9 @@ add_gtest_executable(
SRCS
unittest.cpp
unittest_format.cpp
+ unittest_transcode.cpp
TestEndpoint.cpp
+ TestStream.cpp
LIBS
module-audio
)
A module-audio/Audio/test/MockStream.hpp => module-audio/Audio/test/MockStream.hpp +44 -0
@@ 0,0 1,44 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <Audio/AbstractStream.hpp>
+
+#include <gmock/gmock.h>
+
+namespace testing::audio
+{
+
+ class MockStream : public ::audio::AbstractStream
+ {
+ public:
+ MOCK_METHOD(bool, push, (void *data, std::size_t dataSize), (override));
+ MOCK_METHOD(bool, push, (const Span &span), (override));
+ MOCK_METHOD(bool, push, (), (override));
+ MOCK_METHOD(bool, pop, (Span & span), (override));
+ MOCK_METHOD(bool, reserve, (Span & span), (override));
+ MOCK_METHOD(void, commit, (), (override));
+ MOCK_METHOD(void, release, (), (override));
+ MOCK_METHOD(bool, peek, (Span & span), (override));
+ MOCK_METHOD(void, consume, (), (override));
+ MOCK_METHOD(void, unpeek, (), (override));
+ MOCK_METHOD(void, reset, (), (override));
+ MOCK_METHOD(void, registerListener, (EventListener * listener), (override));
+ MOCK_METHOD(void, unregisterListeners, (EventListener * listener), (override));
+ MOCK_METHOD(bool, isEmpty, (), (const, noexcept, override));
+ MOCK_METHOD(bool, isFull, (), (const, noexcept, override));
+ MOCK_METHOD(Traits, getInputTraits, (), (const, noexcept, override));
+ MOCK_METHOD(Traits, getOutputTraits, (), (const, noexcept, override));
+ };
+
+ class MockStreamEventListener : public ::audio::AbstractStream::EventListener
+ {
+ public:
+ MOCK_METHOD(void,
+ onEvent,
+ (::audio::AbstractStream * stream, ::audio::AbstractStream::Event event),
+ (override));
+ };
+
+} // namespace testing::audio
A module-audio/Audio/test/TestStream.cpp => module-audio/Audio/test/TestStream.cpp +124 -0
@@ 0,0 1,124 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "TestStream.hpp"
+
+#include <Audio/AbstractStream.hpp>
+
+#include <memory>
+#include <utility>
+
+using ::audio::AbstractStream;
+using testing::audio::TestStream;
+
+TestStream::TestStream(std::size_t blockSize) : bufSize(blockSize)
+{
+ data = std::make_unique<std::uint8_t[]>(bufSize);
+}
+
+const AbstractStream::Span TestStream::getDataSpan() const
+{
+ return AbstractStream::Span{data.get(), bufSize};
+}
+
+bool TestStream::push(void *data, std::size_t dataSize)
+{
+ return true;
+}
+
+bool TestStream::push(const AbstractStream::Span &span)
+{
+ if (span.dataSize != bufSize) {
+ return false;
+ }
+
+ for (std::size_t i = 0; i < span.dataSize; i++) {
+ data.get()[i] = span.data[i];
+ }
+
+ return true;
+}
+
+bool TestStream::push()
+{
+ return true;
+}
+
+bool TestStream::pop(AbstractStream::Span &span)
+{
+ return true;
+}
+
+bool TestStream::reserve(AbstractStream::Span &span)
+{
+ return true;
+}
+
+void TestStream::commit()
+{}
+
+void TestStream::release()
+{}
+
+bool TestStream::peek(AbstractStream::Span &span)
+{
+ span = Span{.data = data.get(), .dataSize = bufSize};
+ return true;
+}
+
+void TestStream::consume()
+{}
+
+void TestStream::unpeek()
+{}
+
+void TestStream::reset()
+{}
+
+void TestStream::setData(uint8_t value)
+{
+ for (std::size_t i = 0; i < bufSize; i++) {
+ data.get()[i] = value;
+ }
+}
+
+bool TestStream::checkData(const Span &span)
+{
+ if (span.dataSize != bufSize) {
+ return false;
+ }
+
+ for (std::size_t i = 0; i < bufSize; i++) {
+ if (data.get()[i] != span.data[i]) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void TestStream::registerListener(EventListener *listener)
+{}
+
+void TestStream::unregisterListeners(EventListener *listener)
+{}
+
+auto TestStream::getInputTraits() const noexcept -> Traits
+{
+ return Traits{.blockSize = bufSize, .format = ::audio::nullFormat};
+}
+
+auto TestStream::getOutputTraits() const noexcept -> Traits
+{
+ return Traits{.blockSize = bufSize, .format = ::audio::nullFormat};
+}
+
+bool TestStream::isEmpty() const noexcept
+{
+ return false;
+}
+
+bool TestStream::isFull() const noexcept
+{
+ return false;
+}
A module-audio/Audio/test/TestStream.hpp => module-audio/Audio/test/TestStream.hpp +57 -0
@@ 0,0 1,57 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <Audio/AbstractStream.hpp>
+
+#include <memory>
+
+namespace testing::audio
+{
+
+ class TestStream : public ::audio::AbstractStream
+ {
+ public:
+ explicit TestStream(std::size_t blockSize);
+
+ // listener registration
+ void registerListener(EventListener *listener) override;
+ void unregisterListeners(EventListener *listener) override;
+
+ /// push
+ bool push(void *data, std::size_t dataSize) override;
+ bool push(const Span &span) override;
+ bool push() override;
+
+ /// pop
+ bool pop(Span &span) override;
+
+ /// zero copy write
+ bool reserve(Span &span) override;
+ void commit() override;
+ void release() override;
+
+ /// zero copy read
+ bool peek(Span &span) override;
+ void consume() override;
+ void unpeek() override;
+
+ void reset() override;
+
+ [[nodiscard]] auto getInputTraits() const noexcept -> Traits override;
+ [[nodiscard]] auto getOutputTraits() const noexcept -> Traits override;
+ [[nodiscard]] bool isEmpty() const noexcept override;
+ [[nodiscard]] bool isFull() const noexcept override;
+
+ const Span getDataSpan() const;
+
+ void setData(uint8_t value);
+ bool checkData(const Span &span);
+
+ private:
+ std::unique_ptr<std::uint8_t[]> data;
+ std::size_t bufSize;
+ };
+
+}; // namespace testing::audio
M module-audio/Audio/test/unittest_stream.cpp => module-audio/Audio/test/unittest_stream.cpp +212 -12
@@ 1,23 1,32 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <gtest/gtest.h>
+#include <gmock/gmock.h>
#include <Audio/Stream.hpp>
+#include <Audio/AudioFormat.hpp>
+#include <Audio/StreamProxy.hpp>
+
+#include "MockStream.hpp"
#include <cstdint>
#include <cstring>
+using audio::NonCacheableStreamAllocator;
+using audio::StandardStreamAllocator;
+using audio::Stream;
+using testing::Return;
+using testing::audio::MockStream;
+using testing::audio::MockStreamEventListener;
+
constexpr std::size_t defaultBlockSize = 64U;
constexpr std::size_t defaultBuffering = 4U;
-
-using namespace audio;
+constexpr audio::AudioFormat format = audio::AudioFormat(44100, 16, 2);
static std::uint8_t testData[defaultBuffering][defaultBlockSize];
static std::uint8_t emptyBlock[defaultBlockSize];
-#include <iostream>
-
static void initTestData()
{
auto fillbuf = [](std::uint8_t *b, std::size_t s, unsigned step) {
@@ 52,17 61,68 @@ TEST(Stream, Init)
{
StandardStreamAllocator a;
constexpr auto bufferingSize = 2U;
- Stream s(a, defaultBlockSize, bufferingSize);
+ Stream s(format, a, defaultBlockSize, bufferingSize);
EXPECT_EQ(s.getBlockCount(), bufferingSize);
- EXPECT_EQ(s.getBlockSize(), defaultBlockSize);
+ EXPECT_EQ(s.getInputTraits().blockSize, defaultBlockSize);
+ EXPECT_EQ(s.getOutputTraits().blockSize, defaultBlockSize);
EXPECT_EQ(s.getUsedBlockCount(), 0);
}
+TEST(Stream, EmptyFull)
+{
+ StandardStreamAllocator a;
+ constexpr auto bufferingSize = 2U;
+ Stream s(format, a, defaultBlockSize, bufferingSize);
+
+ EXPECT_TRUE(s.isEmpty());
+ EXPECT_TRUE(s.blocksAvailable());
+ EXPECT_FALSE(s.isFull());
+
+ s.push();
+
+ EXPECT_FALSE(s.isEmpty());
+ EXPECT_TRUE(s.blocksAvailable());
+ EXPECT_FALSE(s.isFull());
+
+ s.push();
+
+ EXPECT_FALSE(s.isEmpty());
+ EXPECT_FALSE(s.blocksAvailable());
+ EXPECT_TRUE(s.isFull());
+}
+
+TEST(Stream, Allocator)
+{
+ NonCacheableStreamAllocator a;
+ EXPECT_NO_THROW(Stream(format, a, defaultBlockSize));
+}
+
+TEST(Stream, Listeners)
+{
+ testing::audio::MockStreamEventListener listener;
+ StandardStreamAllocator a;
+ Stream s(format, a, defaultBlockSize);
+ Stream::Span span;
+
+ s.registerListener(&listener);
+
+ // does not take effect
+ s.unregisterListeners(nullptr);
+
+ EXPECT_CALL(listener, onEvent(&s, ::audio::AbstractStream::Event::StreamUnderFlow)).Times(1);
+ s.peek(span);
+
+ s.unregisterListeners(&listener);
+
+ // no second onEvent call - listener is unregistered
+ s.peek(span);
+}
+
TEST(Stream, Push)
{
StandardStreamAllocator a;
- Stream s(a, defaultBlockSize);
+ Stream s(format, a, defaultBlockSize);
auto block = testData[0];
EXPECT_TRUE(s.push(block, defaultBlockSize));
@@ 73,10 133,36 @@ TEST(Stream, Push)
EXPECT_FALSE(s.push(block, defaultBlockSize));
}
+TEST(Stream, InvalidPushPop)
+{
+ StandardStreamAllocator a;
+ std::uint8_t buf[defaultBlockSize];
+ Stream s(format, a, defaultBlockSize);
+ Stream::Span span{.data = buf, .dataSize = 63};
+
+ EXPECT_FALSE(s.push(span));
+ span.dataSize = defaultBlockSize;
+
+ // push and peek null data
+ EXPECT_TRUE(s.push());
+ EXPECT_TRUE(s.peek(span));
+ memset(buf, 0, sizeof(buf));
+
+ ASSERT_EQ(span.dataSize, defaultBlockSize);
+ EXPECT_EQ(memcmp(span.data, buf, span.dataSize), 0);
+
+ // peek in progress - push is not allowed
+ EXPECT_FALSE(s.pop(span));
+
+ // push is not allowed when using zero copy write
+ EXPECT_TRUE(s.reserve(span));
+ EXPECT_FALSE(s.push(span));
+}
+
TEST(Stream, PushPop)
{
StandardStreamAllocator a;
- Stream s(a, defaultBlockSize);
+ Stream s(format, a, defaultBlockSize);
initTestData();
@@ 122,7 208,7 @@ TEST(Stream, PushPop)
TEST(Stream, Peek)
{
StandardStreamAllocator a;
- Stream s(a, defaultBlockSize);
+ Stream s(format, a, defaultBlockSize);
initTestData();
@@ 157,7 243,7 @@ TEST(Stream, Peek)
TEST(Stream, GreedyPeek)
{
StandardStreamAllocator a;
- Stream s(a, defaultBlockSize);
+ Stream s(format, a, defaultBlockSize);
Stream::Span span;
initTestData();
@@ 182,7 268,7 @@ TEST(Stream, GreedyPeek)
TEST(Stream, Reserve)
{
StandardStreamAllocator a;
- Stream s(a, defaultBlockSize);
+ Stream s(format, a, defaultBlockSize);
Stream::Span span;
EXPECT_EQ(s.getReservedCount(), 0);
@@ 206,6 292,120 @@ TEST(Stream, Reserve)
EXPECT_EQ(s.getUsedBlockCount(), 1);
}
+TEST(Stream, Iterator)
+{
+ std::uint8_t buf[defaultBlockSize * 2];
+
+ Stream::RawBlockIterator it(buf, sizeof(buf), buf, 64);
+
+ it++;
+ auto data = *it;
+ EXPECT_EQ(data.data, &buf[defaultBlockSize]);
+ EXPECT_EQ(data.dataSize, defaultBlockSize);
+
+ it++;
+ data = *it;
+ EXPECT_EQ(data.data, buf);
+ EXPECT_EQ(data.dataSize, defaultBlockSize);
+
+ it--;
+ data = *it;
+ EXPECT_EQ(data.data, &buf[defaultBlockSize]);
+ EXPECT_EQ(data.dataSize, defaultBlockSize);
+
+ it--;
+ data = *it;
+ EXPECT_EQ(data.data, buf);
+ EXPECT_EQ(data.dataSize, defaultBlockSize);
+}
+
+TEST(Stream, Reset)
+{
+ StandardStreamAllocator a;
+ Stream s(format, a, defaultBlockSize);
+
+ s.push();
+ ASSERT_FALSE(s.isEmpty());
+
+ s.reset();
+ EXPECT_TRUE(s.isEmpty());
+}
+
+TEST(Stream, spanEquality)
+{
+ std::uint8_t buf[defaultBlockSize];
+ auto span = Stream::Span{.data = buf, .dataSize = defaultBlockSize};
+ auto span2 = span;
+ EXPECT_TRUE(span == span2);
+ EXPECT_FALSE(span != span2);
+
+ span2.data = &buf[1];
+ EXPECT_FALSE(span == span2);
+ EXPECT_TRUE(span != span2);
+
+ span2 = span;
+ span2.dataSize = defaultBlockSize - 1;
+ EXPECT_FALSE(span == span2);
+ EXPECT_TRUE(span != span2);
+}
+
+TEST(Proxy, Write)
+{
+ MockStream mock;
+ auto proxy = ::audio::StreamProxy(mock);
+ ::audio::AbstractStream::Span span{.data = reinterpret_cast<uint8_t *>(0xdeadbeef), .dataSize = 0xc00f33b4d};
+
+ EXPECT_CALL(mock, push(span)).Times(1).WillOnce(Return(true));
+ EXPECT_CALL(mock, push(nullptr, 128)).WillOnce(Return(false));
+ EXPECT_CALL(mock, push()).WillOnce(Return(true));
+
+ EXPECT_TRUE(proxy.push(span));
+ EXPECT_FALSE(proxy.push(nullptr, 128));
+ EXPECT_TRUE(proxy.push());
+
+ EXPECT_CALL(mock, reserve).Times(1);
+ EXPECT_CALL(mock, commit).Times(1);
+ EXPECT_CALL(mock, release).Times(1);
+
+ proxy.reserve(span);
+ proxy.commit();
+ proxy.release();
+}
+
+TEST(Proxy, Read)
+{
+ MockStream mock;
+ auto proxy = ::audio::StreamProxy(mock);
+ ::audio::AbstractStream::Span span{.data = reinterpret_cast<uint8_t *>(0xdeadbeef), .dataSize = 0xc00f33b4d};
+
+ EXPECT_CALL(mock, pop(span)).Times(1).WillOnce(Return(true));
+ EXPECT_TRUE(proxy.pop(span));
+
+ EXPECT_CALL(mock, peek(span)).Times(2).WillOnce(Return(true)).WillOnce(Return(false));
+ EXPECT_CALL(mock, consume());
+ EXPECT_CALL(mock, unpeek());
+
+ EXPECT_TRUE(proxy.peek(span));
+ EXPECT_FALSE(proxy.peek(span));
+
+ proxy.consume();
+ proxy.unpeek();
+}
+
+TEST(Proxy, Misc)
+{
+ MockStream mock;
+ auto proxy = ::audio::StreamProxy(mock);
+
+ EXPECT_CALL(mock, reset).Times(1);
+ EXPECT_CALL(mock, isEmpty).Times(1).WillOnce(Return(true));
+ EXPECT_CALL(mock, isFull).Times(1).WillOnce(Return(false));
+
+ proxy.reset();
+ EXPECT_TRUE(proxy.isEmpty());
+ EXPECT_FALSE(proxy.isFull());
+}
+
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
A module-audio/Audio/test/unittest_transcode.cpp => module-audio/Audio/test/unittest_transcode.cpp +266 -0
@@ 0,0 1,266 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "TestStream.hpp"
+#include "MockStream.hpp"
+
+#include <Audio/AbstractStream.hpp>
+#include <Audio/AudioFormat.hpp>
+#include <Audio/transcode/InputTranscodeProxy.hpp>
+#include <Audio/transcode/Transform.hpp>
+#include <Audio/transcode/MonoToStereo.hpp>
+#include <Audio/transcode/TransformComposite.hpp>
+
+#include <cstdlib>
+
+using ::testing::Return;
+using ::testing::audio::MockStream;
+using ::testing::audio::MockStreamEventListener;
+
+constexpr std::size_t testStreamSize = 8;
+
+class InverseTransform : public audio::transcode::Transform
+{
+ public:
+ auto transform(const Span &span, const Span &transformSpace) const -> Span override
+ {
+ for (std::size_t i = 0; i < span.dataSize; i++) {
+ span.data[i] = ~span.data[i];
+ }
+ return span;
+ }
+
+ auto transformBlockSize(std::size_t inputBufferSize) const noexcept -> std::size_t override
+ {
+ return inputBufferSize;
+ }
+
+ auto validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool override
+ {
+ return true;
+ }
+
+ auto transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat override
+ {
+ return inputFormat;
+ }
+};
+
+class NullTransform : public audio::transcode::Transform
+{
+ public:
+ auto transform(const Span &span, const Span &transformSpace) const -> Span override
+ {
+ return span;
+ }
+
+ auto validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool override
+ {
+ return true;
+ }
+
+ auto transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat override
+ {
+ return inputFormat;
+ }
+
+ auto transformBlockSize(std::size_t inputBufferSize) const noexcept -> std::size_t override
+ {
+ return inputBufferSize;
+ }
+};
+
+TEST(Transcode, Reset)
+{
+ MockStream mock;
+ NullTransform nullTransform;
+
+ EXPECT_CALL(mock, getInputTraits);
+ audio::transcode::InputTranscodeProxy proxy(mock, nullTransform);
+
+ EXPECT_CALL(mock, reset()).Times(1);
+
+ proxy.reset();
+}
+
+TEST(Transcode, Push)
+{
+ static std::uint8_t testData[testStreamSize] = {0, 1, 2, 3, 4, 5, 6, 7};
+ static std::uint8_t invertedData[testStreamSize] = {255, 254, 253, 252, 251, 250, 249, 248};
+ InverseTransform inv;
+ ::testing::audio::TestStream stream(testStreamSize);
+ ::audio::transcode::InputTranscodeProxy transform(stream, inv);
+
+ transform.push(::audio::AbstractStream::Span{testData, testStreamSize});
+ EXPECT_TRUE(stream.checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+}
+
+TEST(Transcode, FailedPeek)
+{
+ NullTransform nullTransform;
+ MockStream mockStream;
+
+ EXPECT_CALL(mockStream, getInputTraits);
+ ::audio::transcode::InputTranscodeProxy transform(mockStream, nullTransform);
+ ::audio::AbstractStream::Span dataSpan;
+
+ EXPECT_CALL(mockStream, peek).Times(1).WillOnce(Return(false));
+ EXPECT_EQ(transform.peek(dataSpan), false);
+}
+
+TEST(Transcode, Commit)
+{
+ static std::uint8_t invertedData[testStreamSize] = {255, 254, 253, 252, 251, 250, 249, 248};
+ InverseTransform inv;
+ ::testing::audio::TestStream stream(testStreamSize);
+ ::audio::transcode::InputTranscodeProxy transform(stream, inv);
+ ::audio::AbstractStream::Span span;
+
+ stream.setData(0);
+
+ ASSERT_TRUE(transform.peek(span));
+ ASSERT_TRUE(span.data != nullptr);
+ ASSERT_TRUE(span.dataSize == testStreamSize);
+
+ for (std::size_t i = 0; i < span.dataSize; ++i) {
+ span.data[i] = i;
+ }
+
+ transform.commit();
+ EXPECT_TRUE(stream.checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+
+ // no conversion on commit with no peek
+ stream.commit();
+ EXPECT_TRUE(stream.checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+}
+
+TEST(Transcode, Traits)
+{
+ auto testFormat = ::audio::AudioFormat(44100, 16, 1);
+ ::audio::transcode::MonoToStereo m2s;
+ MockStream mockStream;
+
+ EXPECT_CALL(mockStream, getInputTraits)
+ .Times(1)
+ .WillOnce(Return(::audio::AbstractStream::Traits{.blockSize = 128, .format = testFormat}));
+
+ ::audio::transcode::InputTranscodeProxy proxy(mockStream, m2s);
+
+ EXPECT_CALL(mockStream, getInputTraits)
+ .Times(1)
+ .WillOnce(Return(::audio::AbstractStream::Traits{.blockSize = 128, .format = testFormat}));
+
+ EXPECT_CALL(mockStream, getOutputTraits)
+ .Times(1)
+ .WillOnce(Return(::audio::AbstractStream::Traits{.blockSize = 128, .format = testFormat}));
+
+ auto proxyInputTraits = proxy.getInputTraits();
+
+ EXPECT_EQ(proxyInputTraits.blockSize, 256);
+ EXPECT_EQ(proxyInputTraits.format.getChannels(), 2);
+ EXPECT_EQ(proxyInputTraits.format.getSampleRate(), 44100);
+ EXPECT_EQ(proxyInputTraits.format.getBitWidth(), 16);
+
+ auto proxyOutputTraits = proxy.getOutputTraits();
+
+ EXPECT_EQ(proxyOutputTraits.blockSize, 128);
+ EXPECT_EQ(proxyOutputTraits.format.getChannels(), 1);
+ EXPECT_EQ(proxyOutputTraits.format.getSampleRate(), 44100);
+ EXPECT_EQ(proxyOutputTraits.format.getBitWidth(), 16);
+}
+
+TEST(Transcode, ListenersWrap)
+{
+ MockStream mock;
+ MockStreamEventListener listener;
+ NullTransform nullTransform;
+
+ EXPECT_CALL(mock, getInputTraits);
+ audio::transcode::InputTranscodeProxy proxy(mock, nullTransform);
+
+ EXPECT_CALL(mock, registerListener(&listener)).Times(1);
+
+ proxy.registerListener(&listener);
+
+ EXPECT_CALL(mock, unregisterListeners(&listener)).Times(1);
+
+ proxy.unregisterListeners(&listener);
+}
+
+TEST(Transform, MonoToStereo)
+{
+ audio::transcode::MonoToStereo m2s;
+ static std::uint16_t inputBuffer[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+ static std::uint16_t outputBuffer[16];
+ auto input = ::audio::AbstractStream::Span{.data = reinterpret_cast<std::uint8_t *>(&inputBuffer[0]),
+ .dataSize = sizeof(inputBuffer)};
+ auto output = ::audio::AbstractStream::Span{.data = reinterpret_cast<std::uint8_t *>(&outputBuffer[0]),
+ .dataSize = sizeof(outputBuffer)};
+
+ ASSERT_EQ(input.dataSize, 16);
+ ASSERT_EQ(output.dataSize, 32);
+
+ EXPECT_TRUE(m2s.validateInputFormat(audio::AudioFormat(44100, 16, 1)));
+ EXPECT_FALSE(m2s.validateInputFormat(audio::AudioFormat(44100, 16, 0)));
+ EXPECT_FALSE(m2s.validateInputFormat(audio::AudioFormat(44100, 16, 2)));
+
+ auto outputFormat = m2s.transformFormat(audio::AudioFormat(44100, 16, 1));
+ EXPECT_EQ(outputFormat, audio::AudioFormat(44100, 16, 2));
+
+ EXPECT_EQ(m2s.getTransformSize(4), 8);
+ EXPECT_EQ(m2s.getTransformSize(128), 256);
+ EXPECT_EQ(m2s.getTransformSize(0), 0);
+
+ static std::uint16_t expectedResult[16] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7};
+ ASSERT_EQ(m2s.transform(input, output), output);
+ ASSERT_EQ(memcmp(output.data, expectedResult, 32), 0);
+}
+
+TEST(Transform, Composite)
+{
+ audio::transcode::MonoToStereo m2s;
+ InverseTransform inv;
+ audio::transcode::TransformComposite composite{&inv, &m2s};
+ static std::uint16_t inputBuffer[8] = {0, 1, 2, 3, 4, 5, 6, 7};
+ static std::uint16_t outputBuffer[16];
+ static std::uint16_t expectedResult[] = {
+ 0xFFFF,
+ 0xFFFF,
+ 0xFFFE,
+ 0xFFFE,
+ 0xFFFD,
+ 0xFFFD,
+ 0xFFFC,
+ 0xFFFC,
+ 0xFFFB,
+ 0xFFFB,
+ 0xFFFA,
+ 0xFFFA,
+ 0xFFF9,
+ 0xFFF9,
+ 0xFFF8,
+ 0xFFF8,
+ };
+
+ auto input = ::audio::AbstractStream::Span{.data = reinterpret_cast<std::uint8_t *>(&inputBuffer[0]),
+ .dataSize = sizeof(inputBuffer)};
+ auto output = ::audio::AbstractStream::Span{.data = reinterpret_cast<std::uint8_t *>(&outputBuffer[0]),
+ .dataSize = sizeof(outputBuffer)};
+
+ ASSERT_EQ(output.dataSize, composite.getTransformSize(sizeof(inputBuffer)));
+ auto result = composite.transform(input, output);
+ ASSERT_EQ(result.dataSize, sizeof(expectedResult));
+ ASSERT_EQ(memcmp(result.data, expectedResult, sizeof(expectedResult)), 0);
+
+ EXPECT_TRUE(composite.validateInputFormat(audio::AudioFormat(44100, 16, 1)));
+ EXPECT_FALSE(composite.validateInputFormat(audio::AudioFormat(44100, 16, 0)));
+ EXPECT_FALSE(composite.validateInputFormat(audio::AudioFormat(44100, 16, 2)));
+
+ auto outputFormat = composite.transformFormat(audio::AudioFormat(44100, 16, 1));
+ EXPECT_EQ(outputFormat, audio::AudioFormat(44100, 16, 2));
+ auto outputBlockSize = composite.transformBlockSize(sizeof(inputBuffer));
+ EXPECT_EQ(outputBlockSize, 2 * sizeof(inputBuffer));
+}
A module-audio/Audio/transcode/InputTranscodeProxy.cpp => module-audio/Audio/transcode/InputTranscodeProxy.cpp +49 -0
@@ 0,0 1,49 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "InputTranscodeProxy.hpp"
+#include "Transform.hpp"
+
+#include <utility>
+
+using audio::transcode::InputTranscodeProxy;
+
+InputTranscodeProxy::InputTranscodeProxy(AbstractStream &wrappedStream, Transform &transform) noexcept
+ : StreamProxy(wrappedStream), transform(transform),
+ transcodingSpaceSize(transform.getTransformSize(wrappedStream.getInputTraits().blockSize)),
+ transcodingSpace(std::make_unique<std::uint8_t[]>(transcodingSpaceSize)), transcodingSpaceSpan{
+ .data = transcodingSpace.get(),
+ .dataSize = transcodingSpaceSize}
+{}
+
+bool InputTranscodeProxy::push(const Span &span)
+{
+ return getWrappedStream().push(transform.transform(span, transcodingSpaceSpan));
+}
+
+void InputTranscodeProxy::commit()
+{
+ transform.transform(peekedSpan, transcodingSpaceSpan);
+ getWrappedStream().commit();
+ peekedSpan.reset();
+}
+
+bool InputTranscodeProxy::peek(Span &span)
+{
+ auto result = getWrappedStream().peek(span);
+
+ if (result) {
+ peekedSpan = span;
+ }
+
+ return result;
+}
+
+auto InputTranscodeProxy::getInputTraits() const noexcept -> Traits
+{
+ auto originalTraits = StreamProxy::getInputTraits();
+ auto transcodedFormat = transform.transformFormat(originalTraits.format);
+ auto transcodedBlockSize = transform.transformBlockSize(originalTraits.blockSize);
+
+ return Traits{.blockSize = transcodedBlockSize, .format = transcodedFormat};
+}
A module-audio/Audio/transcode/InputTranscodeProxy.hpp => module-audio/Audio/transcode/InputTranscodeProxy.hpp +41 -0
@@ 0,0 1,41 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <Audio/StreamProxy.hpp>
+
+#include "Transform.hpp"
+
+#include <memory>
+
+namespace audio::transcode
+{
+ /**
+ * @brief An input transcoding proxy for an AbstractStream.
+ */
+ class InputTranscodeProxy : public audio::StreamProxy
+ {
+ public:
+ /**
+ * @brief Construct a new Input Transcode Proxy object
+ *
+ * @param wrappedStream
+ * @param transform to apply on the input
+ */
+ explicit InputTranscodeProxy(AbstractStream &wrappedStream, Transform &transform) noexcept;
+
+ bool push(const Span &span) override;
+ void commit() override;
+ bool peek(Span &span) override;
+ [[nodiscard]] auto getInputTraits() const noexcept -> Traits override;
+
+ private:
+ Transform &transform;
+ Span peekedSpan;
+ std::size_t transcodingSpaceSize;
+ std::unique_ptr<std::uint8_t[]> transcodingSpace;
+ Span transcodingSpaceSpan;
+ };
+
+}; // namespace audio::transcode
A module-audio/Audio/transcode/MonoToStereo.cpp => module-audio/Audio/transcode/MonoToStereo.cpp +36 -0
@@ 0,0 1,36 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "MonoToStereo.hpp"
+
+#include <Audio/AudioFormat.hpp>
+
+using audio::transcode::MonoToStereo;
+
+auto MonoToStereo::transform(const Span &span, const Span &transformSpace) const -> Span
+{
+ auto outputSpan = Span{.data = transformSpace.data, .dataSize = getTransformSize(span.dataSize)};
+ auto outputBuffer = reinterpret_cast<std::uint16_t *>(transformSpace.data);
+ auto inputBuffer = reinterpret_cast<std::uint16_t *>(span.data);
+
+ for (std::size_t i = span.dataSize / sizeof(std::uint16_t); i > 0; i--) {
+ outputBuffer[i * 2 - 1] = outputBuffer[i * 2 - 2] = inputBuffer[i - 1];
+ }
+
+ return outputSpan;
+}
+
+auto MonoToStereo::transformBlockSize(std::size_t inputBufferSize) const noexcept -> std::size_t
+{
+ return 2 * inputBufferSize;
+}
+
+auto MonoToStereo::validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool
+{
+ return inputFormat.getChannels() == 1;
+}
+
+auto MonoToStereo::transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat
+{
+ return audio::AudioFormat(inputFormat.getSampleRate(), inputFormat.getBitWidth(), 2);
+}
A module-audio/Audio/transcode/MonoToStereo.hpp => module-audio/Audio/transcode/MonoToStereo.hpp +22 -0
@@ 0,0 1,22 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "Transform.hpp"
+
+namespace audio::transcode
+{
+ /**
+ * @brief Mono to stereo PCM16 data transform
+ */
+ class MonoToStereo : public Transform
+ {
+ public:
+ auto transform(const Span &span, const Span &transformSpace) const -> Span override;
+ auto validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool override;
+ auto transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat override;
+ auto transformBlockSize(std::size_t blockSize) const noexcept -> std::size_t override;
+ };
+
+} // namespace audio::transcode
A module-audio/Audio/transcode/Transform.hpp => module-audio/Audio/transcode/Transform.hpp +90 -0
@@ 0,0 1,90 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <Audio/AudioFormat.hpp>
+#include <Audio/AbstractStream.hpp>
+
+namespace audio::transcode
+{
+
+ /**
+ * @brief Data transform which can be applied on a data range.
+ */
+ class Transform
+ {
+ public:
+ using Span = audio::AbstractStream::Span;
+
+ virtual ~Transform() = default;
+
+ /**
+ * @brief Transforms data within range
+ *
+ * @param inputSpan - input data to transform
+ * @param transformSpace - overall space available to the transform
+ * @return Output data (contained within )
+ */
+ virtual auto transform(const Span &inputSpan, const Span &transformSpace) const -> Span = 0;
+
+ /**
+ * @brief Checks if audio format is suitable for transformation.
+ *
+ * @param inputFormat - format to be checked
+ * @return true if inputFormat is suitable for transformation, false otherwise
+ */
+ virtual auto validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool = 0;
+
+ /**
+ * @brief Calculates the audio format on transform output based on the
+ * input format.
+ *
+ * @param inputFormat - format of the data on input
+ * @return format of the data on output.
+ */
+ virtual auto transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat = 0;
+
+ /**
+ * @brief Calculates the size of an output data after the transform.
+ *
+ * @param blockSize - size of an input block
+ * @return size of an output block
+ */
+ virtual auto transformBlockSize(std::size_t blockSize) const noexcept -> std::size_t = 0;
+
+ /**
+ * @brief Get the overall space size required for transformation. Some transforms
+ * may need to use more space than the size of an output data, e.g. resampling
+ * where there is a signal interpolation before the final decimation.
+ *
+ * @param inputBufferSize
+ * @return std::size_t
+ */
+ virtual auto getTransformSize(std::size_t inputBufferSize) const noexcept -> std::size_t
+ {
+ return transformBlockSize(inputBufferSize);
+ }
+
+ /**
+ * @brief A convenience transform operator.
+ */
+ inline auto operator()(const Span &span, const Span &transformSpace)
+ {
+ return transform(span, transformSpace);
+ }
+
+ /**
+ * @brief A convenience wrapper for transforms which has transcoding size
+ * same as output size and a transcoding operation is performed in-place.
+ *
+ * @param span to transform
+ * @return transformed span
+ */
+ inline auto transform(const Span &span)
+ {
+ return transform(span, span);
+ }
+ };
+
+}; // namespace audio::transcode
A module-audio/Audio/transcode/TransformComposite.cpp => module-audio/Audio/transcode/TransformComposite.cpp +75 -0
@@ 0,0 1,75 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "TransformComposite.hpp"
+#include "Transform.hpp"
+
+#include <Audio/AudioFormat.hpp>
+
+#include <algorithm>
+#include <initializer_list>
+
+using audio::transcode::Transform;
+using audio::transcode::TransformComposite;
+
+TransformComposite::TransformComposite(std::initializer_list<Transform *> transforms) : children(transforms)
+{}
+
+auto TransformComposite::transform(const Span &input, const Span &conversionSpace) const -> Span
+{
+ auto output = input;
+
+ for (auto t : children) {
+ output = t->transform(output, conversionSpace);
+ }
+ return output;
+}
+
+auto TransformComposite::getTransformSize(std::size_t inputBufferSize) const noexcept -> std::size_t
+{
+ auto nextBlockSize = inputBufferSize;
+ auto inputBlockSize = inputBufferSize;
+
+ for (auto t : children) {
+ nextBlockSize = t->getTransformSize(nextBlockSize);
+ inputBlockSize = std::max(nextBlockSize, inputBlockSize);
+ }
+
+ return inputBlockSize;
+}
+
+auto TransformComposite::validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool
+{
+ auto checkFormat = inputFormat;
+
+ for (auto t : children) {
+ if (!t->validateInputFormat(checkFormat)) {
+ return false;
+ }
+ checkFormat = t->transformFormat(checkFormat);
+ }
+
+ return true;
+}
+
+auto TransformComposite::transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat
+{
+ auto outputFormat = inputFormat;
+
+ for (auto t : children) {
+ outputFormat = t->transformFormat(outputFormat);
+ }
+
+ return outputFormat;
+}
+
+auto TransformComposite::transformBlockSize(std::size_t blockSize) const noexcept -> std::size_t
+{
+ std::size_t transformedBlockSize = blockSize;
+
+ for (auto t : children) {
+ transformedBlockSize = t->transformBlockSize(transformedBlockSize);
+ }
+
+ return transformedBlockSize;
+}
A module-audio/Audio/transcode/TransformComposite.hpp => module-audio/Audio/transcode/TransformComposite.hpp +39 -0
@@ 0,0 1,39 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "Transform.hpp"
+
+#include <Audio/AudioFormat.hpp>
+
+#include <initializer_list>
+#include <vector>
+
+namespace audio::transcode
+{
+
+ /**
+ * @brief Allows to execute a set of transforms to be performed in a chain.
+ */
+ class TransformComposite : public Transform
+ {
+ public:
+ /**
+ * @brief Construct a new Transform Composite object
+ *
+ * @param children - transforms to be executed on each transform call.
+ */
+ TransformComposite(std::initializer_list<Transform *> children);
+
+ auto transform(const Span &span, const Span &transformSpace) const -> Span override;
+ auto getTransformSize(std::size_t inputBufferSize) const noexcept -> std::size_t override;
+ auto validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool override;
+ auto transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat override;
+ auto transformBlockSize(std::size_t blockSize) const noexcept -> std::size_t override;
+
+ private:
+ std::vector<Transform *> children;
+ };
+
+} // namespace audio::transcode
M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +37 -30
@@ 8,40 8,45 @@ project(module-audio VERSION 1.0
module_is_test_entity()
-set(SOURCES
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Audio.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioCommon.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioDeviceFactory.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioFormat.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioMux.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/VolumeScaler.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/Decoder.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderMP3.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderWAV.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderWorker.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/xing_header.c"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/Encoder.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/EncoderWAV.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Endpoint.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/IdleOperation.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/Operation.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/PlaybackOperation.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RecorderOperation.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RouterOperation.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Profiles/Profile.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/ServiceObserver.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/Stream.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamFactory.cpp"
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamQueuedEventsListener.cpp"
-)
-
# include target specific rules
set(AUDIO_BOARD_LIBRARY audio-${PROJECT_TARGET_NAME})
add_subdirectory(board/${PROJECT_TARGET_NAME})
target_include_directories(${AUDIO_BOARD_LIBRARY} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-add_library(${PROJECT_NAME} STATIC ${SOURCES})
+add_library(${PROJECT_NAME} STATIC)
+target_sources(
+ ${PROJECT_NAME}
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Audio.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioCommon.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioDeviceFactory.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioFormat.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/AudioMux.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/Decoder.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderMP3.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderWAV.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/DecoderWorker.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/xing_header.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/Encoder.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/encoder/EncoderWAV.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Endpoint.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/IdleOperation.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/Operation.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/PlaybackOperation.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RecorderOperation.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Operation/RouterOperation.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Profiles/Profile.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/ServiceObserver.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/Stream.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamFactory.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamProxy.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/StreamQueuedEventsListener.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/transcode/InputTranscodeProxy.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/transcode/MonoToStereo.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/transcode/TransformComposite.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/VolumeScaler.cpp
+)
target_include_directories(${PROJECT_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_CONFIG_DEFINITIONS})
target_compile_definitions(${PROJECT_NAME} PUBLIC ${PROJECT_TARGET})
@@ 51,9 56,11 @@ target_compile_features(${PROJECT_NAME} PUBLIC ${TARGET_COMPILE_FEATURES})
target_link_options(${PROJECT_NAME} PUBLIC ${TARGET_LINK_OPTIONS})
target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+target_compile_options(${PROJECT_NAME} PRIVATE -O0 -g)
+
# supress warning for flac decoder
set_source_files_properties(
- "${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp"
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/decoder/decoderFLAC.cpp
PROPERTIES COMPILE_FLAGS
"-Wno-implicit-fallthrough -Wno-error=maybe-uninitialized"
)
M module-audio/board/rt1051/SAIAudioDevice.cpp => module-audio/board/rt1051/SAIAudioDevice.cpp +6 -2
@@ 3,6 3,8 @@
#include "SAIAudioDevice.hpp"
+#include <Audio/Stream.hpp>
+
using namespace audio;
SAIAudioDevice::SAIAudioDevice(I2S_Type *base, sai_edma_handle_t *rxHandle, sai_edma_handle_t *txHandle)
@@ 20,8 22,10 @@ void SAIAudioDevice::initiateRxTransfer()
void SAIAudioDevice::initiateTxTransfer()
{
- auto nullSpan = Sink::_stream->getNullSpan();
- auto xfer = sai_transfer_t{.data = nullSpan.data, .dataSize = nullSpan.dataSize};
+ audio::Stream::Span dataToSend;
+ Sink::_stream->peek(dataToSend);
+
+ auto xfer = sai_transfer_t{.data = dataToSend.data, .dataSize = dataToSend.dataSize};
SAI_TransferSendEDMA(_base, tx, &xfer);
}