mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-24 22:08:33 -05:00
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>
170 lines
4.8 KiB
C++
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
|