From 8bd4a7218c70d92030932c2f0e77115f4e8f153d Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 27 Jul 2026 08:07:19 +0300 Subject: [PATCH] fix: true pause/resume with position preservation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SoundManager: pauseCurrentPcm()/resumeCurrentPcm() — stop/start without destroy - VoicePlayState: added paused/pausedPosSec fields - editorEvent: 3-state logic — playing→pause, paused→resume, stopped→new - Different file click: stops old with full cleanup, plays new from start - Timer: skips paused items, full cleanup on auto-complete --- tools/chatgui/src/messagedelegate.cpp | 80 ++++++++++++++++++++------- tools/chatgui/src/sound_manager.cpp | 8 +++ tools/chatgui/src/sound_manager.h | 2 + 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/tools/chatgui/src/messagedelegate.cpp b/tools/chatgui/src/messagedelegate.cpp index d6fd92cc..b9e128a5 100644 --- a/tools/chatgui/src/messagedelegate.cpp +++ b/tools/chatgui/src/messagedelegate.cpp @@ -25,8 +25,10 @@ MessageDelegate::MessageDelegate(QObject *parent) struct VoicePlayState { bool playing = false; + bool paused = false; qint64 startMs = 0; float durationSec = 0.0f; + float pausedPosSec = 0.0f; }; static QMap s_voiceStates; static QTimer* s_voiceTimer = nullptr; @@ -39,10 +41,13 @@ static void ensureVoiceTimer() { bool anyPlaying = false; qint64 now = QDateTime::currentMSecsSinceEpoch(); for (auto it = s_voiceStates.begin(); it != s_voiceStates.end(); ) { + if (!it->playing && !it->paused) { ++it; continue; } + if (it->paused) { ++it; continue; } if (!it->playing) { ++it; continue; } float elapsed = (now - it->startMs) / 1000.0f; if (elapsed >= it->durationSec && it->durationSec > 0) { it->playing = false; + it->paused = false; SoundManager::instance()->stopCurrentPcm(); ++it; } else { @@ -51,7 +56,6 @@ static void ensureVoiceTimer() { } } if (!anyPlaying) s_voiceTimer->stop(); - // Trigger repaint through any visible view if (auto* w = QApplication::activeWindow()) w->update(); }); } @@ -75,29 +79,57 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, ensureVoiceTimer(); VoicePlayState& st = s_voiceStates[filePath]; + bool isOtherPlaying = false; + for (auto it = s_voiceStates.begin(); it != s_voiceStates.end(); ++it) { + if (it.key() != filePath && (it->playing || it->paused)) + isOtherPlaying = true; + } if (st.playing) { + /* Playing → Pause */ + qint64 now = QDateTime::currentMSecsSinceEpoch(); + st.pausedPosSec = (now - st.startMs) / 1000.0f; st.playing = false; - SoundManager::instance()->stopCurrentPcm(); - model->setData(index, 0, MsgVoicePlayStateRole); - } else { - for (auto it = s_voiceStates.begin(); it != s_voiceStates.end(); ++it) - it->playing = false; - SoundManager::instance()->stopCurrentPcm(); - - QString durStr = index.data(MsgVoiceDurationRole).toString(); - durStr.remove('s'); - float dur = durStr.toFloat(); - if (dur <= 0) dur = 1.0f; + st.paused = true; + SoundManager::instance()->pauseCurrentPcm(); + model->setData(index, 2, MsgVoicePlayStateRole); + return true; + } + if (st.paused) { + /* Paused → Resume */ + st.paused = false; st.playing = true; - st.startMs = QDateTime::currentMSecsSinceEpoch(); - st.durationSec = dur; - - VoicePlayer::playOpusFile(filePath); + qint64 now = QDateTime::currentMSecsSinceEpoch(); + st.startMs = now - (qint64)(st.pausedPosSec * 1000.0); + SoundManager::instance()->resumeCurrentPcm(); s_voiceTimer->start(); model->setData(index, 1, MsgVoicePlayStateRole); + return true; } + + /* Stopped or different file → stop old, play new */ + if (isOtherPlaying) { + for (auto it = s_voiceStates.begin(); it != s_voiceStates.end(); ++it) { + it->playing = false; it->paused = false; + } + SoundManager::instance()->stopCurrentPcm(); + } + + QString durStr = index.data(MsgVoiceDurationRole).toString(); + durStr.remove('s'); + float dur = durStr.toFloat(); + if (dur <= 0) dur = 1.0f; + + st.playing = true; + st.paused = false; + st.pausedPosSec = 0; + st.startMs = QDateTime::currentMSecsSinceEpoch(); + st.durationSec = dur; + + VoicePlayer::playOpusFile(filePath); + s_voiceTimer->start(); + model->setData(index, 1, MsgVoicePlayStateRole); return true; } @@ -578,13 +610,14 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio } bool isPlaying = st.playing; + bool isPaused = st.paused; QRectF playR = L.voicePlayBtnRect; /* Play/Pause button */ painter->setPen(Qt::NoPen); painter->setBrush(QColor(0x33, 0x90, 0xEC)); - if (isPlaying) { + if (isPlaying || isPaused) { /* Pause icon (two vertical bars) */ qreal bw = playR.width() / 5.0; qreal gap = playR.width() / 8.0; @@ -617,10 +650,15 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio int pbTop = L.voicePlayBtnRect.bottom() + 6; int pbHeight = 3; - if (isPlaying && st.durationSec > 0) { - qint64 now = QDateTime::currentMSecsSinceEpoch(); - float elapsed = (now - st.startMs) / 1000.0f; - if (elapsed > st.durationSec) elapsed = st.durationSec; + if ((isPlaying || isPaused) && st.durationSec > 0) { + float elapsed; + if (isPaused) { + elapsed = st.pausedPosSec; + } else { + qint64 now = QDateTime::currentMSecsSinceEpoch(); + elapsed = (now - st.startMs) / 1000.0f; + if (elapsed > st.durationSec) elapsed = st.durationSec; + } float frac = elapsed / st.durationSec; painter->setPen(Qt::NoPen); diff --git a/tools/chatgui/src/sound_manager.cpp b/tools/chatgui/src/sound_manager.cpp index 25e45b28..ef6216e6 100644 --- a/tools/chatgui/src/sound_manager.cpp +++ b/tools/chatgui/src/sound_manager.cpp @@ -172,6 +172,14 @@ void SoundManager::playRawPcm(const int16_t* pcm, int sampleCount, int sampleRat ma_sound_start(m_currentPcmSound); } +void SoundManager::pauseCurrentPcm() { + if (m_currentPcmSound) ma_sound_stop(m_currentPcmSound); +} + +void SoundManager::resumeCurrentPcm() { + if (m_currentPcmSound) ma_sound_start(m_currentPcmSound); +} + void SoundManager::stopCurrentPcm() { if (m_currentPcmSound) { ma_sound_stop(m_currentPcmSound); diff --git a/tools/chatgui/src/sound_manager.h b/tools/chatgui/src/sound_manager.h index eb09f6db..54a59290 100644 --- a/tools/chatgui/src/sound_manager.h +++ b/tools/chatgui/src/sound_manager.h @@ -39,6 +39,8 @@ public: ma_context* context(); void playRawPcm(const int16_t* pcm, int sampleCount, int sampleRate, int channels); + void pauseCurrentPcm(); + void resumeCurrentPcm(); void stopCurrentPcm(); struct DeviceInfo {