M module-audio/Audio/AudioDevice.hpp => module-audio/Audio/AudioDevice.hpp +10 -16
@@ 54,7 54,7 @@ namespace audio
None
};
- using Format = struct
+ using Configuration = struct
{
uint32_t sampleRate_Hz = 0; /*!< Sample rate of audio data */
uint32_t bitWidth = 0; /*!< Data length of audio data, usually 8/16/24/32 bits */
@@ 65,22 65,16 @@ namespace audio
OutputPath outputPath = OutputPath::None;
};
- AudioDevice() = default;
- explicit AudioDevice(const audio::Endpoint::Capabilities &sourceCaps,
- const audio::Endpoint::Capabilities &sinkCaps)
- : IOProxy(sourceCaps, sinkCaps)
- {}
-
virtual ~AudioDevice() = default;
- virtual RetCode Start(const Format &format) = 0;
- virtual RetCode Stop() = 0;
+ virtual RetCode Start(const Configuration &format) = 0;
+ virtual RetCode Stop() = 0;
- virtual RetCode OutputVolumeCtrl(float vol) = 0;
- virtual RetCode InputGainCtrl(float gain) = 0;
- virtual RetCode OutputPathCtrl(OutputPath outputPath) = 0;
- virtual RetCode InputPathCtrl(InputPath inputPath) = 0;
- virtual bool IsFormatSupported(const Format &format) = 0;
+ virtual RetCode OutputVolumeCtrl(float vol) = 0;
+ virtual RetCode InputGainCtrl(float gain) = 0;
+ virtual RetCode OutputPathCtrl(OutputPath outputPath) = 0;
+ virtual RetCode InputPathCtrl(InputPath inputPath) = 0;
+ virtual bool IsFormatSupported(const Configuration &format) = 0;
float GetOutputVolume() const noexcept
{
@@ 102,13 96,13 @@ namespace audio
return currentFormat.inputPath;
}
- Format GetCurrentFormat() const noexcept
+ Configuration GetCurrentFormat() const noexcept
{
return currentFormat;
}
protected:
- Format currentFormat;
+ Configuration currentFormat;
bool isInitialized = false;
};
M module-audio/Audio/AudioFormat.cpp => module-audio/Audio/AudioFormat.cpp +17 -0
@@ 3,6 3,8 @@
#include "AudioFormat.hpp"
+#include <integer.hpp>
+
using audio::AudioFormat;
auto AudioFormat::getSampleRate() const noexcept -> unsigned int
@@ 87,3 89,18 @@ auto AudioFormat::isNull() const noexcept -> bool
{
return operator==(audio::nullFormat);
}
+
+auto AudioFormat::bytesToMicroseconds(unsigned int bytes) const noexcept -> std::chrono::microseconds
+{
+ auto bytesPerSecond = getBitrate() / utils::integer::BitsInByte;
+ auto duration = std::chrono::duration<double>(static_cast<double>(bytes) / bytesPerSecond);
+
+ return std::chrono::duration_cast<std::chrono::microseconds>(duration);
+}
+
+auto AudioFormat::microsecondsToBytes(std::chrono::microseconds duration) const noexcept -> unsigned int
+{
+ auto bytesPerSecond = getBitrate() / utils::integer::BitsInByte;
+ auto seconds = std::chrono::duration<double>(duration).count();
+ return static_cast<unsigned int>(seconds * bytesPerSecond);
+}
M module-audio/Audio/AudioFormat.hpp => module-audio/Audio/AudioFormat.hpp +4 -0
@@ 3,6 3,7 @@
#pragma once
+#include <chrono>
#include <set>
#include <string>
#include <vector>
@@ 30,6 31,9 @@ namespace audio
auto getBitrate() const noexcept -> unsigned long int;
auto toString() const -> std::string;
+ auto bytesToMicroseconds(unsigned int bytes) const noexcept -> std::chrono::microseconds;
+ auto microsecondsToBytes(std::chrono::microseconds duration) const noexcept -> unsigned int;
+
auto operator==(const AudioFormat &other) const -> bool;
auto operator!=(const AudioFormat &other) const -> bool;
auto operator>(const AudioFormat &other) const -> bool;
M module-audio/Audio/Endpoint.cpp => module-audio/Audio/Endpoint.cpp +10 -22
@@ 8,17 8,14 @@
#include <cassert> // assert
-using namespace audio;
-
-Endpoint::Endpoint(const Capabilities &caps) : _caps(caps)
-{}
-
-const Endpoint::Capabilities &Endpoint::getCapabilities() const noexcept
-{
- return _caps;
-}
-
-void Endpoint::connectStream(Stream &stream)
+using audio::AbstractStream;
+using audio::Endpoint;
+using audio::IOProxy;
+using audio::Sink;
+using audio::Source;
+using audio::StreamConnection;
+
+void Endpoint::connectStream(AbstractStream &stream)
{
assert(_stream == nullptr);
_stream = &stream;
@@ 41,16 38,7 @@ auto Endpoint::isFormatSupported(const AudioFormat &format) -> bool
return std::find(std::begin(formats), std::end(formats), format) != std::end(formats);
}
-Sink::Sink(const Capabilities &caps) : Endpoint(caps)
-{}
-
-Source::Source(const Capabilities &caps) : Endpoint(caps)
-{}
-
-IOProxy::IOProxy(const Capabilities &sourceCaps, const Capabilities &sinkCaps) : Source(sourceCaps), Sink(sinkCaps)
-{}
-
-StreamConnection::StreamConnection(Source *source, Sink *sink, Stream *stream)
+StreamConnection::StreamConnection(Source *source, Sink *sink, AbstractStream *stream)
: _sink(sink), _source(source), _stream(stream)
{
assert(_sink != nullptr);
@@ 114,7 102,7 @@ Sink *StreamConnection::getSink() const noexcept
return _sink;
}
-Stream *StreamConnection::getStream() const noexcept
+AbstractStream *StreamConnection::getStream() const noexcept
{
return _stream;
}
M module-audio/Audio/Endpoint.hpp => module-audio/Audio/Endpoint.hpp +20 -28
@@ 3,9 3,11 @@
#pragma once
-#include "Stream.hpp"
+#include "AbstractStream.hpp"
#include "AudioFormat.hpp"
+#include <chrono>
+#include <optional>
#include <vector>
#include <cstdint>
@@ 15,36 17,33 @@ namespace audio
class Endpoint
{
public:
- struct Capabilities
+ struct Traits
{
- bool usesDMA = false;
- std::size_t minBlockSize = 1;
- std::size_t maxBlockSize = SIZE_MAX;
+ bool usesDMA = false;
+
+ // optional constraints
+ std::optional<std::size_t> blockSizeConstraint = std::nullopt;
+ std::optional<std::chrono::milliseconds> timeConstraint = std::nullopt;
};
Endpoint() = default;
- Endpoint(const Capabilities &caps);
- void connectStream(Stream &stream);
+ void connectStream(AbstractStream &stream);
void disconnectStream();
bool isConnected() const noexcept;
- [[nodiscard]] const Capabilities &getCapabilities() const noexcept;
+ [[nodiscard]] virtual auto getTraits() const -> Traits = 0;
auto isFormatSupported(const AudioFormat &format) -> bool;
virtual auto getSupportedFormats() -> const std::vector<AudioFormat> & = 0;
protected:
- Capabilities _caps;
- Stream *_stream = nullptr;
+ AbstractStream *_stream = nullptr;
};
class Sink : public Endpoint
{
public:
- Sink() = default;
- explicit Sink(const Capabilities &caps);
-
virtual void onDataSend() = 0;
virtual void enableOutput() = 0;
virtual void disableOutput() = 0;
@@ 53,9 52,6 @@ namespace audio
class Source : public Endpoint
{
public:
- Source() = default;
- explicit Source(const Capabilities &caps);
-
virtual auto getSourceFormat() -> AudioFormat;
virtual void onDataReceive() = 0;
virtual void enableInput() = 0;
@@ 65,9 61,6 @@ namespace audio
class IOProxy : public Source, public Sink
{
public:
- IOProxy() = default;
- IOProxy(const Capabilities &sourceCaps, const Capabilities &sinkCaps);
-
inline bool isSinkConnected() const noexcept
{
return Sink::isConnected();
@@ 78,12 71,12 @@ namespace audio
return Source::isConnected();
}
- inline void connectOutputStream(Stream &stream)
+ inline void connectOutputStream(AbstractStream &stream)
{
Sink::connectStream(stream);
}
- inline void connectInputStream(Stream &stream)
+ inline void connectInputStream(AbstractStream &stream)
{
Source::connectStream(stream);
}
@@ 103,7 96,7 @@ namespace audio
{
public:
StreamConnection() = default;
- StreamConnection(Source *source, Sink *sink, Stream *stream);
+ StreamConnection(Source *source, Sink *sink, AbstractStream *stream);
~StreamConnection();
void enable();
@@ 112,14 105,13 @@ namespace audio
[[nodiscard]] Source *getSource() const noexcept;
[[nodiscard]] Sink *getSink() const noexcept;
- [[nodiscard]] Stream *getStream() const noexcept;
-
+ [[nodiscard]] AbstractStream *getStream() const noexcept;
[[nodiscard]] bool isEnabled() const noexcept;
private:
- bool enabled = false;
- Sink *_sink = nullptr;
- Source *_source = nullptr;
- Stream *_stream = nullptr;
+ bool enabled = false;
+ Sink *_sink = nullptr;
+ Source *_source = nullptr;
+ AbstractStream *_stream = nullptr;
};
}; // namespace audio
M module-audio/Audio/Operation/PlaybackOperation.cpp => module-audio/Audio/Operation/PlaybackOperation.cpp +9 -3
@@ 53,8 53,14 @@ namespace audio
}
// create stream
- StreamFactory streamFactory(playbackCapabilities, playbackBufferingSize);
- dataStreamOut = streamFactory.makeStream(*dec.get(), *audioDevice.get());
+ StreamFactory streamFactory(playbackTimeConstraint);
+ try {
+ dataStreamOut = streamFactory.makeStream(*dec, *audioDevice, currentProfile->getAudioFormat());
+ }
+ catch (std::invalid_argument &e) {
+ LOG_FATAL("Cannot create audio stream: %s", e.what());
+ return audio::RetCode::Failed;
+ }
// create audio connection
outputConnection = std::make_unique<StreamConnection>(dec.get(), audioDevice.get(), dataStreamOut.get());
@@ 63,7 69,7 @@ namespace audio
dec->startDecodingWorker(endOfFileCallback);
// start output device and enable audio connection
- auto ret = audioDevice->Start(currentProfile->GetAudioFormat());
+ auto ret = audioDevice->Start(currentProfile->GetAudioConfiguration());
outputConnection->enable();
// update state and token
M module-audio/Audio/Operation/PlaybackOperation.hpp => module-audio/Audio/Operation/PlaybackOperation.hpp +4 -6
@@ 10,6 10,9 @@
#include "Audio/StreamQueuedEventsListener.hpp"
#include "Audio/decoder/Decoder.hpp"
+#include <chrono>
+using namespace std::chrono_literals;
+
namespace audio::playbackDefaults
{
constexpr audio::Volume defaultLoudspeakerVolume = 10;
@@ 39,12 42,7 @@ namespace audio
Position GetPosition() final;
private:
- // these values are tightly connected to the Bluetooth A2DP update interval
- static constexpr auto playbackBufferingSize = 8U;
- static constexpr auto minimumBlockSize = 512U;
- static constexpr auto maximumBlockSize = 512U;
- static constexpr Endpoint::Capabilities playbackCapabilities{.minBlockSize = minimumBlockSize,
- .maxBlockSize = maximumBlockSize};
+ static constexpr auto playbackTimeConstraint = 10ms;
std::unique_ptr<Stream> dataStreamOut;
std::unique_ptr<Decoder> dec;
M module-audio/Audio/Operation/RecorderOperation.cpp => module-audio/Audio/Operation/RecorderOperation.cpp +3 -3
@@ 85,8 85,8 @@ namespace audio
operationToken = token;
state = State::Active;
- if (audioDevice->IsFormatSupported(currentProfile->GetAudioFormat())) {
- auto ret = audioDevice->Start(currentProfile->GetAudioFormat());
+ if (audioDevice->IsFormatSupported(currentProfile->GetAudioConfiguration())) {
+ auto ret = audioDevice->Start(currentProfile->GetAudioConfiguration());
return GetDeviceError(ret);
}
else {
@@ 124,7 124,7 @@ namespace audio
}
state = State::Active;
- auto ret = audioDevice->Start(currentProfile->GetAudioFormat());
+ auto ret = audioDevice->Start(currentProfile->GetAudioConfiguration());
return GetDeviceError(ret);
}
M module-audio/Audio/Operation/RouterOperation.cpp => module-audio/Audio/Operation/RouterOperation.cpp +16 -7
@@ 51,28 51,37 @@ namespace audio
state = State::Active;
// check if audio devices support desired audio format
- if (!audioDevice->IsFormatSupported(currentProfile->GetAudioFormat())) {
+ if (!audioDevice->IsFormatSupported(currentProfile->GetAudioConfiguration())) {
return RetCode::InvalidFormat;
}
- if (!audioDeviceCellular->IsFormatSupported(currentProfile->GetAudioFormat())) {
+ if (!audioDeviceCellular->IsFormatSupported(currentProfile->GetAudioConfiguration())) {
return RetCode::InvalidFormat;
}
// try to run devices with the format
- if (auto ret = audioDevice->Start(currentProfile->GetAudioFormat()); ret != AudioDevice::RetCode::Success) {
+ if (auto ret = audioDevice->Start(currentProfile->GetAudioConfiguration());
+ ret != AudioDevice::RetCode::Success) {
return GetDeviceError(ret);
}
- if (auto ret = audioDeviceCellular->Start(currentProfile->GetAudioFormat());
+ if (auto ret = audioDeviceCellular->Start(currentProfile->GetAudioConfiguration());
ret != AudioDevice::RetCode::Success) {
return GetDeviceError(ret);
}
// create streams
- StreamFactory streamFactory(routerCapabilities);
- dataStreamIn = streamFactory.makeStream(*audioDevice.get(), *audioDeviceCellular.get());
- dataStreamOut = streamFactory.makeStream(*audioDevice.get(), *audioDeviceCellular.get());
+ StreamFactory streamFactory(callTimeConstraint);
+ try {
+ dataStreamIn =
+ streamFactory.makeStream(*audioDevice, *audioDeviceCellular, currentProfile->getAudioFormat());
+ dataStreamOut =
+ streamFactory.makeStream(*audioDevice, *audioDeviceCellular, currentProfile->getAudioFormat());
+ }
+ catch (std::invalid_argument &e) {
+ LOG_FATAL("Cannot create audio stream: %s", e.what());
+ return audio::RetCode::Failed;
+ }
// create audio connections
inputConnection =
M module-audio/Audio/Operation/RouterOperation.hpp => module-audio/Audio/Operation/RouterOperation.hpp +7 -6
@@ 14,13 14,16 @@
#include <mutex.hpp>
-#include <memory>
+#include <chrono>
#include <functional>
+#include <memory>
#include <string>
#include <vector>
#include <cstdint>
+using namespace std::chrono_literals;
+
namespace audio
{
class RouterOperation : public Operation
@@ 56,11 59,9 @@ namespace audio
Position GetPosition() final;
private:
- static constexpr auto minimumBlockSize = 64U;
- static constexpr auto maximumBlockSize = 64U;
- static constexpr Endpoint::Capabilities routerCapabilities{.minBlockSize = minimumBlockSize,
- .maxBlockSize = maximumBlockSize};
- Mute mute = Mute::Disabled;
+ static constexpr auto callTimeConstraint = 2ms;
+
+ Mute mute = Mute::Disabled;
JackState jackState = JackState::Unplugged;
void Mute();
M module-audio/Audio/Profiles/Profile.cpp => module-audio/Audio/Profiles/Profile.cpp +8 -8
@@ 79,39 79,39 @@ namespace audio
Profile::Profile(const std::string &name,
const Type type,
- const AudioDevice::Format &fmt,
+ const AudioDevice::Configuration &fmt,
AudioDevice::Type devType)
- : audioFormat(fmt), audioDeviceType(devType), name(name), type(type)
+ : audioConfiguration(fmt), audioDeviceType(devType), name(name), type(type)
{}
void Profile::SetInputGain(Gain gain)
{
- audioFormat.inputGain = gain;
+ audioConfiguration.inputGain = gain;
}
void Profile::SetOutputVolume(Volume vol)
{
- audioFormat.outputVolume = vol;
+ audioConfiguration.outputVolume = vol;
}
void Profile::SetInputPath(AudioDevice::InputPath path)
{
- audioFormat.inputPath = path;
+ audioConfiguration.inputPath = path;
}
void Profile::SetOutputPath(AudioDevice::OutputPath path)
{
- audioFormat.outputPath = path;
+ audioConfiguration.outputPath = path;
}
void Profile::SetInOutFlags(uint32_t flags)
{
- audioFormat.flags = flags;
+ audioConfiguration.flags = flags;
}
void Profile::SetSampleRate(uint32_t samplerate)
{
- audioFormat.sampleRate_Hz = samplerate;
+ audioConfiguration.sampleRate_Hz = samplerate;
}
const std::string str(const Profile::Type &profileType)
M module-audio/Audio/Profiles/Profile.hpp => module-audio/Audio/Profiles/Profile.hpp +21 -10
@@ 60,32 60,32 @@ namespace audio
Volume GetOutputVolume() const
{
- return audioFormat.outputVolume;
+ return audioConfiguration.outputVolume;
}
Gain GetInputGain() const
{
- return audioFormat.inputGain;
+ return audioConfiguration.inputGain;
}
uint32_t GetSampleRate()
{
- return audioFormat.sampleRate_Hz;
+ return audioConfiguration.sampleRate_Hz;
}
uint32_t GetInOutFlags()
{
- return audioFormat.flags;
+ return audioConfiguration.flags;
}
AudioDevice::OutputPath GetOutputPath() const
{
- return audioFormat.outputPath;
+ return audioConfiguration.outputPath;
}
AudioDevice::InputPath GetInputPath() const
{
- return audioFormat.inputPath;
+ return audioConfiguration.inputPath;
}
AudioDevice::Type GetAudioDeviceType() const
@@ 93,9 93,17 @@ namespace audio
return audioDeviceType;
}
- AudioDevice::Format GetAudioFormat()
+ [[deprecated]] AudioDevice::Configuration GetAudioConfiguration()
{
- return audioFormat;
+ return audioConfiguration;
+ }
+
+ auto getAudioFormat() const noexcept
+ {
+ auto isStereo = (audioConfiguration.flags & static_cast<uint32_t>(AudioDevice::Flags::OutputStereo)) != 0 ||
+ (audioConfiguration.flags & static_cast<uint32_t>(AudioDevice::Flags::InputStereo)) != 0;
+ auto channels = isStereo ? 2U : 1U;
+ return AudioFormat(audioConfiguration.sampleRate_Hz, audioConfiguration.bitWidth, channels);
}
const std::string &GetName() const
@@ 109,9 117,12 @@ namespace audio
}
protected:
- Profile(const std::string &name, const Type type, const AudioDevice::Format &fmt, AudioDevice::Type devType);
+ Profile(const std::string &name,
+ const Type type,
+ const AudioDevice::Configuration &fmt,
+ AudioDevice::Type devType);
- AudioDevice::Format audioFormat{};
+ AudioDevice::Configuration audioConfiguration{};
AudioDevice::Type audioDeviceType = AudioDevice::Type::Audiocodec;
std::string name;
M module-audio/Audio/Profiles/ProfileIdle.hpp => module-audio/Audio/Profiles/ProfileIdle.hpp +1 -1
@@ 11,7 11,7 @@ namespace audio
class ProfileIdle : public Profile
{
public:
- ProfileIdle() : Profile("Idle", Type::Idle, AudioDevice::Format{}, AudioDevice::Type::None)
+ ProfileIdle() : Profile("Idle", Type::Idle, AudioDevice::Configuration{}, AudioDevice::Type::None)
{}
};
M module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp => module-audio/Audio/Profiles/ProfilePlaybackBluetoothA2DP.hpp +7 -7
@@ 14,13 14,13 @@ namespace audio
ProfilePlaybackBluetoothA2DP(Volume volume)
: Profile("Playback Bluetooth A2DP",
Type::PlaybackBluetoothA2DP,
- AudioDevice::Format{.sampleRate_Hz = 44100,
- .bitWidth = 16,
- .flags = 0,
- .outputVolume = static_cast<float>(volume),
- .inputGain = 0,
- .inputPath = AudioDevice::InputPath::None,
- .outputPath = AudioDevice::OutputPath::None},
+ AudioDevice::Configuration{.sampleRate_Hz = 44100,
+ .bitWidth = 16,
+ .flags = 0,
+ .outputVolume = static_cast<float>(volume),
+ .inputGain = 0,
+ .inputPath = AudioDevice::InputPath::None,
+ .outputPath = AudioDevice::OutputPath::None},
AudioDevice::Type::Bluetooth)
{}
};
M module-audio/Audio/Profiles/ProfilePlaybackHeadphones.hpp => module-audio/Audio/Profiles/ProfilePlaybackHeadphones.hpp +7 -7
@@ 13,13 13,13 @@ namespace audio
ProfilePlaybackHeadphones(Volume volume)
: Profile("Playback Headphones",
Type::PlaybackHeadphones,
- AudioDevice::Format{.sampleRate_Hz = 0,
- .bitWidth = 16,
- .flags = 0,
- .outputVolume = static_cast<float>(volume),
- .inputGain = 0,
- .inputPath = AudioDevice::InputPath::None,
- .outputPath = AudioDevice::OutputPath::Headphones},
+ AudioDevice::Configuration{.sampleRate_Hz = 0,
+ .bitWidth = 16,
+ .flags = 0,
+ .outputVolume = static_cast<float>(volume),
+ .inputGain = 0,
+ .inputPath = AudioDevice::InputPath::None,
+ .outputPath = AudioDevice::OutputPath::Headphones},
AudioDevice::Type::Audiocodec)
{}
};
M module-audio/Audio/Profiles/ProfilePlaybackLoudspeaker.hpp => module-audio/Audio/Profiles/ProfilePlaybackLoudspeaker.hpp +7 -7
@@ 14,13 14,13 @@ namespace audio
ProfilePlaybackLoudspeaker(Volume volume)
: Profile("Playback Loudspeaker",
Type::PlaybackLoudspeaker,
- AudioDevice::Format{.sampleRate_Hz = 0,
- .bitWidth = 16,
- .flags = 0,
- .outputVolume = static_cast<float>(volume),
- .inputGain = 0,
- .inputPath = AudioDevice::InputPath::None,
- .outputPath = AudioDevice::OutputPath::Loudspeaker},
+ AudioDevice::Configuration{.sampleRate_Hz = 0,
+ .bitWidth = 16,
+ .flags = 0,
+ .outputVolume = static_cast<float>(volume),
+ .inputGain = 0,
+ .inputPath = AudioDevice::InputPath::None,
+ .outputPath = AudioDevice::OutputPath::Loudspeaker},
AudioDevice::Type::Audiocodec)
{}
};
M module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRecordingBluetoothHSP.hpp +9 -8
@@ 14,14 14,15 @@ namespace audio
ProfileRecordingBluetoothHSP(Gain gain)
: Profile("Recording Bluetooth HSP",
Type::RecordingHeadphones,
- AudioDevice::Format{.sampleRate_Hz = 8000,
- .bitWidth = 16,
- .flags = static_cast<uint32_t>(
- AudioDevice::Flags::InputLeft), // microphone use left audio channel
- .outputVolume = 0,
- .inputGain = static_cast<float>(gain),
- .inputPath = AudioDevice::InputPath::None,
- .outputPath = AudioDevice::OutputPath::None},
+ AudioDevice::Configuration{
+ .sampleRate_Hz = 8000,
+ .bitWidth = 16,
+ .flags =
+ static_cast<uint32_t>(AudioDevice::Flags::InputLeft), // microphone use left audio channel
+ .outputVolume = 0,
+ .inputGain = static_cast<float>(gain),
+ .inputPath = AudioDevice::InputPath::None,
+ .outputPath = AudioDevice::OutputPath::None},
AudioDevice::Type::Bluetooth)
{}
};
M module-audio/Audio/Profiles/ProfileRecordingHeadphones.hpp => module-audio/Audio/Profiles/ProfileRecordingHeadphones.hpp +9 -8
@@ 13,14 13,15 @@ namespace audio
ProfileRecordingHeadphones(Gain gain)
: Profile("Recording Headset",
Type::RecordingHeadphones,
- AudioDevice::Format{.sampleRate_Hz = 44100,
- .bitWidth = 16,
- .flags = static_cast<uint32_t>(
- AudioDevice::Flags::InputLeft), // microphone use left audio channel
- .outputVolume = 0,
- .inputGain = static_cast<float>(gain),
- .inputPath = AudioDevice::InputPath::Headphones,
- .outputPath = AudioDevice::OutputPath::None},
+ AudioDevice::Configuration{
+ .sampleRate_Hz = 44100,
+ .bitWidth = 16,
+ .flags =
+ static_cast<uint32_t>(AudioDevice::Flags::InputLeft), // microphone use left audio channel
+ .outputVolume = 0,
+ .inputGain = static_cast<float>(gain),
+ .inputPath = AudioDevice::InputPath::Headphones,
+ .outputPath = AudioDevice::OutputPath::None},
AudioDevice::Type::Audiocodec)
{}
};
M module-audio/Audio/Profiles/ProfileRecordingOnBoardMic.hpp => module-audio/Audio/Profiles/ProfileRecordingOnBoardMic.hpp +9 -8
@@ 13,14 13,15 @@ namespace audio
ProfileRecordingOnBoardMic(Gain gain)
: Profile("Recording On Board Microphone",
Type::RecordingBuiltInMic,
- AudioDevice::Format{.sampleRate_Hz = 44100,
- .bitWidth = 16,
- .flags = static_cast<uint32_t>(
- AudioDevice::Flags::InputLeft), // microphone use left audio channel
- .outputVolume = 0,
- .inputGain = static_cast<float>(gain),
- .inputPath = AudioDevice::InputPath::Microphone,
- .outputPath = AudioDevice::OutputPath::None},
+ AudioDevice::Configuration{
+ .sampleRate_Hz = 44100,
+ .bitWidth = 16,
+ .flags =
+ static_cast<uint32_t>(AudioDevice::Flags::InputLeft), // microphone use left audio channel
+ .outputVolume = 0,
+ .inputGain = static_cast<float>(gain),
+ .inputPath = AudioDevice::InputPath::Microphone,
+ .outputPath = AudioDevice::OutputPath::None},
AudioDevice::Type::Audiocodec)
{}
};
M module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp => module-audio/Audio/Profiles/ProfileRoutingBluetoothHSP.hpp +13 -13
@@ 11,19 11,19 @@ namespace audio
{
public:
ProfileRoutingBluetoothHSP(Volume volume, Gain gain)
- : Profile(
- "Routing Bluetooth HSP",
- Type::RoutingBluetoothHSP,
- AudioDevice::Format{.sampleRate_Hz = 8000,
- .bitWidth = 16,
- .flags = static_cast<uint32_t>(
- AudioDevice::Flags::InputLeft) | // microphone use left audio channel
- static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
- .outputVolume = static_cast<float>(volume),
- .inputGain = static_cast<float>(gain),
- .inputPath = AudioDevice::InputPath::None,
- .outputPath = AudioDevice::OutputPath::None},
- AudioDevice::Type::Bluetooth)
+ : Profile("Routing Bluetooth HSP",
+ Type::RoutingBluetoothHSP,
+ AudioDevice::Configuration{
+ .sampleRate_Hz = 8000,
+ .bitWidth = 16,
+ .flags = static_cast<uint32_t>(
+ AudioDevice::Flags::InputLeft) | // microphone use left audio channel
+ static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
+ .outputVolume = static_cast<float>(volume),
+ .inputGain = static_cast<float>(gain),
+ .inputPath = AudioDevice::InputPath::None,
+ .outputPath = AudioDevice::OutputPath::None},
+ AudioDevice::Type::Bluetooth)
{}
};
M module-audio/Audio/Profiles/ProfileRoutingEarspeaker.hpp => module-audio/Audio/Profiles/ProfileRoutingEarspeaker.hpp +13 -13
@@ 11,19 11,19 @@ namespace audio
{
public:
ProfileRoutingEarspeaker(Volume volume, Gain gain)
- : Profile(
- "Routing Earspeaker",
- Type::RoutingEarspeaker,
- AudioDevice::Format{.sampleRate_Hz = 16000,
- .bitWidth = 16,
- .flags = static_cast<uint32_t>(
- AudioDevice::Flags::InputLeft) | // microphone use left audio channel
- static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
- .outputVolume = static_cast<float>(volume),
- .inputGain = static_cast<float>(gain),
- .inputPath = AudioDevice::InputPath::Microphone,
- .outputPath = AudioDevice::OutputPath::Earspeaker},
- AudioDevice::Type::Audiocodec)
+ : Profile("Routing Earspeaker",
+ Type::RoutingEarspeaker,
+ AudioDevice::Configuration{
+ .sampleRate_Hz = 16000,
+ .bitWidth = 16,
+ .flags = static_cast<uint32_t>(
+ AudioDevice::Flags::InputLeft) | // microphone use left audio channel
+ static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
+ .outputVolume = static_cast<float>(volume),
+ .inputGain = static_cast<float>(gain),
+ .inputPath = AudioDevice::InputPath::Microphone,
+ .outputPath = AudioDevice::OutputPath::Earspeaker},
+ AudioDevice::Type::Audiocodec)
{}
};
M module-audio/Audio/Profiles/ProfileRoutingHeadphones.hpp => module-audio/Audio/Profiles/ProfileRoutingHeadphones.hpp +13 -13
@@ 11,19 11,19 @@ namespace audio
{
public:
ProfileRoutingHeadphones(Volume volume, Gain gain)
- : Profile(
- "Routing Headset",
- Type::RoutingHeadphones,
- AudioDevice::Format{.sampleRate_Hz = 16000,
- .bitWidth = 16,
- .flags = static_cast<uint32_t>(
- AudioDevice::Flags::InputLeft) | // microphone use left audio channel
- static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
- .outputVolume = static_cast<float>(volume),
- .inputGain = static_cast<float>(gain),
- .inputPath = AudioDevice::InputPath::Headphones,
- .outputPath = AudioDevice::OutputPath::Headphones},
- AudioDevice::Type::Audiocodec)
+ : Profile("Routing Headset",
+ Type::RoutingHeadphones,
+ AudioDevice::Configuration{
+ .sampleRate_Hz = 16000,
+ .bitWidth = 16,
+ .flags = static_cast<uint32_t>(
+ AudioDevice::Flags::InputLeft) | // microphone use left audio channel
+ static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
+ .outputVolume = static_cast<float>(volume),
+ .inputGain = static_cast<float>(gain),
+ .inputPath = AudioDevice::InputPath::Headphones,
+ .outputPath = AudioDevice::OutputPath::Headphones},
+ AudioDevice::Type::Audiocodec)
{}
};
M module-audio/Audio/Profiles/ProfileRoutingLoudspeaker.hpp => module-audio/Audio/Profiles/ProfileRoutingLoudspeaker.hpp +13 -13
@@ 11,19 11,19 @@ namespace audio
{
public:
ProfileRoutingLoudspeaker(Volume volume, Gain gain)
- : Profile(
- "Routing Speakerphone",
- Type::RoutingLoudspeaker,
- AudioDevice::Format{.sampleRate_Hz = 16000,
- .bitWidth = 16,
- .flags = static_cast<uint32_t>(
- AudioDevice::Flags::InputLeft) | // microphone use left audio channel
- static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
- .outputVolume = static_cast<float>(volume),
- .inputGain = static_cast<float>(gain),
- .inputPath = AudioDevice::InputPath::Microphone,
- .outputPath = AudioDevice::OutputPath::Loudspeaker},
- AudioDevice::Type::Audiocodec)
+ : Profile("Routing Speakerphone",
+ Type::RoutingLoudspeaker,
+ AudioDevice::Configuration{
+ .sampleRate_Hz = 16000,
+ .bitWidth = 16,
+ .flags = static_cast<uint32_t>(
+ AudioDevice::Flags::InputLeft) | // microphone use left audio channel
+ static_cast<uint32_t>(AudioDevice::Flags::OutputMono),
+ .outputVolume = static_cast<float>(volume),
+ .inputGain = static_cast<float>(gain),
+ .inputPath = AudioDevice::InputPath::Microphone,
+ .outputPath = AudioDevice::OutputPath::Loudspeaker},
+ AudioDevice::Type::Audiocodec)
{}
};
M module-audio/Audio/StreamFactory.cpp => module-audio/Audio/StreamFactory.cpp +75 -22
@@ 7,52 7,105 @@
#include <math/Math.hpp>
#include <algorithm>
+#include <chrono>
+#include <stdexcept>
#include <memory>
-#include <vector>
+#include <optional>
#include <cassert>
+#include <cmath>
-using namespace audio;
+using audio::Sink;
+using audio::Source;
+using audio::Stream;
+using audio::StreamFactory;
+using namespace std::chrono_literals;
-StreamFactory::StreamFactory(Endpoint::Capabilities factoryCaps, unsigned int bufferingSize)
- : caps(std::move(factoryCaps)), bufferingSize(bufferingSize)
+StreamFactory::StreamFactory(std::chrono::milliseconds operationPeriodRequirement)
+ : periodRequirement(operationPeriodRequirement)
{}
-auto StreamFactory::makeStream(Source &source, Sink &sink) -> std::unique_ptr<Stream>
+auto StreamFactory::makeStream(Source &source, Sink &sink, AudioFormat streamFormat) -> std::unique_ptr<Stream>
{
- auto negotiatedCaps = negotiateCaps({source, sink});
- auto format = source.getSourceFormat();
+ auto streamBuffering = defaultBuffering;
+ auto endpointsTraits = {source.getTraits(), sink.getTraits()};
+ 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});
- return std::make_unique<Stream>(
- format, getAllocator(negotiatedCaps.usesDMA), negotiatedCaps.maxBlockSize, bufferingSize);
+ if (streamFormat == audio::nullFormat) {
+ throw std::invalid_argument("No source format provided");
+ }
+
+ if (!blockSizeConstraint.has_value()) {
+ blockSizeConstraint = binary::ceilPowerOfTwo(streamFormat.microsecondsToBytes(timingConstraint));
+ }
+
+ auto blockTransferDuration =
+ std::chrono::duration<double, std::milli>(streamFormat.bytesToMicroseconds(blockSizeConstraint.value()));
+ streamBuffering = static_cast<unsigned int>(std::ceil(timingConstraint / blockTransferDuration)) * defaultBuffering;
+
+ LOG_DEBUG("Creating audio stream: block size = %lu; buffering = %u",
+ static_cast<unsigned long>(blockSizeConstraint.value()),
+ streamBuffering);
+
+ return std::make_unique<Stream>(streamFormat, streamAllocator, blockSizeConstraint.value(), streamBuffering);
}
-auto StreamFactory::negotiateCaps(std::vector<std::reference_wrapper<const Endpoint>> v) -> Endpoint::Capabilities
+auto StreamFactory::getBlockSizeConstraint(std::initializer_list<audio::Endpoint::Traits> traitsList) const
+ -> std::optional<std::size_t>
{
- auto negotiatedCaps = caps;
+ std::optional<std::size_t> blockSize = std::nullopt;
- for (const auto &endpointRef : v) {
- auto &endpointCaps = endpointRef.get().getCapabilities();
+ for (const auto &traits : traitsList) {
+ if (!traits.blockSizeConstraint.has_value()) {
+ continue;
+ }
- negotiatedCaps.maxBlockSize = std::min(negotiatedCaps.maxBlockSize, endpointCaps.maxBlockSize);
- negotiatedCaps.minBlockSize = std::max(negotiatedCaps.minBlockSize, endpointCaps.minBlockSize);
- negotiatedCaps.usesDMA = negotiatedCaps.usesDMA || endpointCaps.usesDMA;
+ auto blockSizeCandidate = traits.blockSizeConstraint.value();
+ if (blockSizeCandidate == 0) {
+ throw std::invalid_argument("Invalid stream block size");
+ }
+
+ if (!blockSize.has_value()) {
+ blockSize = blockSizeCandidate;
+ }
+ else if (blockSize != blockSizeCandidate) {
+ throw std::invalid_argument("Block size mismatch");
+ }
}
- negotiatedCaps.minBlockSize = binary::ceilPowerOfTwo(negotiatedCaps.minBlockSize);
- negotiatedCaps.maxBlockSize = binary::floorPowerOfTwo(negotiatedCaps.maxBlockSize);
+ return blockSize;
+}
- assert(negotiatedCaps.minBlockSize <= negotiatedCaps.maxBlockSize);
+auto StreamFactory::getTimingConstraints(
+ std::initializer_list<std::optional<std::chrono::milliseconds>> timingConstraints) const
+ -> std::chrono::milliseconds
+{
+ auto constraint = std::max(timingConstraints, [](const auto &left, const auto &right) {
+ return right.value_or(std::chrono::milliseconds(0)).count() >
+ left.value_or(std::chrono::milliseconds(0)).count();
+ });
+
+ if (!constraint.has_value()) {
+ throw std::invalid_argument("At least one timing constraint must be provided");
+ }
- return negotiatedCaps;
+ return constraint.value();
}
-auto StreamFactory::getAllocator(bool usesDMA) -> Stream::Allocator &
+auto StreamFactory::negotiateAllocator(std::initializer_list<audio::Endpoint::Traits> traitsList) noexcept
+ -> Stream::Allocator &
{
- if (usesDMA) {
+ auto mustUseDMA =
+ std::any_of(std::begin(traitsList), std::end(traitsList), [](const auto &trait) { return trait.usesDMA; });
+ if (mustUseDMA) {
+ LOG_DEBUG("Using fast memory allocator for audio streaming");
return nonCacheableAlloc;
}
else {
+ LOG_DEBUG("Using standard allocator for audio streaming");
return stdAlloc;
}
}
M module-audio/Audio/StreamFactory.hpp => module-audio/Audio/StreamFactory.hpp +15 -8
@@ 6,8 6,9 @@
#include "Endpoint.hpp"
#include "Stream.hpp"
+#include <initializer_list>
+#include <optional>
#include <memory>
-#include <vector>
namespace audio
{
@@ 15,18 16,24 @@ namespace audio
class StreamFactory
{
public:
- explicit StreamFactory(Endpoint::Capabilities factoryCaps,
- unsigned int bufferingSize = Stream::defaultBufferingSize);
- auto makeStream(Source &source, Sink &sink) -> std::unique_ptr<Stream>;
+ StreamFactory() = default;
+ explicit StreamFactory(std::chrono::milliseconds operationPeriodRequirement);
+ auto makeStream(Source &source, Sink &sink, AudioFormat streamFormat) -> std::unique_ptr<Stream>;
private:
- auto negotiateCaps(std::vector<std::reference_wrapper<const Endpoint>> v) -> Endpoint::Capabilities;
- auto getAllocator(bool usesDMA) -> Stream::Allocator &;
+ static constexpr auto defaultBuffering = 2U;
+
+ auto getBlockSizeConstraint(std::initializer_list<audio::Endpoint::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 &;
+
+ std::optional<std::chrono::milliseconds> periodRequirement = std::nullopt;
- Endpoint::Capabilities caps;
StandardStreamAllocator stdAlloc;
NonCacheableStreamAllocator nonCacheableAlloc;
- unsigned int bufferingSize;
};
} // namespace audio
M module-audio/Audio/decoder/Decoder.cpp => module-audio/Audio/decoder/Decoder.cpp +6 -3
@@ 14,10 14,8 @@
namespace audio
{
-
Decoder::Decoder(const char *fileName)
- : Source(Endpoint::Capabilities{.maxBlockSize = workerBufferSize * sizeof(int16_t)}), filePath(fileName),
- workerBuffer(std::make_unique<int16_t[]>(workerBufferSize)), tag(std::make_unique<Tags>())
+ : filePath(fileName), workerBuffer(std::make_unique<int16_t[]>(workerBufferSize)), tag(std::make_unique<Tags>())
{
fd = std::fopen(fileName, "r");
@@ 183,4 181,9 @@ namespace audio
return formats;
}
+ auto Decoder::getTraits() const -> Endpoint::Traits
+ {
+ return Endpoint::Traits{};
+ }
+
} // namespace audio
M module-audio/Audio/decoder/Decoder.hpp => module-audio/Audio/decoder/Decoder.hpp +4 -2
@@ 110,6 110,8 @@ namespace audio
auto getSourceFormat() -> AudioFormat override;
auto getSupportedFormats() -> const std::vector<AudioFormat> & override;
+ auto getTraits() const -> Endpoint::Traits override;
+
void startDecodingWorker(DecoderWorker::EndOfFileCallback endOfFileCallback);
void stopDecodingWorker();
@@ 122,8 124,8 @@ namespace audio
void convertmono2stereo(int16_t *pcm, uint32_t samplecount);
- static constexpr auto workerBufferSize = 1024 * 8;
- static constexpr Endpoint::Capabilities decoderCaps = {.usesDMA = false};
+ static constexpr auto workerBufferSize = 1024 * 8;
+ static constexpr Endpoint::Traits decoderCaps = {.usesDMA = false};
uint32_t sampleRate = 0;
uint32_t chanNumber = 0;
A module-audio/Audio/test/MockEndpoint.hpp => module-audio/Audio/test/MockEndpoint.hpp +33 -0
@@ 0,0 1,33 @@
+// 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/Endpoint.hpp>
+
+#include <gmock/gmock.h>
+
+namespace testing::audio
+{
+ class MockSink : public ::audio::Sink
+ {
+ public:
+ MOCK_METHOD(::audio::Endpoint::Traits, getTraits, (), (const, override));
+ MOCK_METHOD(const std::vector<::audio::AudioFormat> &, getSupportedFormats, (), (override));
+ MOCK_METHOD(void, onDataSend, (), (override));
+ MOCK_METHOD(void, enableOutput, (), (override));
+ MOCK_METHOD(void, disableOutput, (), (override));
+ };
+
+ class MockSource : public ::audio::Source
+ {
+ public:
+ MOCK_METHOD(::audio::Endpoint::Traits, getTraits, (), (const, override));
+ MOCK_METHOD(const std::vector<::audio::AudioFormat> &, getSupportedFormats, (), (override));
+ MOCK_METHOD(::audio::AudioFormat, getSourceFormat, (), (override));
+ MOCK_METHOD(void, onDataReceive, (), (override));
+ MOCK_METHOD(void, enableInput, (), (override));
+ MOCK_METHOD(void, disableInput, (), (override));
+ };
+
+} // namespace testing::audio
M module-audio/Audio/test/TestEndpoint.cpp => module-audio/Audio/test/TestEndpoint.cpp +10 -0
@@ 26,6 26,11 @@ void TestSink::enableOutput()
void TestSink::disableOutput()
{}
+auto TestSink::getTraits() const -> ::audio::Endpoint::Traits
+{
+ return testTraits;
+}
+
TestSource::TestSource(std::vector<AudioFormat> supportedFormats, AudioFormat sourceFormat)
: formats(std::move(supportedFormats)), sourceFormat(std::move(sourceFormat))
{}
@@ 52,3 57,8 @@ auto TestSource::getSupportedFormats() -> const std::vector<AudioFormat> &
{
return formats;
}
+
+auto TestSource::getTraits() const -> ::audio::Endpoint::Traits
+{
+ return testTraits;
+}
M module-audio/Audio/test/TestEndpoint.hpp => module-audio/Audio/test/TestEndpoint.hpp +4 -0
@@ 10,6 10,8 @@
namespace audio::test
{
+ constexpr inline auto testTraits = ::audio::Endpoint::Traits{};
+
class TestSink : public audio::Sink
{
public:
@@ 19,6 21,7 @@ namespace audio::test
void enableOutput() override;
void disableOutput() override;
auto getSupportedFormats() -> const std::vector<AudioFormat> & override;
+ auto getTraits() const -> ::audio::Endpoint::Traits override;
private:
std::vector<AudioFormat> formats;
@@ 35,6 38,7 @@ namespace audio::test
void enableInput() override;
void disableInput() override;
auto getSupportedFormats() -> const std::vector<AudioFormat> & override;
+ auto getTraits() const -> ::audio::Endpoint::Traits override;
private:
std::vector<AudioFormat> formats;
M module-audio/Audio/test/unittest_format.cpp => module-audio/Audio/test/unittest_format.cpp +16 -0
@@ 79,3 79,19 @@ TEST(AudioFormat, Null)
EXPECT_FALSE(AudioFormat(44100, 16, 0).isNull());
EXPECT_FALSE(AudioFormat(44100, 16, 2).isNull());
}
+
+TEST(AudioFormat, DurationToBytes)
+{
+ auto format = AudioFormat(44100, 16, 2);
+
+ EXPECT_EQ(format.microsecondsToBytes(std::chrono::microseconds(0)), 0);
+ EXPECT_EQ(format.microsecondsToBytes(std::chrono::seconds(1)), 176400);
+}
+
+TEST(AudioFormat, BytesToDuration)
+{
+ auto format = AudioFormat(8000, 8, 1);
+
+ EXPECT_EQ(format.bytesToMicroseconds(0).count(), 0);
+ EXPECT_EQ(format.bytesToMicroseconds(1).count(), 125);
+}
M module-audio/Audio/test/unittest_stream.cpp => module-audio/Audio/test/unittest_stream.cpp +112 -0
@@ 7,7 7,9 @@
#include <Audio/Stream.hpp>
#include <Audio/AudioFormat.hpp>
#include <Audio/StreamProxy.hpp>
+#include <Audio/StreamFactory.hpp>
+#include "MockEndpoint.hpp"
#include "MockStream.hpp"
#include <cstdint>
@@ 16,10 18,13 @@
using audio::NonCacheableStreamAllocator;
using audio::StandardStreamAllocator;
using audio::Stream;
+using audio::StreamFactory;
using testing::Return;
using testing::audio::MockStream;
using testing::audio::MockStreamEventListener;
+using namespace std::chrono_literals;
+
constexpr std::size_t defaultBlockSize = 64U;
constexpr std::size_t defaultBuffering = 4U;
constexpr audio::AudioFormat format = audio::AudioFormat(44100, 16, 2);
@@ 406,6 411,113 @@ TEST(Proxy, Misc)
EXPECT_FALSE(proxy.isFull());
}
+TEST(Factory, BlockSizeConstraints)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory(2ms);
+
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 512U}));
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+
+ auto stream = factory.makeStream(mockSource, mockSink, format);
+
+ EXPECT_EQ(stream->getOutputTraits().blockSize, 512U);
+ EXPECT_EQ(stream->getInputTraits().blockSize, 512U);
+
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 512U}));
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 512U}));
+
+ stream = factory.makeStream(mockSource, mockSink, format);
+
+ EXPECT_EQ(stream->getOutputTraits().blockSize, 512U);
+ EXPECT_EQ(stream->getInputTraits().blockSize, 512U);
+}
+
+TEST(Factory, BlockSizeErrors)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory(2ms);
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 128U}));
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 300U}));
+ EXPECT_THROW(factory.makeStream(mockSource, mockSink, format), std::invalid_argument);
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 128U}));
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 0}));
+ EXPECT_THROW(factory.makeStream(mockSource, mockSink, format), std::invalid_argument);
+}
+
+TEST(Factory, TimeContraintsError)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory;
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+
+ EXPECT_THROW(factory.makeStream(mockSource, mockSink, format), std::invalid_argument);
+}
+
+TEST(Factory, NoSourceFormat)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory;
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+ EXPECT_THROW(factory.makeStream(mockSource, mockSink, ::audio::nullFormat), std::invalid_argument);
+}
+
+TEST(Factory, TimeConstraints)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory(10ms);
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+ EXPECT_CALL(mockSink, getTraits)
+ .WillRepeatedly(Return(::audio::Endpoint::Traits{.blockSizeConstraint = 512U, .timeConstraint = 10ms}));
+
+ auto stream = factory.makeStream(mockSource, mockSink, ::audio::AudioFormat(44100, 16, 2));
+
+ EXPECT_EQ(stream->getBlockCount(), 8);
+ EXPECT_EQ(stream->getOutputTraits().blockSize, 512);
+}
+
+TEST(Factory, TimeBlockConstraints)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory(2ms);
+
+ 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, ::audio::AudioFormat(8000, 16, 1));
+
+ EXPECT_EQ(stream->getBlockCount(), 2);
+ EXPECT_EQ(stream->getOutputTraits().blockSize, 60);
+}
+
+TEST(Factory, NoConstraints)
+{
+ testing::audio::MockSink mockSink;
+ testing::audio::MockSource mockSource;
+ ::audio::StreamFactory factory(2ms);
+
+ EXPECT_CALL(mockSource, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+ EXPECT_CALL(mockSink, getTraits).WillRepeatedly(Return(::audio::Endpoint::Traits{}));
+
+ auto stream = factory.makeStream(mockSource, mockSink, ::audio::AudioFormat(16000, 16, 1));
+
+ EXPECT_EQ(stream->getBlockCount(), 2);
+ EXPECT_EQ(stream->getOutputTraits().blockSize, 64);
+}
+
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);
M module-audio/board/rt1051/RT1051AudioCodec.cpp => module-audio/board/rt1051/RT1051AudioCodec.cpp +9 -4
@@ 30,7 30,7 @@ namespace audio
DeinitBsp();
}
- CodecParamsMAX98090::InputPath RT1051AudioCodec::getCodecInputPath(const AudioDevice::Format &format)
+ CodecParamsMAX98090::InputPath RT1051AudioCodec::getCodecInputPath(const AudioDevice::Configuration &format)
{
switch (format.inputPath) {
case AudioDevice::InputPath::Headphones:
@@ 44,7 44,7 @@ namespace audio
};
}
- CodecParamsMAX98090::OutputPath RT1051AudioCodec::getCodecOutputPath(const AudioDevice::Format &format)
+ CodecParamsMAX98090::OutputPath RT1051AudioCodec::getCodecOutputPath(const AudioDevice::Configuration &format)
{
auto mono = (format.flags & static_cast<std::uint32_t>(AudioDevice::Flags::OutputMono)) != 0;
@@ 64,7 64,7 @@ namespace audio
}
}
- AudioDevice::RetCode RT1051AudioCodec::Start(const AudioDevice::Format &format)
+ AudioDevice::RetCode RT1051AudioCodec::Start(const AudioDevice::Configuration &format)
{
cpp_freertos::LockGuard lock(mutex);
@@ 180,7 180,7 @@ namespace audio
return AudioDevice::RetCode::Success;
}
- bool RT1051AudioCodec::IsFormatSupported(const AudioDevice::Format &format)
+ bool RT1051AudioCodec::IsFormatSupported(const AudioDevice::Configuration &format)
{
if (CodecParamsMAX98090::ValToSampleRate(format.sampleRate_Hz) == CodecParamsMAX98090::SampleRate::Invalid) {
@@ 285,6 285,11 @@ namespace audio
return formats;
}
+ auto RT1051AudioCodec::getTraits() const -> Traits
+ {
+ return Traits{.usesDMA = true};
+ }
+
void rxAudioCodecCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
{
auto self = static_cast<RT1051AudioCodec *>(userData);
M module-audio/board/rt1051/RT1051AudioCodec.hpp => module-audio/board/rt1051/RT1051AudioCodec.hpp +5 -4
@@ 37,14 37,15 @@ namespace audio
RT1051AudioCodec();
virtual ~RT1051AudioCodec();
- AudioDevice::RetCode Start(const Format &format) override final;
+ AudioDevice::RetCode Start(const Configuration &format) override final;
AudioDevice::RetCode Stop() override final;
AudioDevice::RetCode OutputVolumeCtrl(float vol) override final;
AudioDevice::RetCode InputGainCtrl(float gain) override final;
AudioDevice::RetCode OutputPathCtrl(OutputPath outputPath) override final;
AudioDevice::RetCode InputPathCtrl(InputPath inputPath) override final;
- bool IsFormatSupported(const Format &format) override final;
+ bool IsFormatSupported(const Configuration &format) override final;
auto getSupportedFormats() -> const std::vector<AudioFormat> & override final;
+ auto getTraits() const -> Traits override final;
cpp_freertos::MutexStandard mutex;
@@ 85,7 86,7 @@ namespace audio
void OutStop();
void InStop();
- CodecParamsMAX98090::InputPath getCodecInputPath(const AudioDevice::Format &format);
- CodecParamsMAX98090::OutputPath getCodecOutputPath(const AudioDevice::Format &format);
+ CodecParamsMAX98090::InputPath getCodecInputPath(const AudioDevice::Configuration &format);
+ CodecParamsMAX98090::OutputPath getCodecOutputPath(const AudioDevice::Configuration &format);
};
} // namespace audio
M module-audio/board/rt1051/RT1051CellularAudio.cpp => module-audio/board/rt1051/RT1051CellularAudio.cpp +7 -2
@@ 30,7 30,7 @@ namespace audio
Deinit();
}
- AudioDevice::RetCode RT1051CellularAudio::Start(const AudioDevice::Format &format)
+ AudioDevice::RetCode RT1051CellularAudio::Start(const AudioDevice::Configuration &format)
{
cpp_freertos::LockGuard lock(mutex);
@@ 118,7 118,7 @@ namespace audio
return AudioDevice::RetCode::Success;
}
- bool RT1051CellularAudio::IsFormatSupported(const AudioDevice::Format &format)
+ bool RT1051CellularAudio::IsFormatSupported(const AudioDevice::Configuration &format)
{
return true;
}
@@ 254,6 254,11 @@ namespace audio
return formats;
}
+ auto RT1051CellularAudio::getTraits() const -> Traits
+ {
+ return Traits{.usesDMA = true};
+ }
+
void rxCellularCallback(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData)
{
auto self = static_cast<RT1051CellularAudio *>(userData);
M module-audio/board/rt1051/RT1051CellularAudio.hpp => module-audio/board/rt1051/RT1051CellularAudio.hpp +3 -2
@@ 35,14 35,15 @@ namespace audio
RT1051CellularAudio();
virtual ~RT1051CellularAudio();
- AudioDevice::RetCode Start(const Format &format) override final;
+ AudioDevice::RetCode Start(const Configuration &format) override final;
AudioDevice::RetCode Stop() override final;
AudioDevice::RetCode OutputVolumeCtrl(float vol) override final;
AudioDevice::RetCode InputGainCtrl(float gain) override final;
AudioDevice::RetCode OutputPathCtrl(OutputPath outputPath) override final;
AudioDevice::RetCode InputPathCtrl(InputPath inputPath) override final;
- bool IsFormatSupported(const Format &format) override final;
+ bool IsFormatSupported(const Configuration &format) override final;
auto getSupportedFormats() -> const std::vector<AudioFormat> & override final;
+ auto getTraits() const -> Traits override final;
cpp_freertos::MutexStandard mutex;
M module-audio/board/rt1051/SAIAudioDevice.cpp => module-audio/board/rt1051/SAIAudioDevice.cpp +1 -1
@@ 8,7 8,7 @@
using namespace audio;
SAIAudioDevice::SAIAudioDevice(I2S_Type *base, sai_edma_handle_t *rxHandle, sai_edma_handle_t *txHandle)
- : AudioDevice(saiCapabilities, saiCapabilities), _base(base), rx(rxHandle), tx(txHandle)
+ : _base(base), rx(rxHandle), tx(txHandle)
{}
void SAIAudioDevice::initiateRxTransfer()
M module-audio/board/rt1051/SAIAudioDevice.hpp => module-audio/board/rt1051/SAIAudioDevice.hpp +0 -2
@@ 30,8 30,6 @@ namespace audio
sai_edma_handle_t *tx = nullptr;
bool txEnabled = false;
bool rxEnabled = false;
-
- static constexpr Capabilities saiCapabilities = {.usesDMA = true};
};
} // namespace audio
M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.cpp +10 -4
@@ 9,13 9,14 @@
#include <Audio/Stream.hpp>
+#include <chrono>
#include <cassert>
using audio::AudioFormat;
using namespace bluetooth;
+using namespace std::chrono_literals;
-BluetoothAudioDevice::BluetoothAudioDevice(MediaContext *mediaContext)
- : AudioDevice(btCapabilities, btCapabilities), ctx(mediaContext)
+BluetoothAudioDevice::BluetoothAudioDevice(MediaContext *mediaContext) : ctx(mediaContext)
{
LOG_DEBUG("Bluetooth audio device created");
}
@@ 34,7 35,7 @@ void BluetoothAudioDevice::setMediaContext(MediaContext *mediaContext)
static_cast<unsigned>(AVDTP::sbcConfig.numChannels)}};
}
-auto BluetoothAudioDevice::Start(const Format &format) -> audio::AudioDevice::RetCode
+auto BluetoothAudioDevice::Start(const Configuration &format) -> audio::AudioDevice::RetCode
{
return audio::AudioDevice::RetCode::Success;
}
@@ 71,7 72,7 @@ auto BluetoothAudioDevice::InputPathCtrl(InputPath inputPath) -> audio::AudioDev
return audio::AudioDevice::RetCode::Success;
}
-auto BluetoothAudioDevice::IsFormatSupported(const Format &format) -> bool
+auto BluetoothAudioDevice::IsFormatSupported(const Configuration &format) -> bool
{
return true;
}
@@ 136,3 137,8 @@ auto BluetoothAudioDevice::getSupportedFormats() -> const std::vector<AudioForma
{
return formats;
}
+
+auto BluetoothAudioDevice::getTraits() const -> Traits
+{
+ return Traits{.usesDMA = false, .blockSizeConstraint = 512U, .timeConstraint = 10ms};
+}
M module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp => module-bluetooth/Bluetooth/audio/BluetoothAudioDevice.hpp +3 -4
@@ 18,15 18,16 @@ namespace bluetooth
void setMediaContext(MediaContext *MediaContext);
- auto Start(const Format &format) -> audio::AudioDevice::RetCode override;
+ auto Start(const Configuration &format) -> audio::AudioDevice::RetCode override;
auto Stop() -> audio::AudioDevice::RetCode override;
auto OutputVolumeCtrl(float vol) -> audio::AudioDevice::RetCode override;
auto InputGainCtrl(float gain) -> audio::AudioDevice::RetCode override;
auto OutputPathCtrl(OutputPath outputPath) -> audio::AudioDevice::RetCode override;
auto InputPathCtrl(InputPath inputPath) -> audio::AudioDevice::RetCode override;
- auto IsFormatSupported(const Format &format) -> bool override;
+ auto IsFormatSupported(const Configuration &format) -> bool override;
auto getSupportedFormats() -> const std::vector<audio::AudioFormat> & override;
+ auto getTraits() const -> Traits override;
// Endpoint control methods
void onDataSend() override;
@@ 42,8 43,6 @@ namespace bluetooth
MediaContext *ctx = nullptr;
bool outputEnabled = false;
std::vector<audio::AudioFormat> formats;
-
- static constexpr Capabilities btCapabilities = {.usesDMA = false, .minBlockSize = 512U, .maxBlockSize = 512U};
};
} // namespace bluetooth