feat: voice playback delay compensation + waveform click-to-seek
This commit is contained in:
1 parent
da953c6c7a
commit
6b765de5cc
3 files changed
+60
-9
No files matched your search
@@ -71,15 +71,58 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
||||
|
||||
QMouseEvent* me = static_cast<QMouseEvent*>(event);
|
||||
QPoint localPos = me->pos() - option.rect.topLeft();
|
||||
if (!L.voicePlayBtnRect.contains(localPos)) return false;
|
||||
|
||||
bool onPlayBtn = L.voicePlayBtnRect.contains(localPos);
|
||||
bool onWaveform = L.voiceWaveformRect.contains(localPos);
|
||||
if (!onPlayBtn && !onWaveform) return false;
|
||||
|
||||
QString filePath = index.data(MsgVoiceFileRole).toString();
|
||||
GUI_DEBUG("editorEvent voice click: filePath=%s exists=%d",
|
||||
qPrintable(filePath), QFile::exists(filePath));
|
||||
GUI_DEBUG("editorEvent voice click: filePath=%s exists=%d wave=%d",
|
||||
qPrintable(filePath), QFile::exists(filePath), (int)onWaveform);
|
||||
if (filePath.isEmpty()) return false;
|
||||
|
||||
ensureVoiceTimer();
|
||||
VoicePlayState& st = s_voiceStates[filePath];
|
||||
|
||||
QString durStr = index.data(MsgVoiceDurationRole).toString();
|
||||
durStr.remove('s');
|
||||
float dur = durStr.toFloat();
|
||||
if (dur <= 0) dur = 1.0f;
|
||||
|
||||
if (onWaveform) {
|
||||
float clickFrac = (float)(localPos.x() - L.voiceWaveformRect.x()) / L.voiceWaveformRect.width();
|
||||
if (clickFrac < 0) clickFrac = 0; if (clickFrac > 1) clickFrac = 1;
|
||||
float targetSec = clickFrac * dur;
|
||||
qint64 now = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
if (!st.playing && !st.paused) {
|
||||
/* Start playback then seek */
|
||||
st.playing = true;
|
||||
st.paused = false;
|
||||
st.durationSec = dur;
|
||||
st.startMs = now - (qint64)(targetSec * 1000.0);
|
||||
st.pausedPosSec = targetSec;
|
||||
VoicePlayer::playOpusFile(filePath);
|
||||
int sr = SoundManager::instance()->currentPcmSampleRate();
|
||||
if (sr > 0)
|
||||
SoundManager::instance()->seekCurrentPcm((unsigned long long)(targetSec * sr));
|
||||
s_voiceTimer->start();
|
||||
model->setData(index, 1, MsgVoicePlayStateRole);
|
||||
} else {
|
||||
/* Currently playing or paused — seek */
|
||||
st.startMs = now - (qint64)(targetSec * 1000.0);
|
||||
if (st.paused)
|
||||
st.pausedPosSec = targetSec;
|
||||
else if (!st.playing)
|
||||
{ st.playing = true; s_voiceTimer->start(); }
|
||||
int sr = SoundManager::instance()->currentPcmSampleRate();
|
||||
if (sr > 0)
|
||||
SoundManager::instance()->seekCurrentPcm((unsigned long long)(targetSec * sr));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --- Play button click (original logic) --- */
|
||||
bool isOtherPlaying = false;
|
||||
for (auto it = s_voiceStates.begin(); it != s_voiceStates.end(); ++it) {
|
||||
if (it.key() != filePath && (it->playing || it->paused))
|
||||
@@ -117,11 +160,6 @@ bool MessageDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
||||
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;
|
||||
@@ -633,7 +671,11 @@ void MessageDelegate::paint(QPainter *painter, const QStyleOptionViewItem &optio
|
||||
elapsed = (now - st.startMs) / 1000.0f;
|
||||
if (elapsed > durSec) elapsed = durSec;
|
||||
}
|
||||
if (durSec > 0) frac = elapsed / durSec;
|
||||
if (durSec > 0) {
|
||||
float dispElapsed = elapsed - 0.2f; /* 200ms audio output delay */
|
||||
if (dispElapsed < 0) dispElapsed = 0;
|
||||
frac = dispElapsed / durSec;
|
||||
}
|
||||
if (frac < 0) frac = 0; if (frac > 1) frac = 1;
|
||||
|
||||
QRectF playR = L.voicePlayBtnRect;
|
||||
|
||||
Reference in new issue
Block a user