Files
MuditaOS/module-audio/Audio/Operation/RecorderOperation.cpp
T
Hubert Chrzaniuk 7ccc52c04c [EGD-4357] Improve error handling in audio (#990)
- error code can be now returned also during
   initialization
 - fixed crash when user requested to playback
   file that does not exist
2020-11-12 16:15:42 +01:00

212 lines
7.2 KiB
C++

// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "RecorderOperation.hpp"
#include "Audio/encoder/Encoder.hpp"
#include "bsp/audio/bsp_audio.hpp"
#include "Audio/Profiles/Profile.hpp"
#include "Audio/Profiles/ProfileRecordingHeadphones.hpp"
#include "Audio/Profiles/ProfileRecordingOnBoardMic.hpp"
#include "Audio/AudioCommon.hpp"
#include "log/log.hpp"
#include "FreeRTOS.h"
#include "task.h"
namespace audio
{
#define PERF_STATS_ON 0
using namespace bsp;
RecorderOperation::RecorderOperation(
const char *file, std::function<uint32_t(const std::string &path, const uint32_t &defaultValue)> dbCallback)
{
audioCallback = [this](const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer) -> int32_t {
#if PERF_STATS_ON == 1
auto tstamp = xTaskGetTickCount();
#endif
auto ret = enc->Encode(framesPerBuffer, reinterpret_cast<int16_t *>(const_cast<void *>(inputBuffer)));
#if PERF_STATS_ON == 1
LOG_DEBUG("Enc:%dms", xTaskGetTickCount() - tstamp);
// LOG_DEBUG("Watermark:%lu",uxTaskGetStackHighWaterMark2(NULL)); M.P: left here on purpose, it's handy
// during sf tests on hardware
#endif
if (ret == 0) {
state = State::Idle;
eventCallback({PlaybackEventType::FileSystemNoSpace, audio::Token::MakeBadToken()});
}
return ret;
};
constexpr audio::Gain defaultRecordingOnBoardMicGain = 200;
constexpr audio::Gain defaultRecordingHeadsetGain = 100;
const auto dbRecordingOnBoardMicGainPath =
audio::dbPath(audio::Setting::Gain, audio::PlaybackType::None, audio::Profile::Type::RecordingBuiltInMic);
const auto recordingOnBoardMicGain = dbCallback(dbRecordingOnBoardMicGainPath, defaultRecordingOnBoardMicGain);
const auto dbRecordingHeadsetGainPath =
audio::dbPath(audio::Setting::Gain, audio::PlaybackType::None, audio::Profile::Type::RecordingHeadphones);
const auto recordingHeadsetGain = dbCallback(dbRecordingHeadsetGainPath, defaultRecordingHeadsetGain);
// order in vector defines priority
supportedProfiles.emplace_back(
Profile::Create(Profile::Type::RecordingBluetoothHSP, nullptr, 0, recordingHeadsetGain), false);
supportedProfiles.emplace_back(
Profile::Create(Profile::Type::RecordingHeadphones, nullptr, 0, recordingHeadsetGain), false);
supportedProfiles.emplace_back(
Profile::Create(Profile::Type::RecordingBuiltInMic, nullptr, 0, recordingOnBoardMicGain), true);
auto defaultProfile = GetProfile(Profile::Type::PlaybackLoudspeaker);
if (!defaultProfile) {
throw AudioInitException("Error during initializing profile", RetCode::ProfileNotSet);
}
currentProfile = defaultProfile;
uint32_t channels = 0;
if ((currentProfile->GetInOutFlags() & static_cast<uint32_t>(AudioDevice::Flags::InputLeft)) ||
(currentProfile->GetInOutFlags() & static_cast<uint32_t>(AudioDevice::Flags::InputRight))) {
channels = 1;
}
else if (currentProfile->GetInOutFlags() & static_cast<uint32_t>(AudioDevice::Flags::InputStereo)) {
channels = 2;
}
enc = Encoder::Create(file, Encoder::Format{.chanNr = channels, .sampleRate = currentProfile->GetSampleRate()});
if (enc == nullptr) {
throw AudioInitException("Error during initializing encoder", RetCode::InvalidFormat);
}
auto retCode = SwitchToPriorityProfile();
if (retCode != RetCode::Success) {
throw AudioInitException("Failed to switch audio profile", retCode);
}
}
audio::RetCode RecorderOperation::Start(audio::AsyncCallback callback, audio::Token token)
{
if (state == State::Paused || state == State::Active) {
return RetCode::InvokedInIncorrectState;
}
operationToken = token;
eventCallback = callback;
state = State::Active;
if (audioDevice->IsFormatSupported(currentProfile->GetAudioFormat())) {
auto ret = audioDevice->Start(currentProfile->GetAudioFormat());
return GetDeviceError(ret);
}
else {
return RetCode::InvalidFormat;
}
}
audio::RetCode RecorderOperation::Stop()
{
if (state == State::Paused || state == State::Idle) {
return RetCode::InvokedInIncorrectState;
}
state = State::Idle;
return GetDeviceError(audioDevice->Stop());
}
audio::RetCode RecorderOperation::Pause()
{
if (state == State::Paused || state == State::Idle) {
return RetCode::InvokedInIncorrectState;
}
state = State::Paused;
return GetDeviceError(audioDevice->Stop());
}
audio::RetCode RecorderOperation::Resume()
{
if (state == State::Active || state == State::Idle) {
return RetCode::InvokedInIncorrectState;
}
state = State::Active;
auto ret = audioDevice->Start(currentProfile->GetAudioFormat());
return GetDeviceError(ret);
}
audio::RetCode RecorderOperation::SendEvent(std::shared_ptr<Event> evt)
{
auto isAvailable = evt->getDeviceState() == Event::DeviceState::Connected ? true : false;
switch (evt->getType()) {
case EventType::JackState:
SetProfileAvailability({Profile::Type::RecordingHeadphones}, isAvailable);
SwitchToPriorityProfile();
break;
case EventType::BlutoothHSPDeviceState:
SetProfileAvailability({Profile::Type::RecordingBluetoothHSP}, isAvailable);
SwitchToPriorityProfile();
break;
default:
return RetCode::UnsupportedEvent;
}
return RetCode::Success;
}
audio::RetCode RecorderOperation::SwitchProfile(const Profile::Type type)
{
auto ret = GetProfile(type);
if (ret) {
currentProfile = ret;
}
else {
return RetCode::UnsupportedProfile;
}
audioDevice = AudioDevice::Create(currentProfile->GetAudioDeviceType(), audioCallback).value_or(nullptr);
if (audioDevice == nullptr) {
LOG_ERROR("Error creating AudioDevice");
return RetCode::Failed;
}
switch (state) {
case State::Idle:
case State::Paused:
break;
case State::Active:
state = State::Idle;
Start(eventCallback, operationToken);
break;
}
return audio::RetCode::Success;
}
audio::RetCode RecorderOperation::SetOutputVolume(float vol)
{
currentProfile->SetOutputVolume(vol);
auto ret = audioDevice->OutputVolumeCtrl(vol);
return GetDeviceError(ret);
}
audio::RetCode RecorderOperation::SetInputGain(float gain)
{
currentProfile->SetInputGain(gain);
auto ret = audioDevice->InputGainCtrl(gain);
return GetDeviceError(ret);
}
Position RecorderOperation::GetPosition()
{
return enc->getCurrentPosition();
}
} // namespace audio