mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-17 10:29:37 -05:00
Fix of the issue that caused system crash when trying to play 96kHz FLAC file with USB cable connected. The reason of the issue was the lack of FreeRTOS heap space left, what caused pvPortMalloc() to fail when allocating memory for stream buffer. Additionally minor code cleanup.
133 lines
4.1 KiB
C++
133 lines
4.1 KiB
C++
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include "DecoderWorker.hpp"
|
|
#include <Audio/AbstractStream.hpp>
|
|
#include <Audio/decoder/Decoder.hpp>
|
|
|
|
audio::DecoderWorker::DecoderWorker(audio::AbstractStream *audioStreamOut,
|
|
Decoder *decoder,
|
|
EndOfFileCallback endOfFileCallback,
|
|
ChannelMode mode)
|
|
: sys::Worker(DecoderWorker::workerName, DecoderWorker::workerPriority, stackDepth), audioStreamOut(audioStreamOut),
|
|
decoder(decoder), endOfFileCallback(std::move(endOfFileCallback)),
|
|
bufferSize(audioStreamOut->getInputTraits().blockSize / sizeof(BufferInternalType)), channelMode(mode)
|
|
{}
|
|
|
|
audio::DecoderWorker::~DecoderWorker()
|
|
{
|
|
audioStreamOut->unregisterListeners(queueListener.get());
|
|
}
|
|
|
|
auto audio::DecoderWorker::init(std::list<sys::WorkerQueueInfo> queues) -> bool
|
|
{
|
|
const std::list<sys::WorkerQueueInfo> list{
|
|
{listenerQueueName, StreamQueuedEventsListener::listenerElementSize, listenerQueueCapacity}};
|
|
|
|
const auto isSuccessful = Worker::init(list);
|
|
|
|
queueListener = std::make_unique<StreamQueuedEventsListener>(getQueueByName(listenerQueueName));
|
|
if (!queueListener) {
|
|
return false;
|
|
}
|
|
|
|
audioStreamOut->registerListener(queueListener.get());
|
|
|
|
decoderBuffer = std::make_unique<BufferInternalType[]>(bufferSize);
|
|
if (!decoderBuffer) {
|
|
return false;
|
|
}
|
|
|
|
return isSuccessful;
|
|
}
|
|
|
|
bool audio::DecoderWorker::handleMessage(uint32_t queueID)
|
|
{
|
|
auto queue = queues[queueID];
|
|
auto &queueName = queue->GetQueueName();
|
|
if (queueName == listenerQueueName && queueListener) {
|
|
auto event = queueListener->getEvent();
|
|
|
|
switch (event.second) {
|
|
case Stream::Event::StreamOverflow:
|
|
break;
|
|
case Stream::Event::StreamUnderFlow:
|
|
break;
|
|
case Stream::Event::NoEvent:
|
|
break;
|
|
case Stream::Event::StreamFull:
|
|
break;
|
|
case Stream::Event::StreamHalfUsed:
|
|
[[fallthrough]];
|
|
case Stream::Event::StreamEmpty:
|
|
pushAudioData();
|
|
}
|
|
}
|
|
else if (queueName == SERVICE_QUEUE_NAME) {
|
|
auto &serviceQueue = getServiceQueue();
|
|
sys::WorkerCommand cmd;
|
|
|
|
if (serviceQueue.Dequeue(&cmd)) {
|
|
switch (static_cast<Command>(cmd.command)) {
|
|
case Command::EnablePlayback: {
|
|
playbackEnabled = true;
|
|
stateSemaphore.Give();
|
|
pushAudioData();
|
|
break;
|
|
}
|
|
case Command::DisablePlayback: {
|
|
playbackEnabled = false;
|
|
stateSemaphore.Give();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void audio::DecoderWorker::pushAudioData()
|
|
{
|
|
auto samplesRead = 0;
|
|
const unsigned int readScale = channelMode == ChannelMode::ForceStereo ? 2 : 1;
|
|
|
|
while (!audioStreamOut->isFull() && playbackEnabled) {
|
|
auto buffer = decoderBuffer.get();
|
|
samplesRead = decoder->decode(bufferSize / readScale, buffer);
|
|
|
|
if (samplesRead == 0) {
|
|
endOfFileCallback();
|
|
break;
|
|
}
|
|
|
|
// pcm mono to stereo force conversion
|
|
if (channelMode == ChannelMode::ForceStereo) {
|
|
for (unsigned int i = bufferSize / 2; i > 0; i--) {
|
|
buffer[i * 2 - 1] = buffer[i * 2 - 2] = buffer[i - 1];
|
|
}
|
|
}
|
|
|
|
if (!audioStreamOut->push(decoderBuffer.get(), samplesRead * sizeof(BufferInternalType) * readScale)) {
|
|
LOG_FATAL("Decoder failed to push to stream.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool audio::DecoderWorker::enablePlayback()
|
|
{
|
|
return sendCommand({.command = static_cast<uint32_t>(Command::EnablePlayback), .data = nullptr}) &&
|
|
stateChangeWait();
|
|
}
|
|
|
|
bool audio::DecoderWorker::disablePlayback()
|
|
{
|
|
return sendCommand({.command = static_cast<uint32_t>(Command::DisablePlayback), .data = nullptr}) &&
|
|
stateChangeWait();
|
|
}
|
|
|
|
bool audio::DecoderWorker::stateChangeWait()
|
|
{
|
|
return stateSemaphore.Take();
|
|
}
|