Skip to content

Commit b464e2d

Browse files
committed
feat(microphone): Notify on any microphone being muted.
1 parent 77c4ee3 commit b464e2d

File tree

3 files changed

+41
-20
lines changed

3 files changed

+41
-20
lines changed

.github/copilot-instructions.md

+4
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,7 @@ SoundSwitch is a Windows application for switching audio playback and recording
9191
2. Test changes thoroughly across different Windows versions
9292
3. Use GitHub flow for contributions (feature branches and PRs)
9393
4. Update documentation and CHANGELOG.md with significant changes
94+
95+
## DeviceFullInfo
96+
97+
Never use the Name property. Always use the NameClean property instead. Ensure that any handling of device names is done using NameClean to avoid potential issues with formatting or invalid characters.

SoundSwitch/Framework/Audio/Microphone/MicrophoneMuteToggler.cs

+12-19
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,26 @@
55

66
namespace SoundSwitch.Framework.Audio.Microphone
77
{
8+
/// <summary>
9+
/// Provides functionality to toggle microphone mute state
10+
/// </summary>
811
public class MicrophoneMuteToggler
912
{
1013
private readonly AudioSwitcher _switcher;
11-
private readonly NotificationManager.NotificationManager _notificationManager;
1214

13-
public MicrophoneMuteToggler(AudioSwitcher switcher, NotificationManager.NotificationManager notificationManager)
15+
/// <summary>
16+
/// Initializes a new instance of the MicrophoneMuteToggler class
17+
/// </summary>
18+
/// <param name="switcher">The audio switcher instance</param>
19+
public MicrophoneMuteToggler(AudioSwitcher switcher)
1420
{
1521
_switcher = switcher;
16-
_notificationManager = notificationManager;
1722
}
1823

1924
/// <summary>
2025
/// Toggle mute state for the default microphone
21-
/// <returns>Tuple with the device name and current mute state, null if couldn't interact with the microphone</returns>
2226
/// </summary>
27+
/// <returns>Tuple with the device name and current mute state, null if couldn't interact with the microphone</returns>
2328
public (string Name, bool MuteState)? ToggleDefaultMute()
2429
{
2530
var microphone = _switcher.GetDefaultMmDevice(EDataFlow.eCapture, ERole.eCommunications);
@@ -45,20 +50,14 @@ public MicrophoneMuteToggler(AudioSwitcher switcher, NotificationManager.Notific
4550
return default;
4651
});
4752

48-
if (result == default)
49-
{
50-
return null;
51-
}
52-
53-
_notificationManager.NotifyMuteChanged(result.Name, result.NewMuteState);
54-
return result;
53+
return result == default ? null : result;
5554
}
5655

5756
/// <summary>
5857
/// Set mute state for the default microphone
58+
/// </summary>
5959
/// <param name="muteState">The mute state to set</param>
6060
/// <returns>Tuple with the device name and current mute state, null if couldn't interact with the microphone</returns>
61-
/// </summary>
6261
public (string Name, bool MuteState)? SetDefaultMuteState(bool muteState)
6362
{
6463
var microphone = _switcher.GetDefaultMmDevice(EDataFlow.eCapture, ERole.eCommunications);
@@ -83,13 +82,7 @@ public MicrophoneMuteToggler(AudioSwitcher switcher, NotificationManager.Notific
8382
return default;
8483
});
8584

86-
if (result == default)
87-
{
88-
return null;
89-
}
90-
91-
_notificationManager.NotifyMuteChanged(result.Name, result.NewMuteState);
92-
return result;
85+
return result == default ? null : result;
9386
}
9487
}
9588
}

SoundSwitch/Model/AppModel.cs

+25-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System;
1717
using System.Collections.Generic;
1818
using System.Linq;
19+
using System.Reactive.Linq;
1920
using System.Threading;
2021
using NAudio.CoreAudioApi;
2122
using RailSharp;
@@ -57,7 +58,7 @@ private AppModel()
5758

5859
_deviceCyclerManager = new DeviceCyclerManager();
5960
_selectedDevices = null;
60-
_microphoneMuteToggler = new MicrophoneMuteToggler(AudioSwitcher.Instance, _notificationManager);
61+
_microphoneMuteToggler = new MicrophoneMuteToggler(AudioSwitcher.Instance);
6162
_updateScheduler = new LimitedConcurrencyLevelTaskScheduler(1);
6263
}
6364

@@ -331,6 +332,15 @@ public void InitializeMain(IAudioDeviceLister active)
331332
JobScheduler.Instance.ScheduleJob(new ProcessNotificationEventsJob());
332333
AudioDeviceLister.DefaultDeviceChanged.Subscribe((@event) => { DefaultDeviceChanged?.Invoke(this, new DeviceDefaultChangedEvent(@event.Device, @event.Role)); });
333334

335+
// Subscribe to volume change events for microphones
336+
AudioDeviceLister.DeviceVolumeChanged
337+
.Where(payload =>
338+
// Only listen for recording devices (microphones)
339+
payload.Device.Type == DataFlow.Capture &&
340+
// Only care about mute changes
341+
payload.MuteChanged)
342+
.Subscribe(HandleMicrophoneMuteChanged);
343+
334344
RegisterHotKey(AppConfigs.Configuration.PlaybackHotKey);
335345
var saveConfig = false;
336346
if (!RegisterHotKey(AppConfigs.Configuration.RecordingHotKey))
@@ -392,6 +402,20 @@ public void InitializeMain(IAudioDeviceLister active)
392402
}
393403

394404

405+
/// <summary>
406+
/// Handles microphone mute state changes and triggers appropriate notifications
407+
/// </summary>
408+
/// <param name="payload">The volume changed payload</param>
409+
private void HandleMicrophoneMuteChanged(DeviceVolumeChangedPayload payload)
410+
{
411+
Log.Information("Microphone {DeviceName} mute state changed from {WasMuted} to {IsMuted}",
412+
payload.Device.NameClean, payload.WasMuted, payload.IsMuted);
413+
414+
// Notify about the mute state change
415+
_notificationManager.NotifyMuteChanged(payload.Device.NameClean, payload.IsMuted);
416+
}
417+
418+
395419
private void InitUpdateChecker()
396420
{
397421
#if DEBUG

0 commit comments

Comments
 (0)