Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Reflection;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using AudioSwitcher.AudioApi.CoreAudio; // Nuget dependency: "AudioSwitcher.AudioApi.CoreAudio".
- // Version 23
- // POSTED ONLINE: https://pastebin.com/NEbxVfnQ
- // Razer.com post where i shared this code: https://insider.razer.com/razer-synapse-4-55/synapse-4-keeps-changing-default-audio-device-68915?postid=235801#post235801
- // 11/14/2024 - Did Synapse 4 break? It no longer can set default playback devices for me even when i click the "SET AS DEFAULT" buttons in the GUI under "SOUND" and "MIC".
- // Added a configurable abort timer to handle these situations instead of waiting forever.
- namespace Sound
- {
- class Program
- {
- // Usage: $ Sound.exe [option[="value"]] ...
- //
- // goodplayback - The desired playback device, e.g. 'CABLE Input (VB-Audio Virtual Cable)'.
- // goodrecording - The desired recording device, e.g. 'Voicemeeter Out B1 (VB-Audio Voicemeeter VAIO)'.
- // badplayback - The undesired playback device, e.g. 'Speakers (Razer Audio Controller - Game)'.
- // badrecording - The undesired recording device, e.g. 'Headset Microphone (Razer Audio Controller - Chat)'.
- // badprocess - The undesired process that changes sound settings, i.e. 'RazerAppEngine'.
- // recheck - The number of checks to perform to ensure sound devices are configured properly. The default value is 5.
- // delay - The delay used to reduce CPU usage spikes when performing repetitive tasks. The default value is 200.
- // abort - The delay before giving up waiting for bad processes to alter sound devices. The default value is 30.
- // devices - List playback and recording devices. The default value is False.
- // nopause - Prevent this program from pausing before exit. The default value is False.
- //
- // Example: $ Sound.exe ^
- // goodplayback="CABLE Input (VB-Audio Virtual Cable)" ^
- // goodrecording="Voicemeeter Out B1 (VB-Audio Voicemeeter VAIO)" ^
- // badplayback="Speakers (Razer Audio Controller - Game)" ^
- // badrecording="Headset Microphone (Razer Audio Controller - Chat)" ^
- // badprocess="RazerAppEngine" ^
- // recheck="5" ^
- // delay="200" ^
- // abort="30" ^
- // nopause
- //
- // Note: Use [$ Sound.exe devices] to get the device names to use with the arguments.
- // Note: If your device lacks a microphone, then don't use [goodrecording="..."] and [badrecording="..."] options.
- // How i use this program:
- // I have a Start.bat file that runs on startup with $ reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /v "Start" /t REG_SZ /d \"%~dp0Start.bat\" /f
- //
- // And one of its lines looks like this:
- //
- // "Sound\Sound\bin\Debug\Sound.exe" ^
- // goodplayback="CABLE Input (VB-Audio Virtual Cable)" ^
- // goodrecording="Voicemeeter Out B1 (VB-Audio Voicemeeter VAIO)" ^
- // badplayback="Speakers (Razer Audio Controller - Game)" ^
- // badrecording="Headset Microphone (Razer Audio Controller - Chat)" ^
- // badprocess="RazerAppEngine" ^
- // recheck="4" ^
- // delay="200" ^
- // abort="10" ^
- // nopause
- static async Task Main(string[] args)
- {
- Arguments arguments = new Arguments(args);
- #region Wait for Synapse to start and begin changing sound settings
- if (string.IsNullOrWhiteSpace(arguments.BadProcess) == false &&
- (arguments.GoodPlaybackDevice != null || arguments.GoodRecordingDevice != null))
- {
- Output.WriteLine($" Waiting for {arguments.BadProcess} to start . . .");
- CancellationTokenSource cts = (arguments.Abort > 0) ? new CancellationTokenSource(TimeSpan.FromSeconds(arguments.Abort)) : new CancellationTokenSource(); // Time delay abort or no time delay abort.
- try
- {
- while (Process.GetProcessesByName(arguments.BadProcess).Any() == false)
- await Task.Delay(arguments.Delay, cts.Token).ConfigureAwait(false);
- if (arguments.BadPlaybackDevice != null || arguments.BadRecordingDevice != null)
- {
- Output.WriteLine();
- Output.WriteLine(" Waiting for bad sound devices to be set as default . . .");
- }
- while (true)
- {
- CoreAudioController coreAudioController = new CoreAudioController(); // CoreAudioController calls RefreshSystemDevices() in its constructor with no other way to refresh devices.
- var defaultPlaybackDevice = coreAudioController.DefaultPlaybackDevice;
- var defaultRecordingDevice = coreAudioController.DefaultCaptureDevice;
- bool isBadPlaybackDeviceSet = arguments.BadPlaybackDevice == null ||
- (defaultPlaybackDevice != null && defaultPlaybackDevice.FullName.Equals(arguments.BadPlaybackDevice, StringComparison.InvariantCultureIgnoreCase));
- bool isBadRecordingDeviceSet = arguments.BadRecordingDevice == null ||
- (defaultRecordingDevice != null && defaultRecordingDevice.FullName.Equals(arguments.BadRecordingDevice, StringComparison.InvariantCultureIgnoreCase));
- if ((isBadPlaybackDeviceSet && isBadRecordingDeviceSet) ||
- (arguments.GoodPlaybackDevice == null && isBadRecordingDeviceSet) ||
- (isBadPlaybackDeviceSet && arguments.GoodRecordingDevice == null))
- break;
- await Task.Delay(arguments.Delay, cts.Token).ConfigureAwait(false);
- }
- }
- catch (OperationCanceledException)
- {
- Output.WriteLine();
- Output.WriteLine($" Stopped waiting after {arguments.Abort} seconds . . .");
- }
- finally
- {
- cts.Dispose();
- }
- }
- #endregion Wait for Synapse to start and begin changing sound settings
- #region Set desired sound devices
- if (arguments.GoodPlaybackDevice != null ||
- arguments.GoodRecordingDevice != null)
- {
- for (int check = 0, attempt = 0; check < arguments.ReCheck; check++)
- {
- CoreAudioController coreAudioController = new CoreAudioController(); // CoreAudioController calls RefreshSystemDevices() in its constructor with no other way to refresh devices.
- bool success = true;
- // If default playback device does not match desired playback device.
- if (arguments.GoodPlaybackDevice != null &&
- coreAudioController.DefaultPlaybackDevice.FullName.Equals(arguments.GoodPlaybackDevice, StringComparison.InvariantCultureIgnoreCase) == false)
- {
- check = 0; // Restart loop.
- Output.WriteLine();
- Output.WriteLine($" ! Unwanted default playback device detected: {coreAudioController.DefaultPlaybackDevice.FullName}");
- Output.WriteLine($" Changing default playback device to: {arguments.GoodPlaybackDevice}");
- success &= await coreAudioController.SetDefaultDeviceAsync(arguments.GoodPlaybackDevice).ConfigureAwait(false);
- }
- // If default recording device does not match desired recording device.
- if (arguments.GoodRecordingDevice != null &&
- coreAudioController.DefaultCaptureDevice.FullName.Equals(arguments.GoodRecordingDevice, StringComparison.InvariantCultureIgnoreCase) == false)
- {
- check = 0; // Restart loop.
- Output.WriteLine();
- Output.WriteLine($" ! Unwanted default recording device detected: {coreAudioController.DefaultCaptureDevice.FullName}");
- Output.WriteLine($" Changing default recording device to: {arguments.GoodRecordingDevice}");
- success &= await coreAudioController.SetDefaultDeviceAsync(arguments.GoodRecordingDevice).ConfigureAwait(false);
- }
- if (success)
- {
- // If not asked to wait for a "bad" process to alter default sound devices, or to only perform one check.
- if (arguments.BadProcess == null ||
- arguments.ReCheck == 1)
- break;
- if (check == 0)
- Output.WriteLine(); // Add a blank line before write-same-line output.
- Output.WriteSameLine($" + Checking default sound devices . . . {(check + 1) / (double)arguments.ReCheck:P0}");
- }
- else
- {
- check = 0; // Restart loop.
- attempt++;
- Output.WriteLine();
- Output.WriteLine($" ! Retry attempt {attempt} of {MAX_RETRIES} due to device configuration failure.");
- if (attempt >= MAX_RETRIES)
- {
- arguments.NoPause.Value = false; // Disable no-pause because of setting-sound-device-as-default error.
- break;
- }
- }
- await Task.Delay(arguments.Delay).ConfigureAwait(false);
- }
- }
- #endregion Set desired sound devices
- if (arguments.Devices)
- await new CoreAudioController().DisplaySoundDevicesAsync().ConfigureAwait(false); // CoreAudioController calls RefreshSystemDevices() in its constructor with no other way to refresh devices.
- if (arguments.NoPause == false)
- Output.Pause();
- }
- private const int MAX_RETRIES = 3;
- }
- public static class CoreAudioControllerExtensions
- {
- public static async Task<bool> SetDefaultDeviceAsync(this CoreAudioController coreAudioController, string deviceName)
- {
- if (coreAudioController == null)
- throw new ArgumentNullException(nameof(coreAudioController));
- if (string.IsNullOrEmpty(deviceName))
- throw new ArgumentNullException(nameof(deviceName));
- try
- {
- var devices = await coreAudioController.GetDevicesAsync().ConfigureAwait(false);
- foreach (CoreAudioDevice device in devices)
- {
- if (device.FullName.Equals(deviceName, StringComparison.InvariantCultureIgnoreCase))
- {
- bool setAsDefault = await device.SetAsDefaultAsync().ConfigureAwait(false);
- bool setAsDefaultCommunications = await device.SetAsDefaultCommunicationsAsync().ConfigureAwait(false);
- if (setAsDefault == false ||
- setAsDefaultCommunications == false)
- {
- Output.WriteLine();
- Output.WriteLine($" Error: Failed to set device '{deviceName}' as default.");
- return false;
- }
- return true; // Successfully set the device as default.
- }
- }
- }
- catch (Exception ex)
- {
- Output.WriteLine();
- Output.WriteLine($" Error: Failed to set device '{deviceName}' as default - {ex.Message}");
- return false;
- }
- Output.WriteLine();
- Output.WriteLine($" Error: Could not find audio device: {deviceName}");
- return false;
- }
- public static async Task DisplaySoundDevicesAsync(this CoreAudioController coreAudioController)
- {
- if (coreAudioController == null)
- throw new ArgumentNullException(nameof(coreAudioController));
- CoreAudioDevice defaultPlaybackDevice = coreAudioController.DefaultPlaybackDevice;
- Output.WriteLine();
- Output.WriteLine(" Playback devices:");
- foreach (CoreAudioDevice device in await coreAudioController.GetPlaybackDevicesAsync().ConfigureAwait(false))
- {
- if (device == null)
- continue;
- string isDefault = (defaultPlaybackDevice != null && device.Id == defaultPlaybackDevice.Id) ? "*" : " ";
- Output.WriteLine($" {isDefault} {device.FullName}");
- }
- CoreAudioDevice defaultRecordingDevice = coreAudioController.DefaultCaptureDevice;
- Output.WriteLine();
- Output.WriteLine(" Recording devices:");
- foreach (CoreAudioDevice device in await coreAudioController.GetCaptureDevicesAsync().ConfigureAwait(false))
- {
- if (device == null)
- continue;
- string isDefault = (defaultRecordingDevice != null && device.Id == defaultRecordingDevice.Id) ? "*" : " ";
- Output.WriteLine($" {isDefault} {device.FullName}");
- }
- }
- }
- public class Arguments
- {
- public Argument<string> GoodPlaybackDevice { get; } = new Argument<string>("goodplayback", "The desired playback device, e.g. 'CABLE Input (VB-Audio Virtual Cable)'.");
- public Argument<string> GoodRecordingDevice { get; } = new Argument<string>("goodrecording", "The desired recording device, e.g. 'Voicemeeter Out B1 (VB-Audio Voicemeeter VAIO)'.");
- public Argument<string> BadPlaybackDevice { get; } = new Argument<string>("badplayback", "The undesired playback device, e.g. 'Speakers (Razer Audio Controller - Game)'.");
- public Argument<string> BadRecordingDevice { get; } = new Argument<string>("badrecording", "The undesired recording device, e.g. 'Headset Microphone (Razer Audio Controller - Chat)'.");
- public Argument<string> BadProcess { get; } = new Argument<string>("badprocess", "The undesired process that changes sound settings, i.e. 'RazerAppEngine'.");
- public Argument<int> ReCheck { get; } = new Argument<int>("recheck", "The number of checks to perform to ensure sound devices are configured properly.", 5);
- public Argument<int> Delay { get; } = new Argument<int>("delay", "The delay used to reduce CPU usage spikes when performing repetitive tasks.", 200);
- public Argument<int> Abort { get; } = new Argument<int>("abort", "The delay before giving up waiting for bad processes to alter sound devices.", 30);
- public Argument<bool> Devices { get; } = new Argument<bool>("devices", "List playback and recording devices.");
- public Argument<bool> NoPause { get; } = new Argument<bool>("nopause", "Prevent this program from pausing before exit.");
- public Arguments(string[] args)
- {
- // Get all properties of type Argument<T>.
- _arguments = GetType()
- .GetProperties(BindingFlags.Public | BindingFlags.Instance)
- .Where(prop => prop.PropertyType.IsGenericType &&
- prop.PropertyType.GetGenericTypeDefinition() == typeof(Argument<>));
- bool success = true;
- foreach (string arg in args)
- {
- bool argParsed = _arguments.Any(property =>
- {
- dynamic argument = property.GetValue(this);
- if (ReferenceEquals(argument, null) == false) // Null check without using the overloaded equality operator in Argument<T>.
- return argument.TryParse(arg);
- return false;
- });
- if (argParsed == false)
- {
- success = false;
- Output.WriteLine($" Error: Unparsed argument detected: {arg}");
- }
- }
- if (args.Length == 0 ||
- success == false)
- {
- DisplayUsage();
- Environment.Exit(1);
- }
- }
- public void DisplayUsage()
- {
- string programName = AppDomain.CurrentDomain.FriendlyName;
- StringBuilder sb = new StringBuilder();
- sb.AppendLine();
- sb.AppendLine($" Usage: $ {programName} [option[=\"value\"]] ...");
- sb.AppendLine();
- foreach (PropertyInfo property in _arguments)
- {
- dynamic argument = property.GetValue(this);
- if (ReferenceEquals(argument, null) == false) // Null check without using the overloaded equality operator in Argument<T>.
- {
- string defaultValue = argument.DefaultValue != null ? $" The default value is {argument.DefaultValue}." : string.Empty;
- sb.AppendLine($" {argument.Name} - {argument.Description}{defaultValue}");
- }
- }
- sb.AppendLine();
- sb.AppendLine($" Example: $ {programName} ^");
- sb.AppendLine($" {GoodPlaybackDevice.Name}=\"CABLE Input (VB-Audio Virtual Cable)\" ^");
- sb.AppendLine($" {GoodRecordingDevice.Name}=\"Voicemeeter Out B1 (VB-Audio Voicemeeter VAIO)\" ^");
- sb.AppendLine($" {BadPlaybackDevice.Name}=\"Speakers (Razer Audio Controller - Game)\" ^");
- sb.AppendLine($" {BadRecordingDevice.Name}=\"Headset Microphone (Razer Audio Controller - Chat)\" ^");
- sb.AppendLine($" {BadProcess.Name}=\"RazerAppEngine\" ^");
- sb.AppendLine($" {ReCheck.Name}=\"{ReCheck.DefaultValue}\" ^");
- sb.AppendLine($" {Delay.Name}=\"{Delay.DefaultValue}\" ^");
- sb.AppendLine($" {Abort.Name}=\"{Abort.DefaultValue}\" ^");
- sb.AppendLine($" {NoPause.Name}");
- sb.AppendLine();
- sb.AppendLine($" Note: Use [$ {programName} {Devices.Name}] to get the device names to use with the arguments.");
- sb.Append($" Note: If your device lacks a microphone, then don't use [goodrecording=\"...\"] and [badrecording=\"...\"] options.");
- Output.WriteLine(sb.ToString());
- Output.Pause();
- }
- private IEnumerable<PropertyInfo> _arguments;
- }
- public class Argument<T>
- {
- public string Name { get; }
- public string Description { get; }
- public T DefaultValue { get; }
- public T Value { get; set; }
- public Argument(string name, string description, T defaultValue = default)
- {
- Name = name;
- Description = description;
- DefaultValue = defaultValue;
- Value = defaultValue;
- }
- public static implicit operator T(Argument<T> argument) => (argument is null) ? default : argument.Value;
- public static bool operator !=(Argument<T> left, Argument<T> right) => !(left == right);
- public static bool operator ==(Argument<T> left, Argument<T> right)
- {
- if (ReferenceEquals(left, right))
- return true;
- if ((ReferenceEquals(left.Value, null) && ReferenceEquals(right, null)) ||
- (ReferenceEquals(left, null)) && ReferenceEquals(right.Value, null))
- return true;
- if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
- return false;
- return EqualityComparer<T>.Default.Equals(left.Value, right.Value);
- }
- public override bool Equals(object obj) => obj is Argument<T> other && this == other;
- public override int GetHashCode() => Value?.GetHashCode() ?? 0;
- public override string ToString() => Value?.ToString() ?? base.ToString();
- public bool TryParse(string arg)
- {
- // Split 'name=value' into 'name' and 'value' parts.
- string[] parts = arg.Split(new[] { '=' }, 2);
- string name = parts[0].Trim();
- if (name.Equals(Name, StringComparison.InvariantCultureIgnoreCase) == false)
- return false;
- if (parts.Length > 1)
- {
- string value = parts[1].Trim();
- try
- {
- // Try to convert the value to the expected type.
- Value = (T)Convert.ChangeType(value, typeof(T));
- return true;
- }
- catch
- {
- return false;
- }
- }
- if (typeof(T) == typeof(bool) &&
- parts.Length == 1)
- {
- // Treat [name] args as shorthand for "name=true".
- Value = (T)(object)true;
- return true;
- }
- return false;
- }
- }
- public static class Output
- {
- public static void Pause()
- {
- lock (_syncRoot)
- {
- WriteLine();
- Write(" Press any key to continue . . . ");
- }
- Console.ReadKey(); // Moved outside lock to avoid blocking other threads on input.
- }
- public static void WriteSameLine(string value)
- {
- lock (_syncRoot)
- {
- // Move the cursor home.
- Console.SetCursorPosition(0, Console.CursorTop);
- // Clear the current line by overwriting it with spaces.
- Write(new string(' ', Console.WindowWidth));
- // Move the cursor home.
- Console.SetCursorPosition(0, Console.CursorTop);
- Write(value);
- }
- }
- #region WriteLine
- public static void WriteLine()
- {
- lock (_syncRoot)
- {
- EnsureNewLine();
- Console.WriteLine();
- }
- }
- public static void WriteLine(string value)
- {
- lock (_syncRoot)
- {
- EnsureNewLine();
- Console.WriteLine(value);
- }
- }
- public static void WriteLine(object value)
- {
- lock (_syncRoot)
- {
- EnsureNewLine();
- Console.WriteLine(value);
- }
- }
- #endregion WriteLine
- public static void Write(string value)
- {
- lock (_syncRoot)
- {
- Console.Write(value);
- _isSameLine = true;
- }
- }
- private static void EnsureNewLine()
- {
- if (_isSameLine)
- {
- // Add a new line to the console to get under the same-line writing that was last used.
- Console.WriteLine();
- _isSameLine = false;
- }
- }
- private static bool _isSameLine = false;
- private static readonly object _syncRoot = new object();
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement