Process the microphone like TS3, and tell it about key presses
The capture path now runs the ported WebRTC chain in place of the home-made noise, typing and gain stages, which are removed. - As in TS3, the speech detector judges the raw microphone signal, while the level meter and volume gate see the processed one. - Noise removal takes TS3's four levels (6, 12, 18 or 21 dB); a stored 0..1 level falls back to TS3's default of 12 dB. - Every key press from the global input hook reaches the connected microphones and the microphone test, so typing attenuation engages while the user types. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -84,9 +84,13 @@ final class DevicesPanel extends FormPanel {
|
||||
|
||||
denoiseCheck = new JCheckBox("Remove background noise", settings.denoise);
|
||||
denoiseCheck.setToolTipText("Attempt to filter out background noises.");
|
||||
denoiseLevel = new JSlider(0, 100, (int) Math.round(settings.denoiserLevel * 100));
|
||||
// TS3's four steps, 6/12/18/21 dB of suppression.
|
||||
denoiseLevel = new JSlider(0, 3, Math.max(0, Math.min(3, settings.denoiserLevel)));
|
||||
denoiseLevel.setMajorTickSpacing(1);
|
||||
denoiseLevel.setPaintTicks(true);
|
||||
denoiseLevel.setSnapToTicks(true);
|
||||
limitWidth(denoiseLevel, SLIDER_WIDTH);
|
||||
denoiseLevel.setToolTipText("Higher = more aggressive noise removal.");
|
||||
denoiseLevel.setToolTipText("How much steady noise to remove: 6, 12, 18 or 21 dB.");
|
||||
typingCheck = new JCheckBox("Typing attenuation", settings.typingAttenuation);
|
||||
typingCheck.setToolTipText("<html><b>Typing attenuation</b> tries to detect and "
|
||||
+ "reduce the sounds made by typing.</html>");
|
||||
@@ -113,7 +117,7 @@ final class DevicesPanel extends FormPanel {
|
||||
denoiseLevel.setEnabled(denoiseCheck.isSelected());
|
||||
applyLive.accept(m -> {
|
||||
m.setNoiseSuppression(denoiseCheck.isSelected());
|
||||
m.setDenoiserLevel(denoiseLevel.getValue() / 100.0);
|
||||
m.setDenoiserLevel(denoiseLevel.getValue());
|
||||
m.setTypingAttenuation(typingCheck.isSelected());
|
||||
m.setAgc(agcCheck.isSelected());
|
||||
});
|
||||
@@ -122,7 +126,7 @@ final class DevicesPanel extends FormPanel {
|
||||
typingCheck.addActionListener(e -> syncNoise.run());
|
||||
agcCheck.addActionListener(e -> syncNoise.run());
|
||||
denoiseLevel.addChangeListener(e ->
|
||||
applyLive.accept(m -> m.setDenoiserLevel(denoiseLevel.getValue() / 100.0)));
|
||||
applyLive.accept(m -> m.setDenoiserLevel(denoiseLevel.getValue())));
|
||||
syncNoise.run();
|
||||
|
||||
mutedWarningCheck = new JCheckBox("Beep when talking while muted", settings.mutedTalkWarning);
|
||||
@@ -148,7 +152,7 @@ final class DevicesPanel extends FormPanel {
|
||||
target.inputVolume = inputGain.getValue() / 100.0;
|
||||
target.outputVolume = outputVol.getValue() / 100.0;
|
||||
target.denoise = denoiseCheck.isSelected();
|
||||
target.denoiserLevel = denoiseLevel.getValue() / 100.0;
|
||||
target.denoiserLevel = denoiseLevel.getValue();
|
||||
target.typingAttenuation = typingCheck.isSelected();
|
||||
target.agc = agcCheck.isSelected();
|
||||
target.mutedTalkWarning = mutedWarningCheck.isSelected();
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.ts3client.hotkey.Hotkeys;
|
||||
import com.ts3client.hotkey.desktop.DesktopInputHooks;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Owns the hotkey machinery for the UI: the stored bindings, the matching engine and
|
||||
@@ -22,11 +23,29 @@ final class HotkeyService {
|
||||
private final HotkeyEngine engine;
|
||||
private final GlobalInputHook hook;
|
||||
private final HotkeyArguments arguments;
|
||||
private final List<Runnable> keyPressListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
HotkeyService(HotkeyEngine.Handler handler, HotkeyArguments arguments) {
|
||||
this.arguments = arguments;
|
||||
engine = new HotkeyEngine(hotkeys, handler);
|
||||
hook = DesktopInputHooks.start(engine);
|
||||
hook = DesktopInputHooks.start((key, pressed) -> {
|
||||
engine.onInput(key, pressed);
|
||||
if (pressed && key.device() == HotkeyKey.Device.KEYBOARD) {
|
||||
for (Runnable l : keyPressListeners) l.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs {@code l} on the input hook's thread for every key going down anywhere on the
|
||||
* system, which is what typing attenuation listens for. Keep it cheap.
|
||||
*/
|
||||
void addKeyPressListener(Runnable l) {
|
||||
keyPressListeners.add(l);
|
||||
}
|
||||
|
||||
void removeKeyPressListener(Runnable l) {
|
||||
keyPressListeners.remove(l);
|
||||
}
|
||||
|
||||
/** The values this action's parameter can take right now, for the action tree. */
|
||||
|
||||
@@ -95,6 +95,7 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
this.soundPlayer = audio.createSoundPlayer(settings);
|
||||
this.sounds.setPlayer(soundPlayer);
|
||||
this.hotkeys = new HotkeyService(new HotkeyActions(this), this::hotkeyArgumentChoices);
|
||||
hotkeys.addKeyPressListener(() -> SwingUtilities.invokeLater(this::keyPressed));
|
||||
contacts.addListener(() -> SwingUtilities.invokeLater(this::contactsChanged));
|
||||
|
||||
setIconImages(Icons.appImages());
|
||||
@@ -482,6 +483,15 @@ public final class MainFrame extends JFrame implements ServerTabPane.Listener {
|
||||
tab.connection().getMicrophone().setPushToTalk(talking);
|
||||
}
|
||||
|
||||
/** Tells every connected microphone about a key press, as TS3 tells all its connections. */
|
||||
private void keyPressed() {
|
||||
for (ServerTab tab : tabs) {
|
||||
if (tab.isConnected() && tab.connection().getMicrophone() != null) {
|
||||
tab.connection().getMicrophone().keyPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void moveMicrophoneToSelectedTab() {
|
||||
if (selected != null && selected.isConnected()) setMicTab(selected);
|
||||
updateToolbar();
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.ts3client.sound.SoundNotifier;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.JTabbedPane;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
@@ -83,11 +84,17 @@ public final class SettingsDialog extends JDialog {
|
||||
getContentPane().add(tabs, BorderLayout.CENTER);
|
||||
getContentPane().add(buttons, BorderLayout.SOUTH);
|
||||
|
||||
// The microphone test hears typing too, so typing attenuation can be tried out here.
|
||||
Runnable testKeyPress = () -> SwingUtilities.invokeLater(
|
||||
() -> voiceActivationPanel.configureTest(VoiceInput::keyPressed));
|
||||
hotkeys.addKeyPressListener(testKeyPress);
|
||||
|
||||
Dialogs.closeOnEscape(this, this::cancel);
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosed(java.awt.event.WindowEvent e) {
|
||||
hotkeys.removeKeyPressListener(testKeyPress);
|
||||
voiceActivationPanel.stopTest();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user