// ============================================================================= // TeslaLauncher — system master volume (shared with vPOD) // ============================================================================= // The Console's set_VolumeLevel ends here. Setter chain: // nircmd.exe (legacy, works everywhere incl. XP, if present in the games dir) // → Core Audio (Vista+) → winmm waveOutSetVolume (XP fallback). // // Like MiniZip.cs, this file is compiled into BOTH the launcher and vPOD // (linked source): vPOD's "Actually set system volume" mode applies the // console's volume commands through the exact code the real pod runs. // ============================================================================= using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; namespace Tesla.Launcher { internal static class VolumeControl { /// Sets the system master volume to /// (clamped to 0.0–1.0). is probed for /// nircmd.exe first (the legacy path used on the original pods); the /// Windows APIs are the fallback. Never throws. public static void SetMasterScalar(float scalar, string nircmdDir) { scalar = Math.Max(0f, Math.Min(1f, scalar)); if (!string.IsNullOrEmpty(nircmdDir)) { var nircmd = Path.Combine(nircmdDir, "nircmd.exe"); if (File.Exists(nircmd)) { try { var p = Process.Start(nircmd, "setsysvolume " + (int)(scalar * 65535)); if (p != null) p.Dispose(); return; } catch { /* fall through to API */ } } } if (Environment.OSVersion.Version.Major >= 6) { try { CoreAudio.SetMasterScalar(scalar); return; } catch { /* fall through */ } } WinMmVolume.SetMasterScalar(scalar); } } // ── Windows Core Audio API — minimal COM interop (Vista+) ───────────────── // No external dependencies. Vtable slot order matches the SDK headers exactly. // Methods we don't call are declared as void stubs to preserve vtable offsets. // NOT available on XP — callers must gate on OS version. internal static class CoreAudio { internal static void SetMasterScalar(float scalar) { var ep = GetEndpointVolume(); try { var ctx = Guid.Empty; ep.SetMasterVolumeLevelScalar(scalar, ref ctx); } finally { Marshal.ReleaseComObject(ep); } } private static IAudioEndpointVolume GetEndpointVolume() { var enumerator = (IMMDeviceEnumerator)new MMAudioEnumeratorComClass(); try { IMMDevice device; enumerator.GetDefaultAudioEndpoint(0 /*eRender*/, 1 /*eMultimedia*/, out device); try { var iid = typeof(IAudioEndpointVolume).GUID; object obj; device.Activate(ref iid, 23 /*CLSCTX_ALL*/, IntPtr.Zero, out obj); return (IAudioEndpointVolume)obj; } finally { Marshal.ReleaseComObject(device); } } finally { Marshal.ReleaseComObject(enumerator); } } } [ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] internal class MMAudioEnumeratorComClass { } [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IMMDeviceEnumerator { void _unused_EnumAudioEndpoints(); // slot 0 — not used [PreserveSig] int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint); } [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IMMDevice { [PreserveSig] int Activate(ref Guid iid, int clsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object interfacePointer); } // Vtable order (after IUnknown): RegisterControlChangeNotify(0), // UnregisterControlChangeNotify(1), GetChannelCount(2), // SetMasterVolumeLevel(3), SetMasterVolumeLevelScalar(4), // GetMasterVolumeLevel(5), GetMasterVolumeLevelScalar(6) [Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IAudioEndpointVolume { void _unused_RegisterControlChangeNotify(); // slot 0 void _unused_UnregisterControlChangeNotify(); // slot 1 void _unused_GetChannelCount(); // slot 2 [PreserveSig] int SetMasterVolumeLevel(float levelDB, ref Guid ctx); [PreserveSig] int SetMasterVolumeLevelScalar(float level, ref Guid ctx); [PreserveSig] int GetMasterVolumeLevel(out float levelDB); [PreserveSig] int GetMasterVolumeLevelScalar(out float level); } // ── winmm wave-out volume (XP fallback) ─────────────────────────────────── // waveOutSetVolume with device -1 sets the wave mixer level of the default // device: low 16 bits = left channel, high 16 bits = right channel. internal static class WinMmVolume { [DllImport("winmm.dll")] private static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume); internal static void SetMasterScalar(float scalar) { uint level = (uint)(Math.Max(0f, Math.Min(1f, scalar)) * 0xFFFF); try { waveOutSetVolume(IntPtr.Zero, (level << 16) | level); } catch { } } } }