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;
|
impl_->sttEngine = sttEngine;
|
||||||
|
|
||||||
// 1s 定时器:确认长按 → 开始正式录音(CapsLock 灯保持 ON,识别后复位)
|
// 确认长按定时器 → 直接进入 Recording,消除 1s 延迟
|
||||||
longPressTimer_ = new QTimer(this);
|
longPressTimer_ = new QTimer(this);
|
||||||
longPressTimer_->setSingleShot(true);
|
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);
|
cooldownTimer_ = new QTimer(this);
|
||||||
@ -163,31 +139,35 @@ void VoiceInputService::onHotkeyActivated() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PreRecording 重复触发:忽略
|
// Idle → 直接进入 Recording,消除 1s 延迟
|
||||||
if (state_ == PreRecording) {
|
state_ = Recording;
|
||||||
LOG_DEBUG(kTag, "忽略重复 Activated (PreRecording 防抖)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Idle → PreRecording(灯亮,预录音)
|
|
||||||
state_ = PreRecording;
|
|
||||||
recording_ = true;
|
recording_ = true;
|
||||||
audioBuffer_.clear();
|
audioBuffer_.clear();
|
||||||
|
|
||||||
// 启动延迟统计
|
|
||||||
hotkeyLatencyTimer_.start();
|
|
||||||
latencyTracking_ = true;
|
|
||||||
|
|
||||||
int deviceIndex = configManager_->get("audio.input_device").toInt();
|
int deviceIndex = configManager_->get("audio.input_device").toInt();
|
||||||
int sampleRate = configManager_->get("stt.sample_rate").toInt();
|
int sampleRate = configManager_->get("stt.sample_rate").toInt();
|
||||||
int bufferSizeMs = configManager_->get("audio.buffer_size_ms").toInt();
|
int bufferSizeMs = configManager_->get("audio.buffer_size_ms").toInt();
|
||||||
impl_->audioCapture->start(deviceIndex, sampleRate, bufferSizeMs);
|
impl_->audioCapture->start(deviceIndex, sampleRate, bufferSizeMs);
|
||||||
|
|
||||||
// 启动 1s 定时器:灯灭 → 正式录音
|
// 延迟统计(现在应该接近 0)
|
||||||
longPressTimer_->start(longPressThreshold_);
|
hotkeyLatencyTimer_.start();
|
||||||
|
latencyTracking_ = true;
|
||||||
|
qint64 latencyMs = 0;
|
||||||
|
|
||||||
LOG_DEBUG(kTag, "Idle → PreRecording (灯亮)");
|
LOG_DEBUG(kTag, "Idle → Recording (立即开始录音)");
|
||||||
emit statusChanged("等待长按确认...");
|
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() {
|
void VoiceInputService::onHotkeyDeactivated() {
|
||||||
@ -205,15 +185,11 @@ void VoiceInputService::onHotkeyDeactivated() {
|
|||||||
impl_->audioCapture->stop();
|
impl_->audioCapture->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state_ == PreRecording) {
|
if (state_ == Recording) {
|
||||||
// 短按 → 恢复 CapsLock 灯
|
// 松开 → 先恢复 CapsLock 灯,再开始识别
|
||||||
simulateCapsLock();
|
simulateCapsLock();
|
||||||
state_ = Idle;
|
state_ = Idle;
|
||||||
LOG_DEBUG(kTag, "短按,恢复 CapsLock 灯");
|
LOG_DEBUG(kTag, "Recording → Idle (松开转写)");
|
||||||
} else if (state_ == Recording) {
|
|
||||||
// 长按后松开 → 灯保持 ON,等待识别完成后复位
|
|
||||||
state_ = Idle;
|
|
||||||
LOG_DEBUG(kTag, "Recording → Idle (松开转写,灯保持 ON)");
|
|
||||||
stopRecordingAndTranscribe();
|
stopRecordingAndTranscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,20 +18,18 @@ class ConfigManager;
|
|||||||
/**
|
/**
|
||||||
* @brief CapsLock 语音输入服务
|
* @brief CapsLock 语音输入服务
|
||||||
*
|
*
|
||||||
* CapsLock 灯作为录音状态指示器:
|
* 流程:
|
||||||
* 按下 → 灯亮 (PreRecording) → 1s 后开始正式录音 (Recording,灯保持亮)
|
* 按下 CapsLock → 立即开始录音(Recording)
|
||||||
* → 松开 → 识别 → 注入文本 → 复位 CapsLock 灯
|
* → 松开 → 识别 → 注入文本 → 复位 CapsLock 灯
|
||||||
*
|
*
|
||||||
* 状态完全通过托盘图标指示:
|
* 托盘图标指示:
|
||||||
* 绿色 ○ — 就绪(静默)
|
* 绿色 ○ — 就绪(静默)
|
||||||
* 黄色 ○ — 等待长按确认
|
|
||||||
* 红色 ● — 正在录音
|
* 红色 ● — 正在录音
|
||||||
* 橙色 ◉ — 正在识别
|
* 橙色 ◉ — 正在识别
|
||||||
*
|
*
|
||||||
* 状态机:
|
* 状态机:
|
||||||
* Idle — 空闲
|
* Idle — 空闲
|
||||||
* PreRecording — 按下,灯亮,等待长按确认
|
* Recording — 按键按下,正式录音(屏蔽后续 Activated 信号)
|
||||||
* Recording — 1s 后正式录音(屏蔽 Portal 信号,灯保持亮)
|
|
||||||
* Cooldown — 松开后冷却,防止误触
|
* Cooldown — 松开后冷却,防止误触
|
||||||
*/
|
*/
|
||||||
class VoiceInputService : public QObject {
|
class VoiceInputService : public QObject {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user