feat: 集成语音输入服务到主窗口和设置页面
主窗口新增 VoiceInputService 生命周期管理,支持通过配置页面 的开关动态启停 CapsLock 语音输入功能,窗口关闭时自动清理。 设置页面新增两个复选框: - 调试录音:保存每次识别的原始音频到临时文件夹 - 快捷语音:启用 CapsLock 长按语音输入 转写页面和 STT 测试页面同步配置中的调试音频开关到引擎。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
cda68e5376
commit
baec3482a7
@ -174,6 +174,10 @@ void FileTranscribePage::onStartTranscribe() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从配置同步调试开关到引擎
|
||||
sttEngine_->setDebugSaveAudio(
|
||||
configManager_->get("stt.debug_save_audio").toBool());
|
||||
|
||||
isTranscribing_ = true;
|
||||
currentTaskIndex_ = 0;
|
||||
progressBar_->setVisible(true);
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include "stt_test_page.h"
|
||||
#include "file_transcribe_page.h"
|
||||
#include "settings_page.h"
|
||||
#include "core/voice_input_service.h"
|
||||
#include "app/config_manager.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
@ -26,6 +27,28 @@ MainWindow::MainWindow(ConfigManager* configManager, QWidget* parent)
|
||||
setupMenuBar();
|
||||
loadStyleSheet();
|
||||
|
||||
// 初始化语音输入服务
|
||||
voiceInputService_ = new VoiceInputService(configManager_, this);
|
||||
connect(voiceInputService_, &VoiceInputService::statusChanged,
|
||||
this, [this](const QString& status) {
|
||||
LOG_DEBUG(kTag, QString("语音输入状态: %1").arg(status));
|
||||
});
|
||||
connect(voiceInputService_, &VoiceInputService::error,
|
||||
this, [this](const QString& err) {
|
||||
LOG_ERROR(kTag, err);
|
||||
});
|
||||
connect(voiceInputService_, &VoiceInputService::recognitionResult,
|
||||
this, [this](const QString& text) {
|
||||
LOG_INFO(kTag, QString("语音识别结果: %1").arg(text));
|
||||
});
|
||||
|
||||
// 监听配置变化,动态启停语音输入服务
|
||||
connect(configManager_, &ConfigManager::configChanged,
|
||||
this, &MainWindow::onVoiceInputConfigChanged);
|
||||
|
||||
// 启动时检查配置
|
||||
onVoiceInputConfigChanged();
|
||||
|
||||
LOG_INFO(kTag, "主窗口已创建");
|
||||
}
|
||||
|
||||
@ -79,8 +102,24 @@ void MainWindow::loadStyleSheet() {
|
||||
}
|
||||
|
||||
void MainWindow::closeEvent(QCloseEvent* event) {
|
||||
if (voiceInputService_) {
|
||||
voiceInputService_->stop();
|
||||
}
|
||||
LOG_INFO(kTag, "主窗口关闭");
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
void MainWindow::onVoiceInputConfigChanged() {
|
||||
if (!voiceInputService_) return;
|
||||
|
||||
bool enabled = configManager_->get("stt.capslock_voice_enabled").toBool();
|
||||
if (enabled && !voiceInputService_->isRunning()) {
|
||||
voiceInputService_->start();
|
||||
LOG_INFO(kTag, "CapsLock 语音输入已启用");
|
||||
} else if (!enabled && voiceInputService_->isRunning()) {
|
||||
voiceInputService_->stop();
|
||||
LOG_INFO(kTag, "CapsLock 语音输入已关闭");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impress
|
||||
|
||||
@ -10,6 +10,7 @@ class ConfigManager;
|
||||
class STTTestPage;
|
||||
class FileTranscribePage;
|
||||
class SettingsPage;
|
||||
class VoiceInputService;
|
||||
|
||||
/**
|
||||
* @brief 主窗口
|
||||
@ -29,8 +30,10 @@ private:
|
||||
void setupUI();
|
||||
void setupMenuBar();
|
||||
void loadStyleSheet();
|
||||
void onVoiceInputConfigChanged();
|
||||
|
||||
ConfigManager* configManager_;
|
||||
VoiceInputService* voiceInputService_;
|
||||
STTTestPage* sttPage_;
|
||||
FileTranscribePage* transcribePage_;
|
||||
SettingsPage* settingsPage_;
|
||||
|
||||
@ -83,6 +83,14 @@ void SettingsPage::setupUI() {
|
||||
streamingCheck_->setChecked(true);
|
||||
sttLayout->addRow("流式识别:", streamingCheck_);
|
||||
|
||||
debugSaveAudioCheck_ = new QCheckBox("保存调试音频到 /tmp/impress_audio_debug/", this);
|
||||
debugSaveAudioCheck_->setToolTip("开启后,每次识别会将原始音频保存为 WAV 文件,用于调试音频质量问题");
|
||||
sttLayout->addRow("调试录音:", debugSaveAudioCheck_);
|
||||
|
||||
capslockVoiceCheck_ = new QCheckBox("启用 CapsLock 长按语音输入", this);
|
||||
capslockVoiceCheck_->setToolTip("长按 CapsLock 键 1 秒后触发录音,松开后自动转写并输入到光标位置");
|
||||
sttLayout->addRow("快捷语音:", capslockVoiceCheck_);
|
||||
|
||||
beamSizeSpin_ = new QSpinBox(this);
|
||||
beamSizeSpin_->setRange(1, 20);
|
||||
beamSizeSpin_->setValue(5);
|
||||
@ -174,6 +182,8 @@ void SettingsPage::loadFromConfig() {
|
||||
sampleRateSpin_->setValue(configManager_->get("stt.sample_rate").toInt());
|
||||
languageCombo_->setCurrentText(configManager_->get("stt.language").toString());
|
||||
streamingCheck_->setChecked(configManager_->get("stt.streaming").toBool());
|
||||
debugSaveAudioCheck_->setChecked(configManager_->get("stt.debug_save_audio").toBool());
|
||||
capslockVoiceCheck_->setChecked(configManager_->get("stt.capslock_voice_enabled").toBool());
|
||||
beamSizeSpin_->setValue(configManager_->get("stt.beam_size").toInt());
|
||||
temperatureSpin_->setValue(configManager_->get("stt.temperature").toDouble());
|
||||
|
||||
@ -196,6 +206,8 @@ void SettingsPage::saveToConfig() {
|
||||
configManager_->set("stt.sample_rate", sampleRateSpin_->value());
|
||||
configManager_->set("stt.language", languageCombo_->currentText());
|
||||
configManager_->set("stt.streaming", streamingCheck_->isChecked());
|
||||
configManager_->set("stt.debug_save_audio", debugSaveAudioCheck_->isChecked());
|
||||
configManager_->set("stt.capslock_voice_enabled", capslockVoiceCheck_->isChecked());
|
||||
configManager_->set("stt.beam_size", beamSizeSpin_->value());
|
||||
configManager_->set("stt.temperature", temperatureSpin_->value());
|
||||
|
||||
|
||||
@ -51,6 +51,8 @@ private:
|
||||
QSpinBox* sampleRateSpin_;
|
||||
QComboBox* languageCombo_;
|
||||
QCheckBox* streamingCheck_;
|
||||
QCheckBox* debugSaveAudioCheck_;
|
||||
QCheckBox* capslockVoiceCheck_;
|
||||
QSpinBox* beamSizeSpin_;
|
||||
QDoubleSpinBox* temperatureSpin_;
|
||||
|
||||
|
||||
@ -135,6 +135,10 @@ void STTTestPage::onToggleRecording() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从配置同步调试开关到引擎
|
||||
sttEngine_->setDebugSaveAudio(
|
||||
configManager_->get("stt.debug_save_audio").toBool());
|
||||
|
||||
// 异步加载模型
|
||||
if (!sttEngine_->isLoaded() ||
|
||||
currentModelPath_ != modelPath) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user