Files
MuditaOS/module-audio/Audio/decoder/Decoder.cpp
Marcin Smoczyński 40bf381eca [EGD-5086] Fix voice not starting when calling
Due to a race condition between source and sink voice is not always
starting when calling. Introduce audio stream connections to avoid
race condition and improve handling of audio start and stop operations.

Signed-off-by: Marcin Smoczyński <smoczynski.marcin@gmail.com>
2021-01-14 11:03:47 +01:00

161 lines
4.6 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 <cstdio>
#include <Utils.hpp>
#include "Decoder.hpp"
#include "decoderMP3.hpp"
#include "decoderFLAC.hpp"
#include "decoderWAV.hpp"
#include "fileref.h"
#include "tag.h"
#include "tfilestream.h"
namespace audio
{
Decoder::Decoder(const char *fileName)
: filePath(fileName), workerBuffer(std::make_unique<int16_t[]>(workerBufferSize)), tag(std::make_unique<Tags>())
{
fd = std::fopen(fileName, "r");
if (fd == NULL) {
return;
}
std::fseek(fd, 0, SEEK_END);
fileSize = std::ftell(fd);
std::rewind(fd);
}
Decoder::~Decoder()
{
if (audioWorker) {
audioWorker->close();
}
if (fd) {
std::fclose(fd);
}
}
std::unique_ptr<Tags> Decoder::fetchTags()
{
if (fd) {
auto inPos = std::ftell(fd);
std::rewind(fd);
TagLib::FileStream fileStream(fd);
TagLib::FileRef tagReader(&fileStream);
if (!tagReader.isNull() && tagReader.tag()) {
TagLib::Tag *tags = tagReader.tag();
TagLib::AudioProperties *properties = tagReader.audioProperties();
tag->title = tags->title().to8Bit();
tag->artist = tags->artist().to8Bit();
tag->album = tags->album().to8Bit();
tag->genre = tags->genre().to8Bit();
tag->year = std::to_string(tags->year());
tag->total_duration_s = properties->length();
tag->duration_min = tag->total_duration_s / utils::secondsInMinute;
tag->duration_hour = tag->duration_min / utils::secondsInMinute;
tag->duration_sec = tag->total_duration_s % utils::secondsInMinute;
tag->sample_rate = properties->sampleRate();
tag->num_channel = properties->channels();
tag->bitrate = properties->bitrate();
}
std::rewind(fd);
fetchTagsSpecific();
std::fseek(fd, inPos, SEEK_SET);
}
tag->filePath.append(filePath);
// If title tag empty fill it with raw file name
if (tag->title.size() == 0) {
if (const auto pos = filePath.rfind("/"); pos == std::string::npos) {
tag->title.append(filePath);
}
else {
tag->title.append(&filePath[pos + 1]);
}
}
return std::make_unique<Tags>(*tag);
}
std::unique_ptr<Decoder> Decoder::Create(const char *file)
{
std::unique_ptr<Decoder> dec;
if ((strstr(file, ".wav") != NULL) || (strstr(file, ".WAV") != NULL)) {
dec = std::make_unique<decoderWAV>(file);
}
else if ((strstr(file, ".mp3") != NULL) || (strstr(file, ".MP3") != NULL)) {
dec = std::make_unique<decoderMP3>(file);
}
else if ((strstr(file, ".flac") != NULL) || (strstr(file, ".FLAC") != NULL)) {
dec = std::make_unique<decoderFLAC>(file);
}
else {
return nullptr;
}
if (!dec->isInitialized) {
return nullptr;
}
else {
return dec;
}
}
void Decoder::convertmono2stereo(int16_t *pcm, uint32_t samplecount)
{
uint32_t i = 0, j = 0;
memset(workerBuffer.get(), 0, workerBufferSize * sizeof(int16_t));
for (; j < samplecount; j++) {
workerBuffer[i++] = pcm[j];
workerBuffer[i++] = pcm[j];
}
memcpy(pcm, &workerBuffer[0], samplecount * 2 * sizeof(int16_t));
}
void Decoder::startDecodingWorker(DecoderWorker::EndOfFileCallback endOfFileCallback)
{
assert(_stream != nullptr);
if (!audioWorker) {
audioWorker = std::make_unique<DecoderWorker>(_stream, this, endOfFileCallback);
audioWorker->init();
audioWorker->run();
}
else {
LOG_DEBUG("AudioWorker already running.");
}
}
void Decoder::stopDecodingWorker()
{
if (audioWorker) {
audioWorker->close();
}
audioWorker = nullptr;
}
void Decoder::onDataReceive()
{
audioWorker->enablePlayback();
}
void Decoder::enableInput()
{
audioWorker->enablePlayback();
}
void Decoder::disableInput()
{
audioWorker->disablePlayback();
}
} // namespace audio