fix: isolate continuous-mode mic capture and clarify voice settings
- Use a separate Microphone instance for continuous-mode VAD monitoring so it no longer races with the transmit capture path. - Respect selected input device in continuous-mode monitor subscription. - Make talk-mode and Sonora processing labels clearer in settings UI.
This commit is contained in:
@@ -991,15 +991,21 @@ impl App {
|
||||
|
||||
// Continuous talk: monitor mic input when in continuous mode
|
||||
if self.talk_mode == audio::TalkMode::Continuous {
|
||||
let mic = self.mic.clone();
|
||||
let selected_input_device = self.selected_input_device.clone();
|
||||
let session_id = self.session_id;
|
||||
subs.push(Subscription::run_with_id(
|
||||
(session_id, "continuous"),
|
||||
(session_id, "continuous", selected_input_device.clone()),
|
||||
iced::stream::channel(100, move |mut sender| async move {
|
||||
let mic = Arc::new(Mutex::new(audio::Microphone::new()));
|
||||
let (sample_tx, sample_rx) = std::sync::mpsc::channel();
|
||||
{
|
||||
let mut mic_guard = mic.lock().await;
|
||||
if mic_guard.start(None, sample_tx).is_err() {
|
||||
let device = if selected_input_device.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(selected_input_device.as_str())
|
||||
};
|
||||
if mic_guard.start(device, sample_tx).is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+46
-18
@@ -643,36 +643,59 @@ impl App {
|
||||
.width(560),
|
||||
);
|
||||
|
||||
let talk_mode_text = match self.talk_mode {
|
||||
audio::TalkMode::PushToTalk => "Push-to-Talk (hold V)",
|
||||
audio::TalkMode::Continuous => "Continuous (voice activation)",
|
||||
};
|
||||
|
||||
let nc_label = self.nc_method.label();
|
||||
let sonora_noise_label = self.sonora_settings.noise_suppression_level.label();
|
||||
|
||||
let voice_col = column![
|
||||
text("Voice Activation Mode").size(13),
|
||||
text(talk_mode_text).size(12).style(text::secondary),
|
||||
text("Talk Mode").size(13),
|
||||
text("Choose how your microphone starts transmitting.").size(12).style(text::secondary),
|
||||
vertical_space().height(8),
|
||||
text("Push-to-Talk Key").size(13),
|
||||
text("V (hold to talk)").size(12).style(text::secondary),
|
||||
vertical_space().height(8),
|
||||
text("Noise Cancellation").size(13),
|
||||
text("Microphone Processing Backend").size(13),
|
||||
text(nc_label).size(12).style(text::secondary),
|
||||
]
|
||||
.spacing(4)
|
||||
.padding(16);
|
||||
|
||||
let voice_col = voice_col.push(
|
||||
button(text("Toggle Voice Mode").size(11))
|
||||
let voice_col = voice_col.push(row![
|
||||
button(text("Push-to-Talk").size(11))
|
||||
.padding([4, 12])
|
||||
.style(theme::secondary_button)
|
||||
.on_press(Message::ToggleTalkMode),
|
||||
.style(if self.talk_mode == audio::TalkMode::PushToTalk {
|
||||
theme::primary_button
|
||||
} else {
|
||||
theme::secondary_button
|
||||
})
|
||||
.on_press(if self.talk_mode == audio::TalkMode::PushToTalk {
|
||||
Message::Noop
|
||||
} else {
|
||||
Message::ToggleTalkMode
|
||||
}),
|
||||
button(text("Voice Activation").size(11))
|
||||
.padding([4, 12])
|
||||
.style(if self.talk_mode == audio::TalkMode::Continuous {
|
||||
theme::primary_button
|
||||
} else {
|
||||
theme::secondary_button
|
||||
})
|
||||
.on_press(if self.talk_mode == audio::TalkMode::Continuous {
|
||||
Message::Noop
|
||||
} else {
|
||||
Message::ToggleTalkMode
|
||||
}),
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Alignment::Center));
|
||||
|
||||
let voice_col = voice_col.push(
|
||||
text("Advanced options below only affect microphone cleanup, not playback.")
|
||||
.size(11)
|
||||
.style(text::secondary),
|
||||
);
|
||||
|
||||
let voice_col = noise_cancel::NoiseCancelMethod::all().iter().copied().fold(
|
||||
voice_col.push(text("Noise Cancel Backend").size(11).style(text::secondary)),
|
||||
voice_col.push(text("Processing backend").size(11).style(text::secondary)),
|
||||
|column, method| {
|
||||
let label = method.label().to_string();
|
||||
column.push(
|
||||
@@ -691,7 +714,10 @@ impl App {
|
||||
let voice_col = if self.nc_method == noise_cancel::NoiseCancelMethod::Sonora {
|
||||
let voice_col = voice_col
|
||||
.push(vertical_space().height(8))
|
||||
.push(text("Sonora Noise Suppression").size(11).style(text::secondary))
|
||||
.push(text("Advanced Sonora Processing").size(13))
|
||||
.push(text("Tune noise cleanup, auto gain, and low-frequency filtering.").size(11).style(text::secondary))
|
||||
.push(vertical_space().height(4))
|
||||
.push(text("Noise Suppression Strength").size(11).style(text::secondary))
|
||||
.push(text(sonora_noise_label).size(12).style(text::secondary));
|
||||
|
||||
let voice_col = noise_cancel::NoiseSuppressionLevelSetting::all()
|
||||
@@ -712,11 +738,12 @@ impl App {
|
||||
|
||||
voice_col
|
||||
.push(vertical_space().height(8))
|
||||
.push(text("Automatic Gain Control").size(11).style(text::secondary))
|
||||
.push(
|
||||
button(text(if self.sonora_settings.agc_enabled {
|
||||
"AGC2: On"
|
||||
"AGC2 On"
|
||||
} else {
|
||||
"AGC2: Off"
|
||||
"AGC2 Off"
|
||||
})
|
||||
.size(11))
|
||||
.padding([4, 12])
|
||||
@@ -727,11 +754,12 @@ impl App {
|
||||
})
|
||||
.on_press(Message::ToggleSonoraAgc),
|
||||
)
|
||||
.push(text("High-Pass Filter").size(11).style(text::secondary))
|
||||
.push(
|
||||
button(text(if self.sonora_settings.high_pass_enabled {
|
||||
"High-Pass: On"
|
||||
"High-Pass On"
|
||||
} else {
|
||||
"High-Pass: Off"
|
||||
"High-Pass Off"
|
||||
})
|
||||
.size(11))
|
||||
.padding([4, 12])
|
||||
|
||||
Reference in New Issue
Block a user