You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
91 lines
3.3 KiB
91 lines
3.3 KiB
#include "voiceplayback.h" |
|
#include "sound_manager.h" |
|
|
|
extern "C" { |
|
#include "../../../lib/opus_codec.h" |
|
} |
|
|
|
#include "../../lib/debug_config.h" |
|
|
|
#include <QFile> |
|
#include <QDebug> |
|
|
|
static const uint32_t OPUS_MAGIC = 0x5355504F; |
|
|
|
int VoicePlayer::decodeOpusFile(const QString& filePath, std::vector<int16_t>& outPcm, |
|
int& outSampleRate, int& outChannels, float& outDurationSec) { |
|
QFile file(filePath); |
|
if (!file.exists()) { |
|
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer: file not found: %s", qPrintable(filePath)); |
|
return -1; |
|
} |
|
if (!file.open(QIODevice::ReadOnly)) { |
|
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer: cannot open %s (%s)", qPrintable(filePath), qPrintable(file.errorString())); |
|
return -1; |
|
} |
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoicePlayer: opened file %s size=%lld", qPrintable(filePath), (long long)file.size()); |
|
|
|
uint32_t magic, sr32; |
|
uint16_t ch16, frameSamples; |
|
if (file.read((char*)&magic, 4) != 4 || magic != OPUS_MAGIC || |
|
file.read((char*)&sr32, 4) != 4 || |
|
file.read((char*)&ch16, 2) != 2 || |
|
file.read((char*)&frameSamples, 2) != 2) { |
|
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer: invalid header in %s", qPrintable(filePath)); |
|
return -1; |
|
} |
|
|
|
int sampleRate = (int)sr32; |
|
int channels = (int)ch16; |
|
outSampleRate = sampleRate; |
|
outChannels = channels; |
|
|
|
opus_codec_decoder_t* dec = opus_codec_decoder_create(sampleRate, channels); |
|
if (!dec) { |
|
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer: decoder create failed"); |
|
return -1; |
|
} |
|
|
|
outPcm.clear(); |
|
int frameCount = 0; |
|
|
|
while (!file.atEnd()) { |
|
uint16_t plen; |
|
if (file.read((char*)&plen, 2) != 2) break; |
|
if (plen == 0) break; /* end marker */ |
|
|
|
QByteArray pktData = file.read(plen); |
|
if (pktData.size() != (int)plen) break; |
|
|
|
std::vector<int16_t> frameBuf(frameSamples * channels); |
|
int decoded = opus_codec_decode(dec, (const uint8_t*)pktData.constData(), |
|
plen, frameBuf.data(), frameSamples); |
|
if (decoded > 0) { |
|
size_t cur = outPcm.size(); |
|
outPcm.resize(cur + decoded * channels); |
|
memcpy(outPcm.data() + cur, frameBuf.data(), decoded * channels * sizeof(int16_t)); |
|
frameCount++; |
|
} |
|
} |
|
|
|
outDurationSec = (float)frameCount * 20.0f / 1000.0f; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoicePlayer: decoded %d frames (%.1fs) from %s", frameCount, outDurationSec, qPrintable(filePath)); |
|
opus_codec_decoder_destroy(dec); |
|
return frameCount; |
|
} |
|
|
|
void VoicePlayer::playOpusFile(const QString& filePath) { |
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoicePlayer::playOpusFile: %s", qPrintable(filePath)); |
|
std::vector<int16_t> pcm; |
|
int sampleRate, channels; |
|
float duration; |
|
|
|
if (decodeOpusFile(filePath, pcm, sampleRate, channels, duration) <= 0) { |
|
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer::playOpusFile: decode failed for %s", qPrintable(filePath)); |
|
return; |
|
} |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoicePlayer::playOpusFile: decoded %zu samples %dHz %dch %.1fs", |
|
pcm.size(), sampleRate, channels, duration); |
|
SoundManager::instance()->playRawPcm(pcm.data(), pcm.size() / channels, sampleRate, channels); |
|
}
|
|
|