Browse Source

feat: voice playback delay compensation + waveform click-to-seek

topo_upd
Evgeny 2 months ago
parent
commit
6b765de5cc
  1. 60
      tools/chatgui/src/messagedelegate.cpp
  2. 6
      tools/chatgui/src/sound_manager.cpp
  3. 3
      tools/chatgui/src/sound_manager.h

60
tools/chatgui/src/messagedelegate.cpp

@ -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;

6
tools/chatgui/src/sound_manager.cpp

@ -153,6 +153,7 @@ void SoundManager::playRawPcm(const int16_t* pcm, int sampleCount, int sampleRat
return;
}
if (m_currentPcmSound) ma_sound_stop(m_currentPcmSound);
m_currentPcmSampleRate = sampleRate;
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "SoundManager::playRawPcm: %d samples %dHz %dch", sampleCount, sampleRate, channels);
ma_audio_buffer_config bufCfg = ma_audio_buffer_config_init(
@ -189,6 +190,11 @@ void SoundManager::resumeCurrentPcm() {
void SoundManager::stopCurrentPcm() {
if (m_currentPcmSound) ma_sound_stop(m_currentPcmSound);
m_currentPcmSampleRate = 0;
}
void SoundManager::seekCurrentPcm(unsigned long long pcmFrame) {
if (m_currentPcmSound) ma_sound_seek_to_pcm_frame(m_currentPcmSound, pcmFrame);
}
static QString decodeDeviceName(const char* name) {

3
tools/chatgui/src/sound_manager.h

@ -42,6 +42,8 @@ public:
void pauseCurrentPcm();
void resumeCurrentPcm();
void stopCurrentPcm();
void seekCurrentPcm(unsigned long long pcmFrame);
int currentPcmSampleRate() const { return m_currentPcmSampleRate; }
struct DeviceInfo {
QString name;
@ -64,6 +66,7 @@ private:
ma_context* m_context = nullptr;
ma_sound* m_currentPcmSound = nullptr;
void* m_currentPcmBuffer = nullptr;
int m_currentPcmSampleRate = 0;
bool m_initialized = false;
bool m_enabled = true;
float m_volume = 0.8f;

Loading…
Cancel
Save