Files
MuditaOS/module-audio/Audio/Audio.cpp
Marcin Smoczyński f1fc9df152 [EGD-4977] Reduce audio lag during voice call
Reduce audio delay by reducing audio buffer size in router operation.
Audio streams are now created directly in the operations, not in the
audio service, which gives more flexibility.

Audio Buffer size is calculated based on endpoints (source, sink) and
operation capabilities. This commit also enables allocations in a
non-cacheable region of OCRAM for endpoints that use DMA for data
transport.

Introduce power-of-two operations that use built-in functions and
possibly dedicated hardware instructions of an MCU. These operations
are required by the audio stream buffer size calculation algorithm.

Signed-off-by: Marcin Smoczyński <smoczynski.marcin@gmail.com>
2021-02-01 22:22:12 +01:00

170 lines
4.8 KiB
C++

// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "Audio.hpp"
#include "Operation/Operation.hpp"
#include <log/log.hpp>
#include <bsp/headset/headset.hpp>
namespace audio
{
Audio::Audio(AudioServiceMessage::Callback callback) : currentOperation(), serviceCallback(callback)
{
auto ret = Operation::Create(Operation::Type::Idle, "", audio::PlaybackType::None, callback);
if (ret) {
currentOperation = std::move(ret);
}
audioSinkState.setConnected(EventType::JackState, bsp::headset::IsInserted());
}
Position Audio::GetPosition()
{
return currentOperation->GetPosition();
}
std::optional<Tags> Audio::GetFileTags(const char *filename)
{
auto ret = Decoder::Create(filename);
if (ret == nullptr) {
return {};
}
else {
return *ret->fetchTags();
};
}
audio::RetCode Audio::SendEvent(std::shared_ptr<Event> evt)
{
audioSinkState.UpdateState(evt);
UpdateProfiles();
return currentOperation->SendEvent(std::move(evt));
}
audio::RetCode Audio::SetOutputVolume(Volume vol)
{
auto volToSet = vol;
if (vol > maxVolume) {
volToSet = maxVolume;
}
if (vol < minVolume) {
volToSet = minVolume;
}
return currentOperation->SetOutputVolume(volToSet);
}
audio::RetCode Audio::SetInputGain(Gain gain)
{
auto gainToSet = gain;
if (gain > maxGain) {
gainToSet = maxGain;
}
if (gain < minGain) {
gainToSet = minGain;
}
return currentOperation->SetInputGain(gainToSet);
}
audio::RetCode Audio::Start(Operation::Type op,
audio::Token token,
const char *fileName,
const audio::PlaybackType &playbackType)
{
try {
auto ret = Operation::Create(op, fileName, playbackType, serviceCallback);
switch (op) {
case Operation::Type::Playback:
currentState = State::Playback;
break;
case Operation::Type::Recorder:
currentState = State::Recording;
break;
case Operation::Type::Router:
currentState = State::Routing;
break;
case Operation::Type::Idle:
break;
}
currentOperation = std::move(ret);
UpdateProfiles();
}
catch (const AudioInitException &audioException) {
// If creating operation failed fallback to IdleOperation which is guaranteed to work
LOG_ERROR(
"Failed to create operation type %s, error message:\n%s", Operation::c_str(op), audioException.what());
currentOperation = Operation::Create(Operation::Type::Idle);
currentState = State ::Idle;
return audioException.getErrorCode();
}
return currentOperation->Start(token);
}
audio::RetCode Audio::Start()
{
currentOperation->Stop();
return Start(currentOperation->GetOperationType(),
currentOperation->GetToken(),
currentOperation->GetFilePath().c_str(),
currentOperation->GetPlaybackType());
}
audio::RetCode Audio::Stop()
{
if (currentState == State::Idle) {
return RetCode::Success;
}
auto retStop = currentOperation->Stop();
if (retStop != RetCode::Success) {
LOG_ERROR("Operation STOP failure: %s", audio::str(retStop).c_str());
}
auto ret = Operation::Create(Operation::Type::Idle);
if (ret) {
currentState = State::Idle;
currentOperation = std::move(ret);
return RetCode::Success;
}
else {
return RetCode::OperationCreateFailed;
}
}
audio::RetCode Audio::Pause()
{
if (currentState == State::Idle) {
return RetCode::InvokedInIncorrectState;
}
return currentOperation->Pause();
}
audio::RetCode Audio::Resume()
{
if (currentState == State::Idle) {
return RetCode::InvokedInIncorrectState;
}
return currentOperation->Resume();
}
audio::RetCode Audio::Mute()
{
return SetOutputVolume(0);
}
void Audio::UpdateProfiles()
{
auto updateEvents = audioSinkState.getUpdateEvents();
for (auto &event : updateEvents) {
currentOperation->SendEvent(event);
}
currentOperation->SwitchToPriorityProfile();
}
} // namespace audio