M module-audio/Audio/AbstractStream.hpp => module-audio/Audio/AbstractStream.hpp +1 -1
@@ 215,7 215,7 @@ namespace audio
[[nodiscard]] virtual auto getInputTraits() const noexcept -> Traits = 0;
/**
- * @brief Get the traits of the stream's input.
+ * @brief Get the traits of the stream's output.
*
* @return Traits
*/
M module-audio/Audio/AudioDevice.hpp => module-audio/Audio/AudioDevice.hpp +7 -0
@@ 43,7 43,14 @@ namespace audio
return RetCode::Success;
}
+ /// Set device output volume
+ /// @param vol desired volume from 0 to 10
+ /// @return RetCode::Success if OK, or RetCode::Failure otherwise
virtual RetCode setOutputVolume(float vol) = 0;
+
+ /// Set device input gain
+ /// @param gain desired input gain from 0 to 100
+ /// @return RetCode::Success if OK, or RetCode::Failure otherwise
virtual RetCode setInputGain(float gain) = 0;
auto getSinkFormat() -> AudioFormat override
M module-audio/board/linux/CMakeLists.txt => module-audio/board/linux/CMakeLists.txt +1 -0
@@ 3,6 3,7 @@
set(AUDIO_LINUX_SOURCES
LinuxAudioPlatform.cpp
+ LinuxAudioDevice.cpp
)
add_library(${AUDIO_BOARD_LIBRARY} STATIC ${AUDIO_LINUX_SOURCES})
A module-audio/board/linux/LinuxAudioDevice.cpp => module-audio/board/linux/LinuxAudioDevice.cpp +213 -0
@@ 0,0 1,213 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "LinuxAudioDevice.hpp"
+#include <Audio/Stream.hpp>
+#include <module-utils/log/log.hpp>
+
+namespace audio
+{
+ namespace
+ {
+ class PortAudio
+ {
+ public:
+ PortAudio();
+ PortAudio(const PortAudio &) = delete;
+ PortAudio(PortAudio &&) = delete;
+ PortAudio &operator=(const PortAudio &) = delete;
+ PortAudio &operator=(PortAudio &&) = delete;
+ ~PortAudio() noexcept;
+ };
+
+ PortAudio::PortAudio()
+ {
+ if (const auto errorCode = Pa_Initialize(); errorCode == paNoError) {
+ LOG_INFO("Portaudio initialized successfully");
+ }
+ else {
+ LOG_ERROR("Error (code %d) initiializing Portaudio: %s", errorCode, Pa_GetErrorText(errorCode));
+ }
+ }
+
+ PortAudio::~PortAudio() noexcept
+ {
+ if (const auto errorCode = Pa_Terminate(); errorCode == paNoError) {
+ LOG_INFO("Portaudio terminated successfully");
+ }
+ else {
+ LOG_ERROR("Error (code %d) while terminating Portaudio: %s", errorCode, Pa_GetErrorText(errorCode));
+ }
+ }
+
+ PortAudio portAudio;
+ } // namespace
+
+ LinuxAudioDevice::LinuxAudioDevice()
+ : supportedFormats(
+ audio::AudioFormat::makeMatrix(supportedSampleRates, supportedBitWidths, supportedChannelModes))
+ {}
+
+ LinuxAudioDevice::~LinuxAudioDevice()
+ {
+ if (stream != nullptr) {
+ closeStream();
+ }
+ }
+
+ void LinuxAudioDevice::closeStream()
+ {
+ if (const auto errorCode = Pa_AbortStream(stream); errorCode != paNoError) {
+ LOG_ERROR("Error (code %d) while stopping Portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
+ }
+ if (const auto errorCode = Pa_CloseStream(stream); errorCode != paNoError) {
+ LOG_ERROR("Error (code %d) while closing Portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
+ }
+ }
+
+ auto LinuxAudioDevice::Start() -> RetCode
+ {
+ if (!isSinkConnected()) {
+ return AudioDevice::RetCode::Failure;
+ }
+ return AudioDevice::RetCode::Success;
+ }
+
+ auto LinuxAudioDevice::Stop() -> RetCode
+ {
+ return AudioDevice::RetCode::Success;
+ }
+
+ auto LinuxAudioDevice::setOutputVolume(float vol) -> RetCode
+ {
+ constexpr auto minVolume = .0f;
+ constexpr auto maxVolume = 10.0f;
+ vol = std::clamp(vol, minVolume, maxVolume);
+ volumeFactor = 1.0f * (vol / maxVolume);
+ return RetCode::Success;
+ }
+
+ auto LinuxAudioDevice::setInputGain([[maybe_unused]] float gain) -> RetCode
+ {
+ return RetCode::Success;
+ }
+
+ auto LinuxAudioDevice::getTraits() const -> Traits
+ {
+ return Traits{};
+ }
+
+ auto LinuxAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
+ {
+ return supportedFormats;
+ }
+
+ auto LinuxAudioDevice::getSourceFormat() -> audio::AudioFormat
+ {
+ return currentFormat;
+ }
+
+ void LinuxAudioDevice::onDataSend()
+ {
+ audio::Stream::Span dataSpan;
+ Sink::_stream->peek(dataSpan);
+ auto streamData = reinterpret_cast<std::int16_t *>(dataSpan.data);
+ cache.insert(cache.end(), &streamData[0], &streamData[dataSpan.dataSize / sizeof(std::int16_t)]);
+ Sink::_stream->consume();
+ }
+
+ void LinuxAudioDevice::onDataReceive()
+ {}
+
+ void LinuxAudioDevice::enableInput()
+ {}
+
+ void LinuxAudioDevice::enableOutput()
+ {
+ LOG_INFO("Enabling audio output...");
+ if (!isSinkConnected()) {
+ LOG_ERROR("Output stream is not connected!");
+ return;
+ }
+
+ currentFormat = Sink::_stream->getOutputTraits().format;
+ const auto numOutputChannels = currentFormat.getChannels();
+ auto callback = [](const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo *timeInfo,
+ PaStreamCallbackFlags statusFlags,
+ void *userData) -> int {
+ LinuxAudioDevice *dev = static_cast<LinuxAudioDevice *>(userData);
+ return dev->streamCallback(input, output, frameCount, timeInfo, statusFlags);
+ };
+ auto errorCode = Pa_OpenDefaultStream(&stream,
+ 0,
+ numOutputChannels,
+ paInt16,
+ currentFormat.getSampleRate(),
+ paFramesPerBufferUnspecified,
+ callback,
+ this);
+ if (errorCode != paNoError) {
+ LOG_ERROR("Error (code %d) while creating portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
+ return;
+ }
+ if (errorCode = Pa_StartStream(stream); errorCode != paNoError) {
+ LOG_ERROR("Error (code %d) while starting portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
+ return;
+ }
+ }
+
+ void LinuxAudioDevice::disableInput()
+ {}
+
+ void LinuxAudioDevice::disableOutput()
+ {
+ LOG_INFO("Disabling audio output...");
+ if (!isSinkConnected()) {
+ LOG_ERROR("Error while stopping Linux Audio Device! Null stream.");
+ return;
+ }
+
+ closeStream();
+ stream = nullptr;
+ currentFormat = {};
+ }
+
+ int LinuxAudioDevice::streamCallback([[maybe_unused]] const void *input,
+ void *output,
+ unsigned long frameCount,
+ [[maybe_unused]] const PaStreamCallbackTimeInfo *timeInfo,
+ [[maybe_unused]] PaStreamCallbackFlags statusFlags)
+ {
+ if (!isSinkConnected()) {
+ return paAbort;
+ }
+
+ const auto expectedBufferSize = frameCount * currentFormat.getChannels();
+ if (!isCacheReady(expectedBufferSize)) {
+ onDataSend();
+ }
+
+ const auto dataReadySize = std::min(expectedBufferSize, cache.size());
+ cacheToOutputBuffer(static_cast<std::int16_t *>(output), dataReadySize);
+
+ return paContinue;
+ }
+
+ bool LinuxAudioDevice::isCacheReady(std::size_t expectedSize) const noexcept
+ {
+ return cache.size() >= expectedSize;
+ }
+
+ void LinuxAudioDevice::cacheToOutputBuffer(std::int16_t *buffer, std::size_t size)
+ {
+ for (size_t i = 0; i < size; ++i) {
+ const auto adjustedValue = static_cast<float>(cache[i]) * volumeFactor;
+ *(buffer) = static_cast<std::int16_t>(adjustedValue);
+ buffer++;
+ }
+ cache.erase(cache.begin(), cache.begin() + size);
+ }
+} // namespace audio
A module-audio/board/linux/LinuxAudioDevice.hpp => module-audio/board/linux/LinuxAudioDevice.hpp +73 -0
@@ 0,0 1,73 @@
+// 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/AudioDevice.hpp>
+#include <Audio/AudioFormat.hpp>
+
+#include <portaudio.h>
+
+#include <deque>
+
+namespace audio
+{
+ class LinuxAudioDevice : public audio::AudioDevice
+ {
+ public:
+ LinuxAudioDevice();
+ virtual ~LinuxAudioDevice();
+
+ auto Start() -> RetCode override;
+ auto Stop() -> RetCode override;
+
+ /// Set device output volume
+ /// @param vol desired volume from 0 to 10
+ /// @return RetCode::Success if OK, or RetCode::Failure otherwise
+ auto setOutputVolume(float vol) -> RetCode override;
+
+ /// Set device input gain
+ /// @param gain desired input gain from 0 to 100
+ /// @return RetCode::Success if OK, or RetCode::Failure otherwise
+ auto setInputGain(float gain) -> RetCode override;
+
+ auto getTraits() const -> Traits override;
+ auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
+ auto getSourceFormat() -> audio::AudioFormat override;
+
+ // Endpoint control methods
+ void onDataSend() override;
+ void onDataReceive() override;
+ void enableInput() override;
+ void enableOutput() override;
+ void disableInput() override;
+ void disableOutput() override;
+
+ private:
+ int streamCallback(const void *input,
+ void *output,
+ unsigned long frameCount,
+ const PaStreamCallbackTimeInfo *timeInfo,
+ PaStreamCallbackFlags statusFlags);
+ bool isCacheReady(std::size_t expectedSize) const noexcept;
+ void cacheToOutputBuffer(std::int16_t *buffer, std::size_t size);
+
+ void closeStream();
+
+ constexpr static std::initializer_list<unsigned int> supportedSampleRates = {44100, 48000};
+ constexpr static std::initializer_list<unsigned int> supportedBitWidths = {16};
+ constexpr static std::initializer_list<unsigned int> supportedChannelModes = {1, 2};
+
+ std::vector<audio::AudioFormat> supportedFormats;
+ audio::AudioFormat currentFormat;
+
+ /// pointer to portaudio stream
+ PaStream *stream = nullptr;
+
+ /// Local cache to store data read from Pure stream
+ std::deque<std::int16_t> cache;
+
+ float volumeFactor = 1.0f;
+ };
+
+} // namespace audio
M module-audio/board/linux/LinuxAudioPlatform.cpp => module-audio/board/linux/LinuxAudioPlatform.cpp +7 -3
@@ 1,8 1,9 @@
-// Copyright (c) 2021, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <Audio/AudioPlatform.hpp>
#include <Audio/Profiles/Profile.hpp>
+#include <board/linux/LinuxAudioDevice.hpp>
#include <memory>
#include <utility>
@@ 11,7 12,7 @@ using audio::AudioDevice;
using audio::AudioDeviceFactory;
using audio::AudioPlatform;
-class DummyAudioFactory : public AudioDeviceFactory
+class LinuxAudioFactory : public AudioDeviceFactory
{
public:
std::shared_ptr<AudioDevice> createCellularAudioDevice() override
@@ 22,11 23,14 @@ class DummyAudioFactory : public AudioDeviceFactory
protected:
std::shared_ptr<AudioDevice> getDevice([[maybe_unused]] const audio::Profile &profile) override
{
+ if (profile.GetAudioDeviceType() == AudioDevice::Type::Audiocodec) {
+ return std::make_shared<audio::LinuxAudioDevice>();
+ }
return nullptr;
}
};
std::unique_ptr<AudioDeviceFactory> AudioPlatform::GetDeviceFactory()
{
- return std::make_unique<DummyAudioFactory>();
+ return std::make_unique<LinuxAudioFactory>();
}
M module-bsp/board/linux/audio/linux_audiocodec.cpp => module-bsp/board/linux/audio/linux_audiocodec.cpp +1 -1
@@ 1,4 1,4 @@
-// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "linux_audiocodec.hpp"
M module-services/service-audio/ServiceAudio.cpp => module-services/service-audio/ServiceAudio.cpp +4 -0
@@ 139,6 139,10 @@ sys::ReturnCodes ServiceAudio::DeinitHandler()
void ServiceAudio::ProcessCloseReason(sys::CloseReason closeReason)
{
+ if (const auto &activeInputOpt = audioMux.GetActiveInput(); activeInputOpt.has_value()) {
+ const auto activeInput = activeInputOpt.value();
+ activeInput->audio->Stop();
+ }
sendCloseReadyMessage(this);
}