mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-02-19 15:39:45 -05:00
* Fix of the issue that seeking in large MP3 file would cause OOM error when creating MP3 indexes. This caused OS crash when looping large MP3 file in Relaxation. * Minor cleanups.
139 lines
3.8 KiB
C++
139 lines
3.8 KiB
C++
// Copyright (c) 2017-2025, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md
|
|
|
|
#include <cstdio>
|
|
#include <Utils.hpp>
|
|
#include "Decoder.hpp"
|
|
#include "DecoderMP3.hpp"
|
|
#include "DecoderFLAC.hpp"
|
|
#include "DecoderWAV.hpp"
|
|
|
|
#include "tag.h"
|
|
#include <tags_fetcher/TagsFetcher.hpp>
|
|
|
|
namespace audio
|
|
{
|
|
Decoder::Decoder(const std::string &path) : filePath(path)
|
|
{
|
|
fd = std::fopen(path.c_str(), "rb");
|
|
if (fd == nullptr) {
|
|
return;
|
|
}
|
|
|
|
constexpr std::size_t streamBufferSize = 16 * 1024;
|
|
streamBuffer = std::make_unique<char[]>(streamBufferSize);
|
|
setvbuf(fd, streamBuffer.get(), _IOFBF, streamBufferSize);
|
|
|
|
std::fseek(fd, 0, SEEK_END);
|
|
fileSize = std::ftell(fd);
|
|
std::rewind(fd);
|
|
|
|
tags = fetchTags();
|
|
}
|
|
|
|
Decoder::~Decoder()
|
|
{
|
|
if (audioWorker) {
|
|
audioWorker->close();
|
|
}
|
|
|
|
if (fd != nullptr) {
|
|
std::fclose(fd);
|
|
}
|
|
}
|
|
|
|
auto Decoder::fetchTags() -> std::unique_ptr<tags::fetcher::Tags>
|
|
{
|
|
return std::make_unique<tags::fetcher::Tags>(tags::fetcher::fetchTags(filePath));
|
|
}
|
|
|
|
auto Decoder::Create(const std::string &filePath) -> std::unique_ptr<Decoder>
|
|
{
|
|
const auto extension = std::filesystem::path(filePath).extension();
|
|
const auto extensionLowercase = utils::stringToLowercase(extension);
|
|
|
|
std::unique_ptr<Decoder> dec;
|
|
|
|
if (extensionLowercase == ".wav") {
|
|
dec = std::make_unique<DecoderWAV>(filePath);
|
|
}
|
|
else if (extensionLowercase == ".mp3") {
|
|
dec = std::make_unique<DecoderMP3>(filePath);
|
|
}
|
|
else if (extensionLowercase == ".flac") {
|
|
dec = std::make_unique<DecoderFLAC>(filePath);
|
|
}
|
|
else {
|
|
return nullptr;
|
|
}
|
|
|
|
if (!dec->isInitialized) {
|
|
return nullptr;
|
|
}
|
|
|
|
return dec;
|
|
}
|
|
|
|
auto Decoder::startDecodingWorker(const DecoderWorker::EndOfFileCallback &endOfFileCallback,
|
|
const DecoderWorker::FileDeletedCallback &fileDeletedCallback) -> void
|
|
{
|
|
assert(_stream != nullptr);
|
|
|
|
if (audioWorker == nullptr) {
|
|
const auto channelMode = (tags->num_channel == 1) ? DecoderWorker::ChannelMode::ForceStereo
|
|
: DecoderWorker::ChannelMode::NoConversion;
|
|
|
|
audioWorker =
|
|
std::make_unique<DecoderWorker>(_stream, this, endOfFileCallback, fileDeletedCallback, channelMode);
|
|
audioWorker->init();
|
|
audioWorker->run();
|
|
}
|
|
else {
|
|
LOG_DEBUG("AudioWorker already running.");
|
|
}
|
|
}
|
|
|
|
auto Decoder::stopDecodingWorker() -> void
|
|
{
|
|
if (audioWorker) {
|
|
audioWorker->close();
|
|
}
|
|
audioWorker = nullptr;
|
|
}
|
|
|
|
auto Decoder::onDataReceive() -> void
|
|
{
|
|
audioWorker->enablePlayback();
|
|
}
|
|
|
|
auto Decoder::enableInput() -> void
|
|
{
|
|
audioWorker->enablePlayback();
|
|
}
|
|
|
|
auto Decoder::disableInput() -> void
|
|
{
|
|
audioWorker->disablePlayback();
|
|
}
|
|
|
|
auto Decoder::getSourceFormat() -> AudioFormat
|
|
{
|
|
auto bitWidth = getBitWidth();
|
|
// this is a decoder mono to stereo hack, will be removed when proper
|
|
// transcoding implementation is added
|
|
auto channels = (tags->num_channel == 1) ? 2U : tags->num_channel;
|
|
|
|
return AudioFormat{tags->sample_rate, bitWidth, channels};
|
|
}
|
|
|
|
auto Decoder::getSupportedFormats() -> std::vector<AudioFormat>
|
|
{
|
|
return std::vector<AudioFormat>{getSourceFormat()};
|
|
}
|
|
|
|
auto Decoder::getTraits() const -> Endpoint::Traits
|
|
{
|
|
return Endpoint::Traits{};
|
|
}
|
|
} // namespace audio
|