M module-audio/Audio/Operation/RouterOperation.cpp => module-audio/Audio/Operation/RouterOperation.cpp +18 -29
@@ 7,6 7,7 @@
#include <Audio/AudioCommon.hpp>
#include <Audio/Profiles/Profile.hpp>
#include <Audio/StreamFactory.hpp>
+#include <Audio/transcode/TransformFactory.hpp>
#include <log/log.hpp>
#include <mutex.hpp>
@@ 50,16 51,6 @@ namespace audio
operationToken = token;
state = State::Active;
- // check if audio devices support desired audio format
- if (!audioDevice->isFormatSupportedBySource(currentProfile->getAudioFormat())) {
- return RetCode::InvalidFormat;
- }
-
- if (!audioDeviceCellular->isFormatSupportedBySource(currentProfile->getAudioFormat())) {
- return RetCode::InvalidFormat;
- }
-
- // try to run devices with the format
if (auto ret = audioDevice->Start(); ret != AudioDevice::RetCode::Success) {
return GetDeviceError(ret);
}
@@ 71,26 62,24 @@ namespace audio
// create streams
StreamFactory streamFactory(callTimeConstraint);
try {
- dataStreamIn =
- streamFactory.makeStream(*audioDevice, *audioDeviceCellular, currentProfile->getAudioFormat());
- dataStreamOut =
- streamFactory.makeStream(*audioDevice, *audioDeviceCellular, currentProfile->getAudioFormat());
+ dataStreamIn = streamFactory.makeStream(*audioDevice, *audioDeviceCellular);
+ dataStreamOut = streamFactory.makeStream(*audioDeviceCellular, *audioDevice);
}
- catch (std::invalid_argument &e) {
+ catch (const std::exception &e) {
LOG_FATAL("Cannot create audio stream: %s", e.what());
return audio::RetCode::Failed;
}
// create audio connections
- inputConnection =
- std::make_unique<audio::StreamConnection>(audioDeviceCellular.get(), audioDevice.get(), dataStreamIn.get());
- outputConnection = std::make_unique<audio::StreamConnection>(
- audioDevice.get(), audioDeviceCellular.get(), dataStreamOut.get());
+ voiceInputConnection =
+ std::make_unique<audio::StreamConnection>(audioDevice.get(), audioDeviceCellular.get(), dataStreamIn.get());
+ voiceOutputConnection = std::make_unique<audio::StreamConnection>(
+ audioDeviceCellular.get(), audioDevice.get(), dataStreamOut.get());
// enable audio connections
- inputConnection->enable();
+ voiceOutputConnection->enable();
if (!IsMuted()) {
- outputConnection->enable();
+ voiceInputConnection->enable();
}
return audio::RetCode::Success;
@@ 103,8 92,8 @@ namespace audio
}
state = State::Idle;
- outputConnection.reset();
- inputConnection.reset();
+ voiceOutputConnection.reset();
+ voiceInputConnection.reset();
audioDevice->Stop();
audioDeviceCellular->Stop();
@@ 122,8 111,8 @@ namespace audio
}
state = State::Paused;
- outputConnection->disable();
- inputConnection->disable();
+ voiceOutputConnection->disable();
+ voiceInputConnection->disable();
return RetCode::Success;
}
@@ 134,8 123,8 @@ namespace audio
}
state = State::Active;
- inputConnection->enable();
- outputConnection->enable();
+ voiceInputConnection->enable();
+ voiceOutputConnection->enable();
return RetCode::Success;
}
@@ 219,13 208,13 @@ namespace audio
void RouterOperation::Mute()
{
- outputConnection->disable();
+ voiceInputConnection->disable();
mute = Mute::Enabled;
}
void RouterOperation::Unmute()
{
- outputConnection->enable();
+ voiceInputConnection->enable();
mute = Mute::Disabled;
}
M module-audio/Audio/Operation/RouterOperation.hpp => module-audio/Audio/Operation/RouterOperation.hpp +4 -6
@@ 6,7 6,6 @@
#include "Operation.hpp"
#include <Audio/AudioDevice.hpp>
-#include <Audio/encoder/Encoder.hpp>
#include <Audio/AudioCommon.hpp>
#include <Audio/Profiles/Profile.hpp>
#include <Audio/Endpoint.hpp>
@@ 68,12 67,11 @@ namespace audio
void Unmute();
[[nodiscard]] auto IsMuted() const noexcept -> bool;
- std::unique_ptr<Stream> dataStreamOut;
- std::unique_ptr<Stream> dataStreamIn;
- std::unique_ptr<Encoder> enc;
+ std::unique_ptr<AbstractStream> dataStreamOut;
+ std::unique_ptr<AbstractStream> dataStreamIn;
std::shared_ptr<AudioDevice> audioDeviceCellular;
- std::unique_ptr<StreamConnection> outputConnection;
- std::unique_ptr<StreamConnection> inputConnection;
+ std::unique_ptr<StreamConnection> voiceOutputConnection;
+ std::unique_ptr<StreamConnection> voiceInputConnection;
};
} // namespace audio
M module-audio/Audio/StreamFactory.cpp => module-audio/Audio/StreamFactory.cpp +49 -3
@@ 4,6 4,8 @@
#include "StreamFactory.hpp"
#include "Endpoint.hpp"
+#include "transcode/TransformFactory.hpp"
+
#include <math/Math.hpp>
#include <algorithm>
@@ 15,24 17,45 @@
#include <cassert>
#include <cmath>
+using audio::AudioFormat;
using audio::Sink;
using audio::Source;
using audio::Stream;
using audio::StreamFactory;
+using audio::transcode::InputTranscodeProxy;
+using audio::transcode::Transform;
+using audio::transcode::TransformFactory;
using namespace std::chrono_literals;
StreamFactory::StreamFactory(std::chrono::milliseconds operationPeriodRequirement)
: periodRequirement(operationPeriodRequirement)
{}
-auto StreamFactory::makeStream(Source &source, Sink &sink, AudioFormat streamFormat) -> std::unique_ptr<Stream>
+auto StreamFactory::makeStream(Source &source, Sink &sink) -> std::unique_ptr<AbstractStream>
+{
+ auto sourceFormat = source.getSourceFormat();
+ auto sinkFormat = sink.getSinkFormat();
+
+ if (sourceFormat == sinkFormat) {
+ return makeStream(source.getTraits(), sink.getTraits(), sourceFormat);
+ }
+ else {
+ auto transformFactory = TransformFactory();
+ auto transform = transformFactory.makeTransform(sourceFormat, sinkFormat);
+
+ return makeInputTranscodingStream(source, sink, sinkFormat, std::move(transform));
+ }
+}
+
+auto StreamFactory::makeStream(Traits sourceTraits, Traits sinkTraits, AudioFormat streamFormat)
+ -> std::unique_ptr<Stream>
{
auto streamBuffering = defaultBuffering;
- auto endpointsTraits = {source.getTraits(), sink.getTraits()};
+ auto endpointsTraits = {sourceTraits, sinkTraits};
auto blockSizeConstraint = getBlockSizeConstraint(endpointsTraits);
auto &streamAllocator = negotiateAllocator(endpointsTraits);
auto timingConstraint = getTimingConstraints(std::initializer_list<std::optional<std::chrono::milliseconds>>{
- sink.getTraits().timeConstraint, source.getTraits().timeConstraint, periodRequirement});
+ sinkTraits.timeConstraint, sourceTraits.timeConstraint, periodRequirement});
if (streamFormat == audio::nullFormat) {
throw std::invalid_argument("No source format provided");
@@ 53,6 76,29 @@ auto StreamFactory::makeStream(Source &source, Sink &sink, AudioFormat streamFor
return std::make_unique<Stream>(streamFormat, streamAllocator, blockSizeConstraint.value(), streamBuffering);
}
+auto StreamFactory::makeStream(Source &source, Sink &sink, AudioFormat streamFormat) -> std::unique_ptr<Stream>
+{
+ return makeStream(source.getTraits(), sink.getTraits(), streamFormat);
+}
+
+auto StreamFactory::makeInputTranscodingStream(Source &source,
+ Sink &sink,
+ AudioFormat streamFormat,
+ std::shared_ptr<Transform>(transform))
+ -> std::unique_ptr<InputTranscodeProxy>
+{
+ auto sourceTraits = source.getTraits();
+
+ if (sourceTraits.blockSizeConstraint.has_value()) {
+ sourceTraits.blockSizeConstraint = transform->transformBlockSize(sourceTraits.blockSizeConstraint.value());
+ }
+
+ auto stream = makeStream(sourceTraits, sink.getTraits(), streamFormat);
+ auto transcodingStream = std::make_unique<InputTranscodeProxy>(std::move(stream), transform);
+
+ return transcodingStream;
+}
+
auto StreamFactory::getBlockSizeConstraint(std::initializer_list<audio::Endpoint::Traits> traitsList) const
-> std::optional<std::size_t>
{
M module-audio/Audio/StreamFactory.hpp => module-audio/Audio/StreamFactory.hpp +15 -4
@@ 5,6 5,8 @@
#include "Endpoint.hpp"
#include "Stream.hpp"
+#include "transcode/Transform.hpp"
+#include "transcode/InputTranscodeProxy.hpp"
#include <initializer_list>
#include <optional>
@@ 18,17 20,26 @@ namespace audio
public:
StreamFactory() = default;
explicit StreamFactory(std::chrono::milliseconds operationPeriodRequirement);
+
+ auto makeStream(Source &source, Sink &sink) -> std::unique_ptr<AbstractStream>;
auto makeStream(Source &source, Sink &sink, AudioFormat streamFormat) -> std::unique_ptr<Stream>;
+ auto makeInputTranscodingStream(Source &source,
+ Sink &sink,
+ AudioFormat streamFormat,
+ std::shared_ptr<transcode::Transform>(transform))
+ -> std::unique_ptr<transcode::InputTranscodeProxy>;
private:
+ using Traits = audio::Endpoint::Traits;
+
static constexpr auto defaultBuffering = 2U;
- auto getBlockSizeConstraint(std::initializer_list<audio::Endpoint::Traits> traitsList) const
- -> std::optional<std::size_t>;
+ auto makeStream(Traits sourceTraits, Traits sinkTraits, AudioFormat streamFormat) -> std::unique_ptr<Stream>;
+
+ auto getBlockSizeConstraint(std::initializer_list<Traits> traitsList) const -> std::optional<std::size_t>;
auto getTimingConstraints(std::initializer_list<std::optional<std::chrono::milliseconds>> timingConstraints)
const -> std::chrono::milliseconds;
- auto negotiateAllocator(std::initializer_list<audio::Endpoint::Traits> traitsList) noexcept
- -> Stream::Allocator &;
+ auto negotiateAllocator(std::initializer_list<Traits> traitsList) noexcept -> Stream::Allocator &;
std::optional<std::chrono::milliseconds> periodRequirement = std::nullopt;
M module-audio/Audio/StreamProxy.cpp => module-audio/Audio/StreamProxy.cpp +19 -19
@@ 6,95 6,95 @@
using audio::StreamProxy;
-StreamProxy::StreamProxy(AbstractStream &wrappedStream) noexcept : wrappedStream(wrappedStream)
+StreamProxy::StreamProxy(std::shared_ptr<AbstractStream> wrappedStream) noexcept : wrappedStream(wrappedStream)
{}
bool StreamProxy::push(void *data, std::size_t dataSize)
{
- return wrappedStream.push(data, dataSize);
+ return wrappedStream->push(data, dataSize);
}
bool StreamProxy::push(const Span &span)
{
- return wrappedStream.push(span);
+ return wrappedStream->push(span);
}
bool StreamProxy::push()
{
- return wrappedStream.push();
+ return wrappedStream->push();
}
bool StreamProxy::pop(Span &span)
{
- return wrappedStream.pop(span);
+ return wrappedStream->pop(span);
}
bool StreamProxy::reserve(Span &span)
{
- return wrappedStream.reserve(span);
+ return wrappedStream->reserve(span);
}
void StreamProxy::commit()
{
- wrappedStream.commit();
+ wrappedStream->commit();
}
void StreamProxy::release()
{
- wrappedStream.release();
+ wrappedStream->release();
}
bool StreamProxy::peek(Span &span)
{
- return wrappedStream.peek(span);
+ return wrappedStream->peek(span);
}
void StreamProxy::consume()
{
- wrappedStream.consume();
+ wrappedStream->consume();
}
void StreamProxy::unpeek()
{
- wrappedStream.unpeek();
+ wrappedStream->unpeek();
}
void StreamProxy::reset()
{
- wrappedStream.reset();
+ wrappedStream->reset();
}
void StreamProxy::registerListener(EventListener *listener)
{
- wrappedStream.registerListener(listener);
+ wrappedStream->registerListener(listener);
}
void StreamProxy::unregisterListeners(EventListener *listener)
{
- wrappedStream.unregisterListeners(listener);
+ wrappedStream->unregisterListeners(listener);
}
auto StreamProxy::getOutputTraits() const noexcept -> Traits
{
- return wrappedStream.getOutputTraits();
+ return wrappedStream->getOutputTraits();
}
auto StreamProxy::getInputTraits() const noexcept -> Traits
{
- return wrappedStream.getInputTraits();
+ return wrappedStream->getInputTraits();
}
bool StreamProxy::isEmpty() const noexcept
{
- return wrappedStream.isEmpty();
+ return wrappedStream->isEmpty();
}
bool StreamProxy::isFull() const noexcept
{
- return wrappedStream.isFull();
+ return wrappedStream->isFull();
}
auto StreamProxy::getWrappedStream() -> AbstractStream &
{
- return wrappedStream;
+ return *wrappedStream;
}
M module-audio/Audio/StreamProxy.hpp => module-audio/Audio/StreamProxy.hpp +4 -2
@@ 5,6 5,8 @@
#include "AbstractStream.hpp"
+#include <memory>
+
namespace audio
{
@@ 20,7 22,7 @@ namespace audio
*
* @param wrappedStream - stream to wrap.
*/
- explicit StreamProxy(AbstractStream &wrappedStream) noexcept;
+ explicit StreamProxy(std::shared_ptr<AbstractStream> wrappedStream) noexcept;
// listener registration
void registerListener(EventListener *listener) override;
@@ 55,7 57,7 @@ namespace audio
auto getWrappedStream() -> AbstractStream &;
private:
- AbstractStream &wrappedStream;
+ std::shared_ptr<AbstractStream> wrappedStream;
};
} // namespace audio
M module-audio/Audio/test/TestStream.cpp => module-audio/Audio/test/TestStream.cpp +1 -1
@@ 51,6 51,7 @@ bool TestStream::pop(AbstractStream::Span &span)
bool TestStream::reserve(AbstractStream::Span &span)
{
+ span = Span{.data = data.get(), .dataSize = bufSize};
return true;
}
@@ 62,7 63,6 @@ void TestStream::release()
bool TestStream::peek(AbstractStream::Span &span)
{
- span = Span{.data = data.get(), .dataSize = bufSize};
return true;
}
M module-audio/Audio/test/unittest_stream.cpp => module-audio/Audio/test/unittest_stream.cpp +68 -19
@@ 8,10 8,13 @@
#include <Audio/AudioFormat.hpp>
#include <Audio/StreamProxy.hpp>
#include <Audio/StreamFactory.hpp>
+#include <Audio/transcode/BasicDecimator.hpp>
#include "MockEndpoint.hpp"
#include "MockStream.hpp"
+#include <memory>
+
#include <cstdint>
#include <cstring>
@@ 356,21 359,21 @@ TEST(Stream, spanEquality)
TEST(Proxy, Write)
{
- MockStream mock;
- auto proxy = ::audio::StreamProxy(mock);
+ auto mock = std::make_shared<MockStream>();
+ auto proxy = ::audio::StreamProxy(std::static_pointer_cast<::audio::AbstractStream>(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_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);
+ EXPECT_CALL(*mock, reserve).Times(1);
+ EXPECT_CALL(*mock, commit).Times(1);
+ EXPECT_CALL(*mock, release).Times(1);
proxy.reserve(span);
proxy.commit();
@@ 379,16 382,16 @@ TEST(Proxy, Write)
TEST(Proxy, Read)
{
- MockStream mock;
- auto proxy = ::audio::StreamProxy(mock);
+ auto mock = std::make_shared<MockStream>();
+ auto proxy = ::audio::StreamProxy(std::static_pointer_cast<::audio::AbstractStream>(mock));
::audio::AbstractStream::Span span{.data = reinterpret_cast<uint8_t *>(0xdeadbeef), .dataSize = 0xc00f33b4d};
- EXPECT_CALL(mock, pop(span)).Times(1).WillOnce(Return(true));
+ 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_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));
@@ 399,12 402,12 @@ TEST(Proxy, Read)
TEST(Proxy, Misc)
{
- MockStream mock;
- auto proxy = ::audio::StreamProxy(mock);
+ auto mock = std::make_shared<MockStream>();
+ auto proxy = ::audio::StreamProxy(std::static_pointer_cast<::audio::AbstractStream>(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));
+ 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());
@@ 518,6 521,52 @@ TEST(Factory, NoConstraints)
EXPECT_EQ(stream->getOutputTraits().blockSize, 64);
}
+TEST(Factory, TranscodingStreamSinkTraits)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory(2ms);
+ auto format = ::audio::AudioFormat(8000, 16, 1);
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 60U}));
+
+ auto stream = factory.makeStream(mockSource, mockSink, format);
+
+ EXPECT_EQ(stream->getBlockCount(), 2);
+ EXPECT_EQ(stream->getOutputTraits().blockSize, 60);
+
+ auto decimatorTransform = std::make_shared<::audio::transcode::BasicDecimator<std::uint16_t, 1, 2>>();
+ auto transcodingStream = factory.makeInputTranscodingStream(
+ mockSource, mockSink, format, std::static_pointer_cast<::audio::transcode::Transform>(decimatorTransform));
+
+ EXPECT_EQ(transcodingStream->getInputTraits().blockSize, 120);
+ EXPECT_EQ(transcodingStream->getOutputTraits().blockSize, 60);
+}
+
+TEST(Factory, TranscodingStreamSourceTraits)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory(2ms);
+ auto format = ::audio::AudioFormat(8000, 16, 1);
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 60U}));
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+
+ auto stream = factory.makeStream(mockSource, mockSink, format);
+
+ EXPECT_EQ(stream->getBlockCount(), 2);
+ EXPECT_EQ(stream->getOutputTraits().blockSize, 60);
+
+ auto decimatorTransform = std::make_shared<::audio::transcode::BasicDecimator<std::uint16_t, 1, 2>>();
+ auto transcodingStream = factory.makeInputTranscodingStream(
+ mockSource, mockSink, format, std::static_pointer_cast<::audio::transcode::Transform>(decimatorTransform));
+
+ EXPECT_EQ(transcodingStream->getInputTraits().blockSize, 60);
+ EXPECT_EQ(transcodingStream->getOutputTraits().blockSize, 30);
+}
+
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
M module-audio/Audio/test/unittest_transcode.cpp => module-audio/Audio/test/unittest_transcode.cpp +205 -69
@@ 15,10 15,15 @@
#include <Audio/transcode/TransformComposite.hpp>
#include <Audio/transcode/BasicInterpolator.hpp>
#include <Audio/transcode/BasicDecimator.hpp>
+#include <Audio/transcode/NullTransform.hpp>
+#include <Audio/transcode/TransformFactory.hpp>
#include <cstdlib>
+using ::audio::transcode::NullTransform;
+using ::testing::_;
using ::testing::Return;
+using ::testing::SetArgReferee;
using ::testing::audio::MockStream;
using ::testing::audio::MockStreamEventListener;
@@ 27,12 32,12 @@ constexpr std::size_t testStreamSize = 8;
class InverseTransform : public audio::transcode::Transform
{
public:
- auto transform(const Span &span, const Span &transformSpace) const -> Span override
+ auto transform(const Span &input, const Span &output) const -> Span override
{
- for (std::size_t i = 0; i < span.dataSize; i++) {
- span.data[i] = ~span.data[i];
+ for (std::size_t i = 0; i < input.dataSize; i++) {
+ output.data[i] = ~input.data[i];
}
- return span;
+ return output;
}
auto transformBlockSize(std::size_t inputBufferSize) const noexcept -> std::size_t override
@@ 40,23 45,9 @@ class InverseTransform : public audio::transcode::Transform
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
+ auto transformBlockSizeInverted(std::size_t outputBufferSize) const noexcept -> std::size_t override
{
- return span;
+ return outputBufferSize;
}
auto validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool override
@@ 68,22 59,17 @@ class NullTransform : public audio::transcode::Transform
{
return inputFormat;
}
-
- auto transformBlockSize(std::size_t inputBufferSize) const noexcept -> std::size_t override
- {
- return inputBufferSize;
- }
};
TEST(Transcode, Reset)
{
- MockStream mock;
- NullTransform nullTransform;
+ auto mock = std::make_shared<MockStream>();
+ auto nullTransform = std::make_shared<NullTransform>();
- EXPECT_CALL(mock, getInputTraits);
- audio::transcode::InputTranscodeProxy proxy(mock, nullTransform);
+ EXPECT_CALL(*mock, getInputTraits);
+ audio::transcode::InputTranscodeProxy proxy(std::static_pointer_cast<::audio::AbstractStream>(mock), nullTransform);
- EXPECT_CALL(mock, reset()).Times(1);
+ EXPECT_CALL(*mock, reset()).Times(1);
proxy.reset();
}
@@ 92,38 78,39 @@ 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);
+ auto inv = std::make_shared<InverseTransform>();
+ auto stream = std::make_shared<::testing::audio::TestStream>(testStreamSize);
+ ::audio::transcode::InputTranscodeProxy transform(std::static_pointer_cast<::audio::AbstractStream>(stream), inv);
transform.push(::audio::AbstractStream::Span{testData, testStreamSize});
- EXPECT_TRUE(stream.checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+ EXPECT_TRUE(stream->checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
}
TEST(Transcode, FailedPeek)
{
- NullTransform nullTransform;
- MockStream mockStream;
+ auto nullTransform = std::make_shared<NullTransform>();
+ auto mockStream = std::make_shared<MockStream>();
- EXPECT_CALL(mockStream, getInputTraits);
- ::audio::transcode::InputTranscodeProxy transform(mockStream, nullTransform);
+ EXPECT_CALL(*mockStream, getInputTraits);
+ ::audio::transcode::InputTranscodeProxy transform(std::static_pointer_cast<::audio::AbstractStream>(mockStream),
+ nullTransform);
::audio::AbstractStream::Span dataSpan;
- EXPECT_CALL(mockStream, peek).Times(1).WillOnce(Return(false));
+ 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);
+ auto inv = std::make_shared<InverseTransform>();
+ auto stream = std::make_shared<::testing::audio::TestStream>(testStreamSize);
+ ::audio::transcode::InputTranscodeProxy transform(std::static_pointer_cast<::audio::AbstractStream>(stream), inv);
::audio::AbstractStream::Span span;
- stream.setData(0);
+ stream->setData(0);
- ASSERT_TRUE(transform.peek(span));
+ ASSERT_TRUE(transform.reserve(span));
ASSERT_TRUE(span.data != nullptr);
ASSERT_TRUE(span.dataSize == testStreamSize);
@@ 132,36 119,106 @@ TEST(Transcode, Commit)
}
transform.commit();
- EXPECT_TRUE(stream.checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+ EXPECT_TRUE(stream->checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+
+ // no conversion on commit with no reserve
+ stream->commit();
+ EXPECT_TRUE(stream->checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+}
+
+TEST(Transcode, ReserveShrinking)
+{
+ static std::uint16_t streamData[8];
+ auto streamDataSpan = ::audio::AbstractStream::Span{.data = reinterpret_cast<std::uint8_t *>(streamData),
+ .dataSize = sizeof(streamData)};
+
+ auto decimatorTransform = std::make_shared<::audio::transcode::BasicDecimator<std::uint16_t, 1, 2>>();
+ auto mockStream = std::make_shared<MockStream>();
+
+ EXPECT_CALL(*mockStream, getInputTraits)
+ .WillRepeatedly(Return(::audio::AbstractStream::Traits{.blockSize = streamDataSpan.dataSize,
+ .format = audio::AudioFormat(8000, 16, 1)}));
+
+ auto transcodingProxy = ::audio::transcode::InputTranscodeProxy(mockStream, decimatorTransform);
+
+ EXPECT_CALL(*mockStream, reserve(_)).WillOnce(DoAll(SetArgReferee<0>(streamDataSpan), Return(true)));
+
+ ::audio::AbstractStream::Span span;
+ transcodingProxy.reserve(span);
+
+ EXPECT_EQ(span.dataSize, 16 * sizeof(std::uint16_t));
+
+ auto buf = reinterpret_cast<std::uint16_t *>(span.data);
+ for (unsigned int i = 0; i < 16; i++) {
+ buf[i] = i;
+ }
+
+ EXPECT_CALL(*mockStream, commit);
+
+ transcodingProxy.commit();
+
+ static std::uint16_t expectedData[8] = {0, 2, 4, 6, 8, 10, 12, 14};
+ EXPECT_TRUE(memcmp(expectedData, streamData, sizeof(expectedData)) == 0);
+}
+
+TEST(Transcode, ReserveBloating)
+{
+ static std::uint16_t streamData[8];
+ auto streamDataSpan = ::audio::AbstractStream::Span{.data = reinterpret_cast<std::uint8_t *>(streamData),
+ .dataSize = sizeof(streamData)};
+
+ auto interpolatorTransform = std::make_shared<::audio::transcode::BasicInterpolator<std::uint16_t, 1, 2>>();
+ auto mockStream = std::make_shared<MockStream>();
+
+ EXPECT_CALL(*mockStream, getInputTraits)
+ .WillRepeatedly(Return(::audio::AbstractStream::Traits{.blockSize = streamDataSpan.dataSize,
+ .format = audio::AudioFormat(8000, 16, 1)}));
- // no conversion on commit with no peek
- stream.commit();
- EXPECT_TRUE(stream.checkData(::audio::AbstractStream::Span{invertedData, testStreamSize}));
+ auto transcodingProxy = ::audio::transcode::InputTranscodeProxy(mockStream, interpolatorTransform);
+
+ EXPECT_CALL(*mockStream, reserve(_)).WillOnce(DoAll(SetArgReferee<0>(streamDataSpan), Return(true)));
+
+ ::audio::AbstractStream::Span span;
+ transcodingProxy.reserve(span);
+
+ EXPECT_EQ(span.dataSize, 4 * sizeof(std::uint16_t));
+
+ auto buf = reinterpret_cast<std::uint16_t *>(span.data);
+ for (unsigned int i = 0; i < 4; i++) {
+ buf[i] = i;
+ }
+
+ EXPECT_CALL(*mockStream, commit);
+
+ transcodingProxy.commit();
+
+ static std::uint16_t expectedData[8] = {0, 0, 1, 1, 2, 2, 3, 3};
+ EXPECT_TRUE(memcmp(expectedData, streamData, sizeof(expectedData)) == 0);
}
TEST(Transcode, Traits)
{
auto testFormat = ::audio::AudioFormat(44100, 16, 1);
- ::audio::transcode::MonoToStereo m2s;
- MockStream mockStream;
+ auto m2s = std::make_shared<::audio::transcode::MonoToStereo>();
+ auto mockStream = std::make_shared<MockStream>();
- EXPECT_CALL(mockStream, getInputTraits)
+ EXPECT_CALL(*mockStream, getInputTraits)
.Times(1)
.WillOnce(Return(::audio::AbstractStream::Traits{.blockSize = 128, .format = testFormat}));
- ::audio::transcode::InputTranscodeProxy proxy(mockStream, m2s);
+ ::audio::transcode::InputTranscodeProxy proxy(std::static_pointer_cast<::audio::AbstractStream>(mockStream), m2s);
- EXPECT_CALL(mockStream, getInputTraits)
+ EXPECT_CALL(*mockStream, getInputTraits)
.Times(1)
.WillOnce(Return(::audio::AbstractStream::Traits{.blockSize = 128, .format = testFormat}));
- EXPECT_CALL(mockStream, getOutputTraits)
+ 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.blockSize, 64);
EXPECT_EQ(proxyInputTraits.format.getChannels(), 2);
EXPECT_EQ(proxyInputTraits.format.getSampleRate(), 44100);
EXPECT_EQ(proxyInputTraits.format.getBitWidth(), 16);
@@ 176,18 233,18 @@ TEST(Transcode, Traits)
TEST(Transcode, ListenersWrap)
{
- MockStream mock;
+ auto mock = std::make_shared<MockStream>();
+ auto nullTransform = std::make_shared<NullTransform>();
MockStreamEventListener listener;
- NullTransform nullTransform;
- EXPECT_CALL(mock, getInputTraits);
- audio::transcode::InputTranscodeProxy proxy(mock, nullTransform);
+ EXPECT_CALL(*mock, getInputTraits);
+ audio::transcode::InputTranscodeProxy proxy(std::static_pointer_cast<::audio::AbstractStream>(mock), nullTransform);
- EXPECT_CALL(mock, registerListener(&listener)).Times(1);
+ EXPECT_CALL(*mock, registerListener(&listener)).Times(1);
proxy.registerListener(&listener);
- EXPECT_CALL(mock, unregisterListeners(&listener)).Times(1);
+ EXPECT_CALL(*mock, unregisterListeners(&listener)).Times(1);
proxy.unregisterListeners(&listener);
}
@@ 212,10 269,6 @@ TEST(Transform, MonoToStereo)
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);
@@ 223,9 276,10 @@ TEST(Transform, MonoToStereo)
TEST(Transform, Composite)
{
- audio::transcode::MonoToStereo m2s;
- InverseTransform inv;
- audio::transcode::TransformComposite composite{&inv, &m2s};
+ auto m2s = std::make_shared<audio::transcode::MonoToStereo>();
+ auto inv = std::make_shared<InverseTransform>();
+ audio::transcode::TransformComposite composite(
+ std::vector<std::shared_ptr<::audio::transcode::Transform>>{m2s, inv});
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[] = {
@@ 252,7 306,7 @@ TEST(Transform, Composite)
auto output = ::audio::AbstractStream::Span{.data = reinterpret_cast<std::uint8_t *>(&outputBuffer[0]),
.dataSize = sizeof(outputBuffer)};
- ASSERT_EQ(output.dataSize, composite.getTransformSize(sizeof(inputBuffer)));
+ ASSERT_EQ(output.dataSize, composite.transformBlockSize(sizeof(inputBuffer)));
auto result = composite.transform(input, output);
ASSERT_EQ(result.dataSize, sizeof(expectedResult));
ASSERT_EQ(memcmp(result.data, expectedResult, sizeof(expectedResult)), 0);
@@ 327,3 381,85 @@ TEST(Transform, BasicDecimator)
EXPECT_EQ(outputSpan.dataSize, sizeof(uint16_t) * 4);
EXPECT_EQ(memcmp(outputSpan.data, expectBuffer, outputSpan.dataSize), 0);
}
+
+TEST(Transform, FactorySampleRateInterpolator)
+{
+ auto factory = ::audio::transcode::TransformFactory();
+ auto sourceFormat = ::audio::AudioFormat{8000, 16, 1};
+ auto sinkFormat = ::audio::AudioFormat{16000, 16, 1};
+
+ auto transform = factory.makeTransform(sourceFormat, sinkFormat);
+
+ EXPECT_STREQ(typeid(*transform).name(), typeid(::audio::transcode::BasicInterpolator<std::uint16_t, 1, 2>).name());
+}
+
+TEST(Transform, FactorySampleRateDecimator)
+{
+ auto factory = ::audio::transcode::TransformFactory();
+ auto sourceFormat = ::audio::AudioFormat{16000, 16, 1};
+ auto sinkFormat = ::audio::AudioFormat{8000, 16, 1};
+
+ auto transform = factory.makeTransform(sourceFormat, sinkFormat);
+
+ EXPECT_STREQ(typeid(*transform).name(), typeid(::audio::transcode::BasicDecimator<std::uint16_t, 1, 2>).name());
+ EXPECT_EQ(transform->transformFormat(sourceFormat), sinkFormat);
+}
+
+TEST(Tranform, FactoryNullTransform)
+{
+ auto factory = ::audio::transcode::TransformFactory();
+ auto sourceFormat = ::audio::AudioFormat{8000, 16, 1};
+ auto sinkFormat = ::audio::AudioFormat{8000, 16, 1};
+
+ auto transform = factory.makeTransform(sourceFormat, sinkFormat);
+
+ EXPECT_STREQ(typeid(*transform).name(), typeid(::audio::transcode::NullTransform).name());
+ EXPECT_EQ(transform->transformFormat(sourceFormat), sinkFormat);
+}
+
+TEST(Transform, FactoryMonoToStereo)
+{
+ auto factory = ::audio::transcode::TransformFactory();
+ auto sourceFormat = ::audio::AudioFormat{8000, 16, 1};
+ auto sinkFormat = ::audio::AudioFormat{8000, 16, 2};
+
+ auto transform = factory.makeTransform(sourceFormat, sinkFormat);
+
+ EXPECT_STREQ(typeid(*transform).name(), typeid(::audio::transcode::MonoToStereo).name());
+ EXPECT_EQ(transform->transformFormat(sourceFormat), sinkFormat);
+}
+
+TEST(Transform, FactoryComposite)
+{
+ auto factory = ::audio::transcode::TransformFactory();
+ auto sourceFormat = ::audio::AudioFormat{16000, 16, 1};
+ auto sinkFormat = ::audio::AudioFormat{32000, 16, 2};
+
+ auto transform = factory.makeTransform(sourceFormat, sinkFormat);
+
+ EXPECT_STREQ(typeid(*transform).name(), typeid(::audio::transcode::TransformComposite).name());
+ EXPECT_EQ(transform->transformFormat(sourceFormat), sinkFormat);
+}
+
+TEST(Transform, FactoryErrors)
+{
+ auto factory = ::audio::transcode::TransformFactory();
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{16000, 16, 1}, ::audio::AudioFormat{16000, 24, 1}),
+ std::runtime_error);
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{44100, 16, 1}, ::audio::AudioFormat{48000, 16, 1}),
+ std::invalid_argument);
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{8000, 16, 1}, ::audio::AudioFormat{24000, 16, 1}),
+ std::invalid_argument);
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{16000, 32, 1}, ::audio::AudioFormat{8000, 32, 1}),
+ std::invalid_argument);
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{8000, 16, 2}, ::audio::AudioFormat{16000, 16, 2}),
+ std::invalid_argument);
+
+ // channel conversions
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{8000, 16, 1}, ::audio::AudioFormat{8000, 16, 3}),
+ std::invalid_argument);
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{8000, 16, 2}, ::audio::AudioFormat{8000, 16, 1}),
+ std::invalid_argument);
+ EXPECT_THROW(factory.makeTransform(::audio::AudioFormat{8000, 32, 1}, ::audio::AudioFormat{8000, 32, 2}),
+ std::invalid_argument);
+}
M module-audio/Audio/transcode/BasicDecimator.hpp => module-audio/Audio/transcode/BasicDecimator.hpp +5 -0
@@ 42,6 42,11 @@ namespace audio::transcode
return blockSize / Ratio;
}
+ auto transformBlockSizeInverted(std::size_t blockSize) const noexcept -> std::size_t override
+ {
+ return blockSize * Ratio;
+ }
+
auto transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat override
{
return audio::AudioFormat{
M module-audio/Audio/transcode/BasicInterpolator.hpp => module-audio/Audio/transcode/BasicInterpolator.hpp +5 -0
@@ 42,6 42,11 @@ namespace audio::transcode
return blockSize * Ratio;
}
+ auto transformBlockSizeInverted(std::size_t blockSize) const noexcept -> std::size_t override
+ {
+ return blockSize / Ratio;
+ }
+
auto transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat override
{
return audio::AudioFormat{
M module-audio/Audio/transcode/InputTranscodeProxy.cpp => module-audio/Audio/transcode/InputTranscodeProxy.cpp +18 -13
@@ 4,13 4,15 @@
#include "InputTranscodeProxy.hpp"
#include "Transform.hpp"
+#include <memory>
#include <utility>
using audio::transcode::InputTranscodeProxy;
-InputTranscodeProxy::InputTranscodeProxy(AbstractStream &wrappedStream, Transform &transform) noexcept
+InputTranscodeProxy::InputTranscodeProxy(std::shared_ptr<AbstractStream> wrappedStream,
+ std::shared_ptr<Transform> transform) noexcept
: StreamProxy(wrappedStream), transform(transform),
- transcodingSpaceSize(transform.getTransformSize(wrappedStream.getInputTraits().blockSize)),
+ transcodingSpaceSize(transform->transformBlockSizeInverted(wrappedStream->getInputTraits().blockSize)),
transcodingSpace(std::make_unique<std::uint8_t[]>(transcodingSpaceSize)), transcodingSpaceSpan{
.data = transcodingSpace.get(),
.dataSize = transcodingSpaceSize}
@@ 18,23 20,26 @@ InputTranscodeProxy::InputTranscodeProxy(AbstractStream &wrappedStream, Transfor
bool InputTranscodeProxy::push(const Span &span)
{
- return getWrappedStream().push(transform.transform(span, transcodingSpaceSpan));
+ return getWrappedStream().push(transform->transform(span, transcodingSpaceSpan));
}
void InputTranscodeProxy::commit()
{
- transform.transform(peekedSpan, transcodingSpaceSpan);
- getWrappedStream().commit();
- peekedSpan.reset();
+ if (isReserved) {
+ transform->transform(transcodingSpaceSpan, reservedSpan);
+ getWrappedStream().commit();
+ reservedSpan.reset();
+ isReserved = false;
+ }
}
-bool InputTranscodeProxy::peek(Span &span)
+bool InputTranscodeProxy::reserve(Span &span)
{
- auto result = getWrappedStream().peek(span);
+ auto result = getWrappedStream().reserve(span);
- if (result) {
- peekedSpan = span;
- }
+ reservedSpan = span;
+ span = transcodingSpaceSpan;
+ isReserved = true;
return result;
}
@@ 42,8 47,8 @@ bool InputTranscodeProxy::peek(Span &span)
auto InputTranscodeProxy::getInputTraits() const noexcept -> Traits
{
auto originalTraits = StreamProxy::getInputTraits();
- auto transcodedFormat = transform.transformFormat(originalTraits.format);
- auto transcodedBlockSize = transform.transformBlockSize(originalTraits.blockSize);
+ auto transcodedFormat = transform->transformFormat(originalTraits.format);
+ auto transcodedBlockSize = transform->transformBlockSizeInverted(originalTraits.blockSize);
return Traits{.blockSize = transcodedBlockSize, .format = transcodedFormat};
}
M module-audio/Audio/transcode/InputTranscodeProxy.hpp => module-audio/Audio/transcode/InputTranscodeProxy.hpp +6 -4
@@ 23,19 23,21 @@ namespace audio::transcode
* @param wrappedStream
* @param transform to apply on the input
*/
- explicit InputTranscodeProxy(AbstractStream &wrappedStream, Transform &transform) noexcept;
+ explicit InputTranscodeProxy(std::shared_ptr<AbstractStream> wrappedStream,
+ std::shared_ptr<Transform> transform) noexcept;
bool push(const Span &span) override;
void commit() override;
- bool peek(Span &span) override;
+ bool reserve(Span &span) override;
[[nodiscard]] auto getInputTraits() const noexcept -> Traits override;
private:
- Transform &transform;
- Span peekedSpan;
+ std::shared_ptr<Transform> transform;
+ Span reservedSpan;
std::size_t transcodingSpaceSize;
std::unique_ptr<std::uint8_t[]> transcodingSpace;
Span transcodingSpaceSpan;
+ bool isReserved = false;
};
}; // namespace audio::transcode
M module-audio/Audio/transcode/MonoToStereo.cpp => module-audio/Audio/transcode/MonoToStereo.cpp +6 -1
@@ 9,7 9,7 @@ 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 outputSpan = Span{.data = transformSpace.data, .dataSize = transformBlockSize(span.dataSize)};
auto outputBuffer = reinterpret_cast<std::uint16_t *>(transformSpace.data);
auto inputBuffer = reinterpret_cast<std::uint16_t *>(span.data);
@@ 25,6 25,11 @@ auto MonoToStereo::transformBlockSize(std::size_t inputBufferSize) const noexcep
return 2 * inputBufferSize;
}
+auto MonoToStereo::transformBlockSizeInverted(std::size_t outputBufferSize) const noexcept -> std::size_t
+{
+ return outputBufferSize / 2;
+}
+
auto MonoToStereo::validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool
{
return inputFormat.getChannels() == 1;
M module-audio/Audio/transcode/MonoToStereo.hpp => module-audio/Audio/transcode/MonoToStereo.hpp +1 -0
@@ 17,6 17,7 @@ namespace audio::transcode
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;
+ auto transformBlockSizeInverted(std::size_t blockSize) const noexcept -> std::size_t override;
};
} // namespace audio::transcode
A module-audio/Audio/transcode/NullTransform.cpp => module-audio/Audio/transcode/NullTransform.cpp +31 -0
@@ 0,0 1,31 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "NullTransform.hpp"
+
+using ::audio::transcode::NullTransform;
+
+auto NullTransform::transform(const Span &span, const Span &transformSpace) const -> Span
+{
+ return span;
+}
+
+auto NullTransform::validateInputFormat(const audio::AudioFormat &inputFormat) const noexcept -> bool
+{
+ return true;
+}
+
+auto NullTransform::transformFormat(const audio::AudioFormat &inputFormat) const noexcept -> audio::AudioFormat
+{
+ return inputFormat;
+}
+
+auto NullTransform::transformBlockSize(std::size_t inputBufferSize) const noexcept -> std::size_t
+{
+ return inputBufferSize;
+}
+
+auto NullTransform::transformBlockSizeInverted(std::size_t outputBufferSize) const noexcept -> std::size_t
+{
+ return outputBufferSize;
+}
A module-audio/Audio/transcode/NullTransform.hpp => module-audio/Audio/transcode/NullTransform.hpp +21 -0
@@ 0,0 1,21 @@
+// 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
+{
+
+ class NullTransform : 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 inputBufferSize) const noexcept -> std::size_t override;
+ auto transformBlockSizeInverted(std::size_t outputBufferSize) const noexcept -> std::size_t override;
+ };
+
+} // namespace audio::transcode
M module-audio/Audio/transcode/Transform.hpp => module-audio/Audio/transcode/Transform.hpp +7 -11
@@ 23,10 23,10 @@ namespace audio::transcode
* @brief Transforms data within range
*
* @param inputSpan - input data to transform
- * @param transformSpace - overall space available to the transform
- * @return Output data (contained within )
+ * @param outputSpan - space for transformation output
+ * @return Output data
*/
- virtual auto transform(const Span &inputSpan, const Span &transformSpace) const -> Span = 0;
+ virtual auto transform(const Span &inputSpan, const Span &outputSpan) const -> Span = 0;
/**
* @brief Checks if audio format is suitable for transformation.
@@ 54,17 54,13 @@ namespace audio::transcode
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.
+ * @brief Calculates required size of an input data to get transformed data of size equal
+ * outputBlockSize
*
- * @param inputBufferSize
+ * @param outputBlockSize - size of an output block
* @return std::size_t
*/
- virtual auto getTransformSize(std::size_t inputBufferSize) const noexcept -> std::size_t
- {
- return transformBlockSize(inputBufferSize);
- }
+ virtual auto transformBlockSizeInverted(std::size_t outputBlockSize) const noexcept -> std::size_t = 0;
/**
* @brief A convenience transform operator.
M module-audio/Audio/transcode/TransformComposite.cpp => module-audio/Audio/transcode/TransformComposite.cpp +13 -14
@@ 7,12 7,13 @@
#include <Audio/AudioFormat.hpp>
#include <algorithm>
+#include <memory>
#include <initializer_list>
using audio::transcode::Transform;
using audio::transcode::TransformComposite;
-TransformComposite::TransformComposite(std::initializer_list<Transform *> transforms) : children(transforms)
+TransformComposite::TransformComposite(std::vector<std::shared_ptr<Transform>> transforms) : children(transforms)
{}
auto TransformComposite::transform(const Span &input, const Span &conversionSpace) const -> Span
@@ 25,19 26,6 @@ auto TransformComposite::transform(const Span &input, const Span &conversionSpac
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;
@@ 73,3 61,14 @@ auto TransformComposite::transformBlockSize(std::size_t blockSize) const noexcep
return transformedBlockSize;
}
+
+auto TransformComposite::transformBlockSizeInverted(std::size_t blockSize) const noexcept -> std::size_t
+{
+ std::size_t transformedBlockSize = blockSize;
+
+ for (auto t : children) {
+ transformedBlockSize = t->transformBlockSizeInverted(transformedBlockSize);
+ }
+
+ return transformedBlockSize;
+}
M module-audio/Audio/transcode/TransformComposite.hpp => module-audio/Audio/transcode/TransformComposite.hpp +4 -4
@@ 7,7 7,7 @@
#include <Audio/AudioFormat.hpp>
-#include <initializer_list>
+#include <memory>
#include <vector>
namespace audio::transcode
@@ 24,16 24,16 @@ namespace audio::transcode
*
* @param children - transforms to be executed on each transform call.
*/
- TransformComposite(std::initializer_list<Transform *> children);
+ TransformComposite(std::vector<std::shared_ptr<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;
+ auto transformBlockSizeInverted(std::size_t blockSize) const noexcept -> std::size_t override;
private:
- std::vector<Transform *> children;
+ std::vector<std::shared_ptr<Transform>> children;
};
} // namespace audio::transcode
A module-audio/Audio/transcode/TransformFactory.cpp => module-audio/Audio/transcode/TransformFactory.cpp +111 -0
@@ 0,0 1,111 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "TransformFactory.hpp"
+
+#include <Audio/AudioFormat.hpp>
+
+#include "BasicDecimator.hpp"
+#include "BasicInterpolator.hpp"
+#include "MonoToStereo.hpp"
+#include "NullTransform.hpp"
+#include "Transform.hpp"
+#include "TransformComposite.hpp"
+
+#include <algorithm>
+#include <memory>
+#include <stdexcept>
+#include <vector>
+
+#include <cassert>
+
+using audio::transcode::NullTransform;
+using audio::transcode::Transform;
+using audio::transcode::TransformFactory;
+
+auto TransformFactory::makeTransform(AudioFormat sourceFormat, AudioFormat sinkFormat) const
+ -> std::unique_ptr<Transform>
+{
+ auto transforms = std::vector<std::unique_ptr<Transform>>{};
+
+ if (sourceFormat == sinkFormat) {
+ return std::make_unique<NullTransform>();
+ }
+
+ if (sourceFormat.getBitWidth() != sinkFormat.getBitWidth()) {
+ throw std::runtime_error("Bitwidth conversion is not implemented");
+ }
+
+ if (sourceFormat.getSampleRate() != sinkFormat.getSampleRate()) {
+ transforms.push_back(getSamplerateTransform(sourceFormat, sinkFormat));
+ }
+
+ if (sourceFormat.getChannels() != sinkFormat.getChannels()) {
+ transforms.push_back(getChannelsTransform(sourceFormat, sinkFormat));
+ }
+
+ assert(!transforms.empty());
+
+ if (transforms.size() > 1) {
+ auto transformsListForComposite = std::vector<std::shared_ptr<Transform>>{};
+ std::move(std::begin(transforms), std::end(transforms), std::back_inserter(transformsListForComposite));
+ return std::make_unique<audio::transcode::TransformComposite>(transformsListForComposite);
+ }
+ else {
+ return std::move(transforms[0]);
+ }
+}
+
+auto TransformFactory::getSamplerateTransform(AudioFormat sourceFormat, AudioFormat sinkFormat) const
+ -> std::unique_ptr<Transform>
+{
+ static constexpr auto supportedSampleRateCoversionRatio = 2U;
+ static constexpr auto supportedBitWidth = 16U;
+ static constexpr auto supportedChannelCount = 1U;
+
+ auto sourceRate = sourceFormat.getSampleRate();
+ auto sinkRate = sinkFormat.getSampleRate();
+
+ auto greater = std::max(sourceRate, sinkRate);
+ auto lesser = std::min(sourceRate, sinkRate);
+
+ if (greater % lesser != 0) {
+ throw std::invalid_argument("Sample rate conversion is not supported");
+ }
+
+ auto ratio = greater / lesser;
+ if (ratio != supportedSampleRateCoversionRatio) {
+ throw std::invalid_argument("Sample rate conversion is not supported (ratio != 2)");
+ }
+
+ if (sourceFormat.getBitWidth() != supportedBitWidth) {
+ throw std::invalid_argument("Sample rate conversion with bit width other than 16 is not supported");
+ }
+
+ if (sourceFormat.getChannels() != supportedChannelCount) {
+ throw std::invalid_argument("Sample rate conversion supported with mono only");
+ }
+
+ if (sourceRate > sinkRate) {
+ return std::make_unique<audio::transcode::BasicDecimator<std::uint16_t,
+ supportedChannelCount,
+ supportedSampleRateCoversionRatio>>();
+ }
+ else {
+ return std::make_unique<audio::transcode::BasicInterpolator<std::uint16_t,
+ supportedChannelCount,
+ supportedSampleRateCoversionRatio>>();
+ }
+}
+
+auto TransformFactory::getChannelsTransform(AudioFormat sourceFormat, AudioFormat sinkFormat) const
+ -> std::unique_ptr<Transform>
+{
+ if (sourceFormat.getChannels() == 1 && sinkFormat.getChannels() == 2 && sourceFormat.getBitWidth() == 16) {
+ auto transform = std::make_unique<audio::transcode::MonoToStereo>();
+ return transform;
+ }
+ else {
+ throw std::invalid_argument("Channels conversion is not supported");
+ }
+}
A module-audio/Audio/transcode/TransformFactory.hpp => module-audio/Audio/transcode/TransformFactory.hpp +24 -0
@@ 0,0 1,24 @@
+// 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/transcode/Transform.hpp>
+
+#include <memory>
+
+namespace audio::transcode
+{
+ class TransformFactory
+ {
+ public:
+ auto makeTransform(AudioFormat sourceFormat, AudioFormat sinkFormat) const -> std::unique_ptr<Transform>;
+
+ private:
+ auto getSamplerateTransform(AudioFormat sourceFormat, AudioFormat sinkFormat) const
+ -> std::unique_ptr<Transform>;
+ auto getChannelsTransform(AudioFormat sourceFormat, AudioFormat sinkFormat) const -> std::unique_ptr<Transform>;
+ };
+
+}; // namespace audio::transcode
M module-audio/CMakeLists.txt => module-audio/CMakeLists.txt +2 -0
@@ 44,7 44,9 @@ target_sources(
${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/NullTransform.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Audio/transcode/TransformComposite.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/Audio/transcode/TransformFactory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Audio/VolumeScaler.cpp
)
target_include_directories(${PROJECT_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS})