Browse Source

Decode voice files asynchronously before starting Qt playback

master
evgeny 2 weeks ago
parent
commit
d4a27d1975
  1. 54
      tools/chatgui/src/voiceplayback.cpp
  2. 5
      tools/chatgui/src/voiceplayback.h

54
tools/chatgui/src/voiceplayback.cpp

@ -10,6 +10,10 @@ extern "C" {
#include <QFile>
#include <QDebug>
#include <QThread>
#include <QPointer>
#include <QCoreApplication>
#include <memory>
static const uint32_t OPUS_MAGIC = 0x5355504F;
@ -78,21 +82,41 @@ int VoicePlayer::decodeOpusFile(const QString& filePath, std::vector<int16_t>& o
return frameCount;
}
float 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 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;
static unsigned s_decodeGeneration = 0; /* Только GUI-поток. */
static bool s_decoding = false;
void VoicePlayer::cancelPending() { s_decodeGeneration++; }
/* Декодирование и выделение PCM выполняются в worker; GUI принимает готовый буфер. */
void VoicePlayer::playOpusFile(const QString& filePath, QObject* context, std::function<void(float)> ready) {
if (s_decoding) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "voice: decoder busy"); ready(0); return; }
struct Result {
std::shared_ptr<std::vector<int16_t>> pcm = std::make_shared<std::vector<int16_t>>();
int rate = 0, channels = 0;
float duration = 0;
};
auto result = std::make_shared<Result>();
unsigned generation = ++s_decodeGeneration;
s_decoding = true;
QPointer<QObject> receiver(context);
auto* thread = QThread::create([filePath, result]() {
try {
if (decodeOpusFile(filePath, *result->pcm, result->rate, result->channels, result->duration) <= 0)
result->duration = 0;
} catch (const std::exception& error) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "voice: decode failed path=%s reason=%s", qPrintable(filePath), error.what());
result->duration = 0;
}
});
QObject::connect(thread, &QThread::finished, QCoreApplication::instance(), [result, receiver, ready, generation]() {
s_decoding = false;
if (!receiver || generation != s_decodeGeneration) return;
if (result->duration > 0) SoundManager::instance()->playRawPcm(result->pcm->data(),
(int)(result->pcm->size() / result->channels), result->rate, result->channels, result->pcm);
ready(result->duration);
});
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();
}
float VoicePlayer::playOpusFromReader(MediaBlockReader& reader) {

5
tools/chatgui/src/voiceplayback.h

@ -3,12 +3,15 @@
#include <QString>
#include <vector>
#include <cstdint>
#include <functional>
class QObject;
class MediaBlockReader;
class VoicePlayer {
public:
static float playOpusFile(const QString& filePath);
static void playOpusFile(const QString& filePath, QObject* context, std::function<void(float)> ready);
static void cancelPending();
static int decodeOpusFile(const QString& filePath, std::vector<int16_t>& outPcm,
int& outSampleRate, int& outChannels, float& outDurationSec);
static float playOpusFromReader(MediaBlockReader& reader);

Loading…
Cancel
Save