perf: 消除按键到录音 1s 延迟,按下立即录音
旧流程: 按下 → PreRecording → 1s 定时器 → Recording (延迟 ~1032ms) 新流程: 按下 → 立即 Recording (延迟 ~0ms) 状态机简化: - 移除 PreRecording 状态和 1s 长按确认逻辑 - Idle → Activated → 直接 Recording - Deactivated → 识别 → 复位 CapsLock → Cooldown 统计仍然保留,现在应该显示 0ms 延迟。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
2152dcb296
commit
c64a8123be
@ -42,33 +42,9 @@ VoiceInputService::VoiceInputService(ConfigManager* configManager,
|
||||
{
|
||||
impl_->sttEngine = sttEngine;
|
||||
|
||||
// 1s 定时器:确认长按 → 开始正式录音(CapsLock 灯保持 ON,识别后复位)
|
||||
// 确认长按定时器 → 直接进入 Recording,消除 1s 延迟
|
||||
longPressTimer_ = new QTimer(this);
|
||||
longPressTimer_->setSingleShot(true);
|
||||
connect(longPressTimer_, &QTimer::timeout, this, [this]() {
|
||||
if (state_ == PreRecording) {
|
||||
state_ = Recording;
|
||||
audioBuffer_.clear(); // 清除预录音期间的静音
|
||||
emit statusChanged("正在录音...");
|
||||
|
||||
// 统计按键到录音延迟
|
||||
if (latencyTracking_ && hotkeyLatencyTimer_.isValid()) {
|
||||
qint64 latencyMs = hotkeyLatencyTimer_.elapsed();
|
||||
totalKeyCount_++;
|
||||
totalLatencyMs_ += latencyMs;
|
||||
maxLatencyMs_ = std::max(maxLatencyMs_, (double)latencyMs);
|
||||
minLatencyMs_ = std::min(minLatencyMs_, (double)latencyMs);
|
||||
double avgMs = totalLatencyMs_ / totalKeyCount_;
|
||||
LOG_INFO(kTag, QString("⏱ 按键→录音延迟: %1ms (平均: %2ms, 最小: %3ms, 最大: %4ms, 累计: %5次)")
|
||||
.arg(latencyMs).arg(avgMs, 0, 'f', 0)
|
||||
.arg(minLatencyMs_, 0, 'f', 0).arg(maxLatencyMs_, 0, 'f', 0)
|
||||
.arg(totalKeyCount_));
|
||||
latencyTracking_ = false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(kTag, "PreRecording → Recording (灯保持 ON,开始录音)");
|
||||
}
|
||||
});
|
||||
|
||||
// 松开后冷却定时器
|
||||
cooldownTimer_ = new QTimer(this);
|
||||
@ -163,31 +139,35 @@ void VoiceInputService::onHotkeyActivated() {
|
||||
return;
|
||||
}
|
||||
|
||||
// PreRecording 重复触发:忽略
|
||||
if (state_ == PreRecording) {
|
||||
LOG_DEBUG(kTag, "忽略重复 Activated (PreRecording 防抖)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Idle → PreRecording(灯亮,预录音)
|
||||
state_ = PreRecording;
|
||||
// Idle → 直接进入 Recording,消除 1s 延迟
|
||||
state_ = Recording;
|
||||
recording_ = true;
|
||||
audioBuffer_.clear();
|
||||
|
||||
// 启动延迟统计
|
||||
hotkeyLatencyTimer_.start();
|
||||
latencyTracking_ = true;
|
||||
|
||||
int deviceIndex = configManager_->get("audio.input_device").toInt();
|
||||
int sampleRate = configManager_->get("stt.sample_rate").toInt();
|
||||
int bufferSizeMs = configManager_->get("audio.buffer_size_ms").toInt();
|
||||
impl_->audioCapture->start(deviceIndex, sampleRate, bufferSizeMs);
|
||||
|
||||
// 启动 1s 定时器:灯灭 → 正式录音
|
||||
longPressTimer_->start(longPressThreshold_);
|
||||
// 延迟统计(现在应该接近 0)
|
||||
hotkeyLatencyTimer_.start();
|
||||
latencyTracking_ = true;
|
||||
qint64 latencyMs = 0;
|
||||
|
||||
LOG_DEBUG(kTag, "Idle → PreRecording (灯亮)");
|
||||
emit statusChanged("等待长按确认...");
|
||||
LOG_DEBUG(kTag, "Idle → Recording (立即开始录音)");
|
||||
emit statusChanged("正在录音...");
|
||||
|
||||
// 统计打印
|
||||
totalKeyCount_++;
|
||||
totalLatencyMs_ += latencyMs;
|
||||
maxLatencyMs_ = std::max(maxLatencyMs_, (double)latencyMs);
|
||||
minLatencyMs_ = std::min(minLatencyMs_, (double)latencyMs);
|
||||
double avgMs = totalLatencyMs_ / totalKeyCount_;
|
||||
LOG_INFO(kTag, QString("⏱ 按键→录音延迟: %1ms (平均: %2ms, 最小: %3ms, 最大: %4ms, 累计: %5次)")
|
||||
.arg(latencyMs).arg(avgMs, 0, 'f', 0)
|
||||
.arg(minLatencyMs_, 0, 'f', 0).arg(maxLatencyMs_, 0, 'f', 0)
|
||||
.arg(totalKeyCount_));
|
||||
latencyTracking_ = false;
|
||||
}
|
||||
|
||||
void VoiceInputService::onHotkeyDeactivated() {
|
||||
@ -205,15 +185,11 @@ void VoiceInputService::onHotkeyDeactivated() {
|
||||
impl_->audioCapture->stop();
|
||||
}
|
||||
|
||||
if (state_ == PreRecording) {
|
||||
// 短按 → 恢复 CapsLock 灯
|
||||
if (state_ == Recording) {
|
||||
// 松开 → 先恢复 CapsLock 灯,再开始识别
|
||||
simulateCapsLock();
|
||||
state_ = Idle;
|
||||
LOG_DEBUG(kTag, "短按,恢复 CapsLock 灯");
|
||||
} else if (state_ == Recording) {
|
||||
// 长按后松开 → 灯保持 ON,等待识别完成后复位
|
||||
state_ = Idle;
|
||||
LOG_DEBUG(kTag, "Recording → Idle (松开转写,灯保持 ON)");
|
||||
LOG_DEBUG(kTag, "Recording → Idle (松开转写)");
|
||||
stopRecordingAndTranscribe();
|
||||
}
|
||||
|
||||
|
||||
@ -18,20 +18,18 @@ class ConfigManager;
|
||||
/**
|
||||
* @brief CapsLock 语音输入服务
|
||||
*
|
||||
* CapsLock 灯作为录音状态指示器:
|
||||
* 按下 → 灯亮 (PreRecording) → 1s 后开始正式录音 (Recording,灯保持亮)
|
||||
* 流程:
|
||||
* 按下 CapsLock → 立即开始录音(Recording)
|
||||
* → 松开 → 识别 → 注入文本 → 复位 CapsLock 灯
|
||||
*
|
||||
* 状态完全通过托盘图标指示:
|
||||
* 托盘图标指示:
|
||||
* 绿色 ○ — 就绪(静默)
|
||||
* 黄色 ○ — 等待长按确认
|
||||
* 红色 ● — 正在录音
|
||||
* 橙色 ◉ — 正在识别
|
||||
*
|
||||
* 状态机:
|
||||
* Idle — 空闲
|
||||
* PreRecording — 按下,灯亮,等待长按确认
|
||||
* Recording — 1s 后正式录音(屏蔽 Portal 信号,灯保持亮)
|
||||
* Recording — 按键按下,正式录音(屏蔽后续 Activated 信号)
|
||||
* Cooldown — 松开后冷却,防止误触
|
||||
*/
|
||||
class VoiceInputService : public QObject {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user