diff --git a/BassBoom.Basolia/Devices/DeviceTools.cs b/BassBoom.Basolia/Devices/DeviceTools.cs index 2e07b93..13c59f2 100644 --- a/BassBoom.Basolia/Devices/DeviceTools.cs +++ b/BassBoom.Basolia/Devices/DeviceTools.cs @@ -19,8 +19,10 @@ using BassBoom.Native.Interop.Init; using BassBoom.Native.Interop.Output; using BassBoom.Native.Runtime; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Runtime.InteropServices; namespace BassBoom.Basolia.Devices { @@ -29,56 +31,115 @@ namespace BassBoom.Basolia.Devices /// public static class DeviceTools { + internal static string activeDriver = ""; + internal static string activeDevice = ""; + + [StructLayout(LayoutKind.Sequential)] + public struct DriverList + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public IntPtr[] listOfDrivers; + } + [StructLayout(LayoutKind.Sequential)] + public struct DriverDescList + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public IntPtr[] listOfDriverDescriptions; + } + [StructLayout(LayoutKind.Sequential)] + public struct DeviceList + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public IntPtr[] listOfDevices; + } + [StructLayout(LayoutKind.Sequential)] + public struct DeviceDescList + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public IntPtr[] listOfDeviceDescriptions; + } + public static ReadOnlyDictionary GetDrivers() { InitBasolia.CheckInited(); Dictionary drivers = new(); // We're now entering the dangerous zone - string[] names, descr; + nint names = nint.Zero, descr = nint.Zero; + int driverCount; + DriverList drvList; + DriverDescList drvDescList; unsafe { // Query the drivers var handle = Mpg123Instance._out123Handle; - int driversStatus = NativeOutputLib.out123_drivers(handle, out names, out descr); + int driversStatus = NativeOutputLib.out123_drivers(handle, ref names, ref descr); if (driversStatus == (int)mpg123_errors.MPG123_ERR) throw new BasoliaException("Can't query the drivers", mpg123_errors.MPG123_ERR); + drvList = Marshal.PtrToStructure(names); + drvDescList = Marshal.PtrToStructure(descr); + driverCount = driversStatus; } // Iterate through each driver - for (int i = 0; i < names.Length; i++) + for (int i = 0; i < driverCount; i++) { - string name = names[i]; - string description = descr[i]; + string name = Marshal.PtrToStringAnsi(drvList.listOfDrivers[i]); + string description = Marshal.PtrToStringAnsi(drvDescList.listOfDriverDescriptions[i]); drivers.Add(name, description); } return new ReadOnlyDictionary(drivers); } - public static ReadOnlyDictionary GetDevices(string driver) + public static ReadOnlyDictionary GetDevices(string driver, ref string activeDevice) { InitBasolia.CheckInited(); Dictionary devices = new(); // We're now entering the dangerous zone - string[] names, descr; + nint names = nint.Zero, descr = nint.Zero, active = nint.Zero; + int deviceCount; + DeviceList devList; + DeviceDescList devDescList; unsafe { // Query the devices var handle = Mpg123Instance._out123Handle; - int devicesStatus = NativeOutputLib.out123_devices(handle, driver, out names, out descr, out string _); + int devicesStatus = NativeOutputLib.out123_devices(handle, driver, ref names, ref descr, ref active); if (devicesStatus == (int)mpg123_errors.MPG123_ERR) throw new BasoliaException("Can't query the devices", mpg123_errors.MPG123_ERR); + devList = Marshal.PtrToStructure(names); + devDescList = Marshal.PtrToStructure(descr); + activeDevice = Marshal.PtrToStringAnsi(active); + deviceCount = devicesStatus; } // Iterate through each device - for (int i = 0; i < names.Length; i++) + for (int i = 0; i < deviceCount; i++) { - string name = names[i]; - string description = descr[i]; + string name = Marshal.PtrToStringAnsi(devList.listOfDevices[i]); + string description = Marshal.PtrToStringAnsi(devDescList.listOfDeviceDescriptions[i]); devices.Add(name, description); } return new ReadOnlyDictionary(devices); } + + public static void SetActiveDriver(string driver) + { + var driverList = GetDrivers(); + if (!driverList.ContainsKey(driver)) + throw new BasoliaException($"Driver {driver} doesn't exist", mpg123_errors.MPG123_ERR); + activeDriver = driver; + } + + public static void SetActiveDevice(string driver, string device) + { + var deviceList = GetDevices(driver, ref activeDevice); + if (string.IsNullOrEmpty(device)) + return; + if (!deviceList.ContainsKey(device)) + throw new BasoliaException($"Device {device} doesn't exist", mpg123_errors.MPG123_ERR); + activeDevice = device; + } } } diff --git a/BassBoom.Basolia/Playback/PlaybackTools.cs b/BassBoom.Basolia/Playback/PlaybackTools.cs index df5f72b..a49ef45 100644 --- a/BassBoom.Basolia/Playback/PlaybackTools.cs +++ b/BassBoom.Basolia/Playback/PlaybackTools.cs @@ -29,6 +29,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using BassBoom.Basolia.Devices; namespace BassBoom.Basolia.Playback { @@ -51,11 +52,6 @@ public static void Play() var handle = Mpg123Instance._mpg123Handle; var outHandle = Mpg123Instance._out123Handle; - // Query the devices - int devicesStatus = NativeOutputLib.out123_devices(outHandle, null, out string[] deviceNames, out string[] deviceDescr, out string active); - if (devicesStatus == (int)mpg123_errors.MPG123_ERR) - throw new BasoliaException("Can't query the devices", mpg123_errors.MPG123_ERR); - // First, get formats and set them var formatInfo = FormatTools.GetFormatInfo(); NativeOutput.mpg123_format_none(handle); @@ -65,9 +61,9 @@ public static void Play() Debug.WriteLine($"Format {formatInfo.rate}, {formatInfo.channels}, {formatInfo.encoding}"); // Try to open output to device - int openStatus = NativeOutputLib.out123_open(outHandle, active, null); + int openStatus = NativeOutputLib.out123_open(outHandle, DeviceTools.activeDriver, DeviceTools.activeDevice); if (openStatus != (int)out123_error.OUT123_OK) - throw new BasoliaOutException($"Can't open output to device {active}", (out123_error)openStatus); + throw new BasoliaOutException($"Can't open output to device {DeviceTools.activeDevice} on driver {DeviceTools.activeDriver}", (out123_error)openStatus); // Start the output int startStatus = NativeOutputLib.out123_start(outHandle, formatInfo.rate, formatInfo.channels, formatInfo.encoding); diff --git a/BassBoom.Native/Interop/Output/NativeOutputLib.cs b/BassBoom.Native/Interop/Output/NativeOutputLib.cs index 21893de..46b5f29 100644 --- a/BassBoom.Native/Interop/Output/NativeOutputLib.cs +++ b/BassBoom.Native/Interop/Output/NativeOutputLib.cs @@ -176,14 +176,15 @@ public static unsafe class NativeOutputLib /// MPG123_EXPORT int out123_drivers(out123_handle *ao, char ***names, char ***descr); /// [DllImport(LibraryTools.LibraryNameOut, CharSet = CharSet.Ansi)] - internal static extern int out123_drivers(out123_handle* ao, out string[] names, out string[] descr); + internal static extern int out123_drivers(out123_handle* ao, ref nint names, ref nint descr); /// /// MPG123_EXPORT int out123_devices( out123_handle *ao, const char *driver /// , char ***names, char ***descr, char **active_driver ); /// [DllImport(LibraryTools.LibraryNameOut, CharSet = CharSet.Ansi)] - internal static extern int out123_devices(out123_handle* ao, string driver, out string[] names, out string[] descr, out string active_driver); + internal static extern int out123_devices(out123_handle* ao, + [MarshalAs(UnmanagedType.LPStr)] string driver, ref nint names, ref nint descr, ref nint active_driver); /// /// MPG123_EXPORT void out123_stringlists_free(char **name, char **descr, int count); diff --git a/BassBoom.Native/runtimes/win-x64/native/mpg123-0.dll b/BassBoom.Native/runtimes/win-x64/native/mpg123-0.dll index 8f3e097..9afcc54 100644 Binary files a/BassBoom.Native/runtimes/win-x64/native/mpg123-0.dll and b/BassBoom.Native/runtimes/win-x64/native/mpg123-0.dll differ diff --git a/BassBoom.Native/runtimes/win-x64/native/out123-0.dll b/BassBoom.Native/runtimes/win-x64/native/out123-0.dll index 12b47c7..246275a 100644 Binary files a/BassBoom.Native/runtimes/win-x64/native/out123-0.dll and b/BassBoom.Native/runtimes/win-x64/native/out123-0.dll differ diff --git a/BassBoom.Native/runtimes/win-x64/native/plugins/output_dummy.dll b/BassBoom.Native/runtimes/win-x64/native/plugins/output_dummy.dll index 37c5c0b..8b4cce8 100644 Binary files a/BassBoom.Native/runtimes/win-x64/native/plugins/output_dummy.dll and b/BassBoom.Native/runtimes/win-x64/native/plugins/output_dummy.dll differ diff --git a/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32.dll b/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32.dll index 7bf94ab..b95a1a7 100644 Binary files a/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32.dll and b/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32.dll differ diff --git a/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32_wasapi.dll b/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32_wasapi.dll index 55fd01b..c190309 100644 Binary files a/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32_wasapi.dll and b/BassBoom.Native/runtimes/win-x64/native/plugins/output_win32_wasapi.dll differ diff --git a/BassBoom.Native/runtimes/win-x64/native/syn123-0.dll b/BassBoom.Native/runtimes/win-x64/native/syn123-0.dll index 005b0de..e593c3e 100644 Binary files a/BassBoom.Native/runtimes/win-x64/native/syn123-0.dll and b/BassBoom.Native/runtimes/win-x64/native/syn123-0.dll differ diff --git a/BassBoom/Views/MainView.axaml.cs b/BassBoom/Views/MainView.axaml.cs index db35082..e65eb64 100644 --- a/BassBoom/Views/MainView.axaml.cs +++ b/BassBoom/Views/MainView.axaml.cs @@ -120,6 +120,7 @@ public void SelectDriver() { string answer = selection.SelectionInput; selectedDriver = answer; + DeviceTools.SetActiveDriver(selectedDriver); }; if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) selection.ShowDialog(desktop.MainWindow); @@ -154,13 +155,15 @@ public void SelectDevice() dialog.ShowAsync(); return; } - var devices = DeviceTools.GetDevices(selectedDriver); + string activeDevice = selectedDevice; + var devices = DeviceTools.GetDevices(selectedDriver, ref activeDevice); var deviceArray = devices.Keys.ToArray(); var selection = new SelectionWindow(new ObservableCollection(deviceArray)); selection.Closed += (s, e) => { string answer = selection.SelectionInput; - selectedDriver = answer; + selectedDevice = answer; + DeviceTools.SetActiveDevice(selectedDriver, selectedDevice); }; if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) selection.ShowDialog(desktop.MainWindow); diff --git a/Directory.Build.props b/Directory.Build.props index 5d8722e..4a2ca93 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,5 @@ - enable 11.0.2