#include "voiceplayback.h" #include "sound_manager.h" #include "media_blocks.h" extern "C" { #include "../../../lib/opus_codec.h" } #include "../../lib/debug_config.h" #include #include static const uint32_t OPUS_MAGIC = 0x5355504F; int VoicePlayer::decodeOpusFile(const QString& filePath, std::vector& 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 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; } float VoicePlayer::playOpusFile(const QString& filePath) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoicePlayer::playOpusFile: %s", qPrintable(filePath)); std::vector 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 0.0f; } 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); return duration; } float VoicePlayer::playOpusFromReader(MediaBlockReader& reader) { DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoicePlayer::playOpusFromReader: %s", qPrintable(reader.debugPath())); std::vector pcm; int sampleRate, channels; float duration; if (decodeOpusFromReader(reader, pcm, sampleRate, channels, duration) <= 0) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer::playOpusFromReader: decode failed"); return 0.0f; } DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "VoicePlayer::playOpusFromReader: decoded %zu samples %dHz %dch %.1fs", pcm.size(), sampleRate, channels, duration); SoundManager::instance()->playRawPcm(pcm.data(), pcm.size() / channels, sampleRate, channels); return duration; } int VoicePlayer::decodeOpusFromReader(MediaBlockReader& reader, std::vector& outPcm, int& outSampleRate, int& outChannels, float& outDurationSec) { QByteArray data = reader.readAll(); if (data.isEmpty()) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer::decodeOpusFromReader: empty data"); return -1; } const uint8_t* ptr = (const uint8_t*)data.constData(); size_t len = (size_t)data.size(); size_t off = 0; if (off + 12 > len) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer: header truncated"); return -1; } uint32_t magic; memcpy(&magic, ptr + off, 4); off += 4; if (magic != OPUS_MAGIC) { DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "VoicePlayer: bad magic %08x", magic); return -1; } uint32_t sr32; memcpy(&sr32, ptr + off, 4); off += 4; uint16_t ch16; memcpy(&ch16, ptr + off, 2); off += 2; uint16_t frameSamples; memcpy(&frameSamples, ptr + off, 2); off += 2; int sampleRate = (int)sr32, 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 (off + 2 <= len) { uint16_t plen; memcpy(&plen, ptr + off, 2); off += 2; if (plen == 0) break; if (off + plen > len) break; std::vector frameBuf(frameSamples * channels); int decoded = opus_codec_decode(dec, ptr + off, plen, frameBuf.data(), frameSamples); off += plen; 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)", frameCount, outDurationSec); opus_codec_decoder_destroy(dec); return frameCount; }