Browse Source

fix: true pause/resume with position preservation

- 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
topo_upd
Evgeny 2 months ago
parent
commit
8bd4a7218c
  1. 80
      tools/chatgui/src/messagedelegate.cpp
  2. 8
      tools/chatgui/src/sound_manager.cpp
  3. 2
      tools/chatgui/src/sound_manager.h

80
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<QString, VoicePlayState> 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);

8
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);

2
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 {

Loading…
Cancel
Save