fix: 长按 1s 录音 + 3s 复位 CapsLock 灯 + 防抖保护
- 分离两个定时器:longPressTimer(1s) 启动录音,capsResetTimer(3s) 复位灯 - 1s 时只确认录音状态,不操作 CapsLock 灯 - 3s 时才模拟 CapsLock 按键复位灯,避免手指抖动导致状态闪烁 - 松开时自动取消 3s 定时器,避免松开后再复位 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d4db8756b5
commit
13d4aae725
@ -39,19 +39,29 @@ VoiceInputService::VoiceInputService(ConfigManager* configManager,
|
|||||||
, impl_(std::make_unique<Impl>())
|
, impl_(std::make_unique<Impl>())
|
||||||
{
|
{
|
||||||
impl_->sttEngine = sttEngine;
|
impl_->sttEngine = sttEngine;
|
||||||
|
|
||||||
|
// 1s 定时器:启动录音
|
||||||
longPressTimer_ = new QTimer(this);
|
longPressTimer_ = new QTimer(this);
|
||||||
longPressTimer_->setSingleShot(true);
|
longPressTimer_->setSingleShot(true);
|
||||||
connect(longPressTimer_, &QTimer::timeout, this, [this]() {
|
connect(longPressTimer_, &QTimer::timeout, this, [this]() {
|
||||||
// 长按超时仍未松开 → 确认为长按录音
|
|
||||||
if (!longPressDetected_) {
|
if (!longPressDetected_) {
|
||||||
longPressDetected_ = true;
|
longPressDetected_ = true;
|
||||||
capsResetDone_ = true;
|
|
||||||
// 立即复位 CapsLock,不等用户松开
|
|
||||||
simulateCapsLock();
|
|
||||||
emit statusChanged("正在录音...");
|
emit statusChanged("正在录音...");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 3s 定时器:复位 CapsLock 灯(防抖:只在未按下状态下执行)
|
||||||
|
capsResetTimer_ = new QTimer(this);
|
||||||
|
capsResetTimer_->setSingleShot(true);
|
||||||
|
connect(capsResetTimer_, &QTimer::timeout, this, [this]() {
|
||||||
|
// 如果已经松开(recording_ = false),不执行
|
||||||
|
if (recording_) {
|
||||||
|
capsResetDone_ = true;
|
||||||
|
simulateCapsLock();
|
||||||
|
LOG_DEBUG(kTag, "CapsLock 灯已复位(防抖保护中)");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 松开后的冷却定时器
|
// 松开后的冷却定时器
|
||||||
cooldownTimer_ = new QTimer(this);
|
cooldownTimer_ = new QTimer(this);
|
||||||
cooldownTimer_->setSingleShot(true);
|
cooldownTimer_->setSingleShot(true);
|
||||||
@ -111,6 +121,7 @@ void VoiceInputService::stop() {
|
|||||||
if (!running_) return;
|
if (!running_) return;
|
||||||
|
|
||||||
longPressTimer_->stop();
|
longPressTimer_->stop();
|
||||||
|
capsResetTimer_->stop();
|
||||||
cooldownTimer_->stop();
|
cooldownTimer_->stop();
|
||||||
|
|
||||||
if (impl_->audioCapture) {
|
if (impl_->audioCapture) {
|
||||||
@ -146,10 +157,12 @@ void VoiceInputService::onHotkeyActivated() {
|
|||||||
LOG_DEBUG(kTag, "快捷键激活(按下)");
|
LOG_DEBUG(kTag, "快捷键激活(按下)");
|
||||||
recording_ = true;
|
recording_ = true;
|
||||||
longPressDetected_ = false;
|
longPressDetected_ = false;
|
||||||
|
capsResetDone_ = false;
|
||||||
audioBuffer_.clear();
|
audioBuffer_.clear();
|
||||||
|
|
||||||
// 启动长按定时器
|
// 启动 1s 录音确认定时器 + 3s CapsLock 灯复位定时器
|
||||||
longPressTimer_->start(longPressThreshold_);
|
longPressTimer_->start(longPressThreshold_);
|
||||||
|
capsResetTimer_->start(capsResetDelayMs_);
|
||||||
|
|
||||||
// 开始音频采集(后台预采集)
|
// 开始音频采集(后台预采集)
|
||||||
int deviceIndex = configManager_->get("audio.input_device").toInt();
|
int deviceIndex = configManager_->get("audio.input_device").toInt();
|
||||||
@ -164,6 +177,7 @@ void VoiceInputService::onHotkeyDeactivated() {
|
|||||||
LOG_DEBUG(kTag, "快捷键停用(松开)");
|
LOG_DEBUG(kTag, "快捷键停用(松开)");
|
||||||
recording_ = false;
|
recording_ = false;
|
||||||
longPressTimer_->stop();
|
longPressTimer_->stop();
|
||||||
|
capsResetTimer_->stop();
|
||||||
|
|
||||||
// 停止音频采集
|
// 停止音频采集
|
||||||
if (impl_->audioCapture && impl_->audioCapture->isRunning()) {
|
if (impl_->audioCapture && impl_->audioCapture->isRunning()) {
|
||||||
@ -176,7 +190,7 @@ void VoiceInputService::onHotkeyDeactivated() {
|
|||||||
simulateCapsLock();
|
simulateCapsLock();
|
||||||
emit statusChanged("短按:切换 CapsLock");
|
emit statusChanged("短按:切换 CapsLock");
|
||||||
} else {
|
} else {
|
||||||
// 长按 → CapsLock 已在长按阈值时复位,松开后直接开始识别
|
// 长按 → CapsLock 已在 3s 定时器时复位,松开后直接开始识别
|
||||||
stopRecordingAndTranscribe();
|
stopRecordingAndTranscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -69,14 +69,16 @@ private:
|
|||||||
bool longPressDetected_ = false;
|
bool longPressDetected_ = false;
|
||||||
bool capsResetDone_ = false; // CapsLock 复位后忽略重复 Activated
|
bool capsResetDone_ = false; // CapsLock 复位后忽略重复 Activated
|
||||||
bool cooldownActive_ = false; // 松开后的冷却期,防止立即重新触发
|
bool cooldownActive_ = false; // 松开后的冷却期,防止立即重新触发
|
||||||
int longPressThreshold_ = 1000;
|
int longPressThreshold_ = 1000; // 1s 启动录音
|
||||||
|
int capsResetDelayMs_ = 3000; // 3s 后复位 CapsLock 灯
|
||||||
int releaseCooldownMs_ = 1000; // 松开后冷却时间
|
int releaseCooldownMs_ = 1000; // 松开后冷却时间
|
||||||
|
|
||||||
std::vector<float> audioBuffer_;
|
std::vector<float> audioBuffer_;
|
||||||
int audioSampleRate_ = 16000;
|
int audioSampleRate_ = 16000;
|
||||||
|
|
||||||
QTimer* longPressTimer_ = nullptr;
|
QTimer* longPressTimer_ = nullptr; // 1s 启动录音
|
||||||
QTimer* cooldownTimer_ = nullptr;
|
QTimer* capsResetTimer_ = nullptr; // 3s 复位 CapsLock 灯
|
||||||
|
QTimer* cooldownTimer_ = nullptr; // 松开后冷却
|
||||||
|
|
||||||
void startRecording();
|
void startRecording();
|
||||||
void stopRecordingAndTranscribe();
|
void stopRecordingAndTranscribe();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user