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
This commit is contained in:
1 parent
830f729f52
commit
8bd4a7218c
3 files changed
+69
-21
No files matched your search
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user