feat(voice): modalità dialogo — voce continua hands-free (v0.2.0)

- Nuovo VoiceRecorder (QAudioInput, PCM 16 bit mono): analisi del livello
  in tempo reale e stop automatico dopo il silenzio (pausa e soglia
  configurabili dalle impostazioni); produce WAV per /api/transcribe;
  emette failed("no-speech") se non rileva voce (cattura scartata)
- ChatPage: modalità dialogo (menu "Voice mode"): ascolto -> trascrizione
  -> invio -> lettura della risposta -> riascolto in ciclo; tocco sul
  testo di stato per uscire; limite di 3 errori consecutivi poi esce
- Dettatura manuale: invio automatico dopo la trascrizione (opzione,
  default attivo, disattivabile)
- Settings: sezione "Voice dialog" (auto-invio, pausa auto-stop,
  sensibilità microfono)
- Il percorso di dettatura manuale (Recorder OGG) resta invariato
This commit is contained in:
2026-09-12 09:14:25 +02:00
parent 479edb0f99
commit 9deeedf5b3
12 changed files with 620 additions and 18 deletions
+34
View File
@@ -94,6 +94,40 @@ Page {
onCheckedChanged: appSettings.ttsAutoRead = checked
}
SectionHeader { text: qsTr("Voice dialog") }
TextSwitch {
text: qsTr("Send right after dictation")
description: qsTr("Send the transcribed text automatically, without tapping Send")
checked: appSettings.dictationAutoSend
onCheckedChanged: appSettings.dictationAutoSend = checked
}
ComboBox {
label: qsTr("Auto-stop pause")
currentIndex: {
var v = appSettings.vadSilenceMs
return v <= 1000 ? 0 : (v <= 1500 ? 1 : (v <= 2000 ? 2 : 3))
}
menu: ContextMenu {
MenuItem { text: qsTr("1.0 second"); onClicked: appSettings.vadSilenceMs = 1000 }
MenuItem { text: qsTr("1.5 seconds"); onClicked: appSettings.vadSilenceMs = 1500 }
MenuItem { text: qsTr("2.0 seconds"); onClicked: appSettings.vadSilenceMs = 2000 }
MenuItem { text: qsTr("3.0 seconds"); onClicked: appSettings.vadSilenceMs = 3000 }
}
}
ComboBox {
label: qsTr("Mic sensitivity")
currentIndex: appSettings.vadThreshold <= 500 ? 0
: (appSettings.vadThreshold <= 1000 ? 1 : 2)
menu: ContextMenu {
MenuItem { text: qsTr("High (even soft voices)"); onClicked: appSettings.vadThreshold = 400 }
MenuItem { text: qsTr("Medium"); onClicked: appSettings.vadThreshold = 800 }
MenuItem { text: qsTr("Low (noisy rooms)"); onClicked: appSettings.vadThreshold = 1600 }
}
}
SectionHeader { text: qsTr("Account") }
Button {