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:
ReTeamSpeak
2026-05-13 19:14:50 +09:00
parent 272d3a1cb7
commit 83346c337f
2 changed files with 55 additions and 21 deletions
+9 -3
View File
@@ -991,15 +991,21 @@ impl App {
// Continuous talk: monitor mic input when in continuous mode // Continuous talk: monitor mic input when in continuous mode
if self.talk_mode == audio::TalkMode::Continuous { 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; let session_id = self.session_id;
subs.push(Subscription::run_with_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 { 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 (sample_tx, sample_rx) = std::sync::mpsc::channel();
{ {
let mut mic_guard = mic.lock().await; 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; return;
} }
} }
+46 -18
View File
@@ -643,36 +643,59 @@ impl App {
.width(560), .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 nc_label = self.nc_method.label();
let sonora_noise_label = self.sonora_settings.noise_suppression_level.label(); let sonora_noise_label = self.sonora_settings.noise_suppression_level.label();
let voice_col = column![ let voice_col = column![
text("Voice Activation Mode").size(13), text("Talk Mode").size(13),
text(talk_mode_text).size(12).style(text::secondary), text("Choose how your microphone starts transmitting.").size(12).style(text::secondary),
vertical_space().height(8), vertical_space().height(8),
text("Push-to-Talk Key").size(13), text("Push-to-Talk Key").size(13),
text("V (hold to talk)").size(12).style(text::secondary), text("V (hold to talk)").size(12).style(text::secondary),
vertical_space().height(8), vertical_space().height(8),
text("Noise Cancellation").size(13), text("Microphone Processing Backend").size(13),
text(nc_label).size(12).style(text::secondary), text(nc_label).size(12).style(text::secondary),
] ]
.spacing(4) .spacing(4)
.padding(16); .padding(16);
let voice_col = voice_col.push( let voice_col = voice_col.push(row![
button(text("Toggle Voice Mode").size(11)) button(text("Push-to-Talk").size(11))
.padding([4, 12]) .padding([4, 12])
.style(theme::secondary_button) .style(if self.talk_mode == audio::TalkMode::PushToTalk {
.on_press(Message::ToggleTalkMode), 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( 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| { |column, method| {
let label = method.label().to_string(); let label = method.label().to_string();
column.push( column.push(
@@ -691,7 +714,10 @@ impl App {
let voice_col = if self.nc_method == noise_cancel::NoiseCancelMethod::Sonora { let voice_col = if self.nc_method == noise_cancel::NoiseCancelMethod::Sonora {
let voice_col = voice_col let voice_col = voice_col
.push(vertical_space().height(8)) .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)); .push(text(sonora_noise_label).size(12).style(text::secondary));
let voice_col = noise_cancel::NoiseSuppressionLevelSetting::all() let voice_col = noise_cancel::NoiseSuppressionLevelSetting::all()
@@ -712,11 +738,12 @@ impl App {
voice_col voice_col
.push(vertical_space().height(8)) .push(vertical_space().height(8))
.push(text("Automatic Gain Control").size(11).style(text::secondary))
.push( .push(
button(text(if self.sonora_settings.agc_enabled { button(text(if self.sonora_settings.agc_enabled {
"AGC2: On" "AGC2 On"
} else { } else {
"AGC2: Off" "AGC2 Off"
}) })
.size(11)) .size(11))
.padding([4, 12]) .padding([4, 12])
@@ -727,11 +754,12 @@ impl App {
}) })
.on_press(Message::ToggleSonoraAgc), .on_press(Message::ToggleSonoraAgc),
) )
.push(text("High-Pass Filter").size(11).style(text::secondary))
.push( .push(
button(text(if self.sonora_settings.high_pass_enabled { button(text(if self.sonora_settings.high_pass_enabled {
"High-Pass: On" "High-Pass On"
} else { } else {
"High-Pass: Off" "High-Pass Off"
}) })
.size(11)) .size(11))
.padding([4, 12]) .padding([4, 12])