4

I want to find the current system volume and set the volume of my app inside the volume mixer but it seems that everything I find:

  • Can change the system volume (one problem sorted)
  • Cannot change the application volume
  • Or simply doesn't work at all
  • And so far, nothing at all can manage to GET me the system volume

We are currently using nircmd to set the global volume and that works great but it doesn't seem to have an option to GET the current volume so we can later restore the volume to what it was. As for setting the value of the app itself, rather than the global volume, I have been hunting down every thread I can find and everything (on here as well as elsewhere) all lead me to either C++ documentation or to C# wrappers around that library but that code simply doesn't work due to that invalid cast right at the root of every function.

Here is one such an example of the broken code from right here on stackoverflow: Controlling Volume Mixer

All those libs/code samples I found do this at some point:

private static ISimpleAudioVolume GetVolumeObject(int pid)
   {
       IMMDeviceEnumerator deviceEnumerator = 
       (IMMDeviceEnumerator)(new MMDeviceEnumerator());

This gives an invalid cast problem due to this:

internal class MMDeviceEnumerator
{
}
internal interface IMMDeviceEnumerator
{
}

The class it is trying to cast to does not implement the interface and apparently thus the cast is not valid. I tried to manually add the functions to MAKE it implement the interface but since that is simply a hack it causes other errors down the line and is not a viable option.

Besides, since absolutely every code snippet or lib I have found on here or elsewhere does this exact same thing it seems unlikely that such a change would have gone unnoticed. Since all the code is many years old, can I assume that this simply doesn't work in Win10?

If so, then how can I do those two things I mentioned?

  • Find the current system volume and
  • Set the current application's volume per attached hardware device?

I also tried fetching the app process for SndVol.exe and successfully find it (or open it if it's not running) but once I have the handle for the mixer I have no idea what to do with it. Is it possible to send commands to the application via the input override available through the Process class? I am totally inexperienced with the Process class so forgive me if that question sounds stupid.

EDIT: My question is simply this: How can I change the individual sliders inside Volume Mixer using.NET. For some reason people are reading this as "How to use Unity?". For that reason I want to pose my question another way that will hopefully make it sound less "esoteric magic" and more "general use case" (hopefully)... :P

Say I wanted to detect the systems' attached audio devices and add a drop down to my app listing those devices, If I then wanted to add a volume slider for each running app so I could adjust the app's volume for that particular device from within my own app, exactly like Volume Mixer does... how would I go about implementing that?

More specifically, the thing i want to know is how do I get my slider values applied to the individual application's volume for the selected device?

JansenSensei
  • 61
  • 1
  • 6
  • What type of application are you writing? Windows Native, UWP, .net core? – NineBerry Aug 06 '19 at 09:40
  • I am writing an app inside of the Unity games engine targeting the Windows Native desktop, 64 bit only. It is a highly specialized app and all the (what I think is) hard parts are basically done... It's just these seemingly simple little things that are causing us this grief :( So, to answer your question again: It's a Windows Native 64bit desktop app. Oh, full .NET (no code stripping) – JansenSensei Aug 06 '19 at 18:08
  • Applications are not supposed to change the system volume. It's also quite dangerous if (what it sounds like) you want to change the system volume without user interaction. You could seriously injure someone wearing headphones by increasing the system volume without user interaction. – NineBerry Aug 06 '19 at 19:58
  • I am a bit in doubt that your unity application is a. Net Framework application. From what I know about unity, on Windows it uses Mono or UWP, not . Net Framework – NineBerry Aug 06 '19 at 20:06

2 Answers2

2

You need to use the Win32 API to do this. http://www.blackwasp.co.uk/BasicVolumeControl.aspx

https://learn.microsoft.com/en-gb/windows/win32/api/winuser/nf-winuser-get_appcommand_lparam

SendMessage(this.Handle, WM_APPCOMMAND, IntPtr.Zero, value);
Kieran Devlin
  • 1,373
  • 1
  • 12
  • 28
  • Can it really be THAT easy? :O Checking that out immediately! Thanks very much for the fast reply! – JansenSensei Aug 06 '19 at 09:56
  • 1
    If the answer helped you solve your question, please can you mark it as the accepted answer and give it an upvote? – Kieran Devlin Aug 06 '19 at 11:39
  • I had a look at that but unfortunately it was too good to be true. :( For one thing, it only shows how to change the master volume up/down/mute it. That I can already do. I need to FIND the current master volume and set the volume of the APP inside the mixer but this blackwasp link only demonstrates "Basic Volume Control" and doesn't go beyond that. I've been trying to find more commandsto send the audio system but have found none. The biggest problem, though, is that when I copy paste the code from BlackWasp I get no errors... but I also don't see any changes in the volume. Darn it. So close! – JansenSensei Aug 06 '19 at 15:20
  • What are you using for the handle? It cant be a console handle as you cant send a message via the API like that. If you're using unity, you can get the handle from the window that they provide you. Here's an example: https://github.com/KieranDevvs/Win32VolumeDemo – Kieran Devlin Aug 06 '19 at 20:55
  • I was actually _wondering_ if the handle I'm using is correct. I currently use two handles but neither is working. Both come from Process.Handle. The 1st handle is the Windows Volume Mixer process handle. The second one is the handle of the active process: Process.GetCurrentProcess().Handle. (See the code sample below) I was wondering if this is the right way to get the handle and maybe that is why SendMessage isn't working. Your example uses the form's "this". I'm not sure how that translates into Unity's handle. I'll see if the docs expose that somewhere. Thanks for the heads up – JansenSensei Aug 07 '19 at 00:49
  • For SendMessage, you don't need a process handle, but a window handle – NineBerry Aug 07 '19 at 09:20
  • To my greatest surprise, I discovered that SendMessage works perfectly fine as long as you set Unity to build a 32 bit app. As soon as you change it to a 64 bit app it stops working again. Who knew!? :O Now that THAT is out of the way, we can return to the questions in the main thread. Kieran's sample calls volume up/down using his own handle. I use Volume Mixer and my app's handle. All adjusts the system volume. That is the only thing I was already able to set BEFORE this thread. Now i have a new way of doing that but I still don't know how to do any of the things I originally asked :( – JansenSensei Aug 07 '19 at 09:42
  • I asked you yesterday if you wanted to change the system volume or the application volume and you said you wanted to change the system volume. If you want to modify the application volume then use the Unity API. – Kieran Devlin Aug 07 '19 at 09:59
  • Based on my readings it sounds like I have to implement an override for GetMessage (and/or call SendMessage to a custom int value in place of the System Volume Up/Down consts, passing params via LValue). This will then localize the SendMessage to the app rather than working on the main system volume but then what? This brings us back to "How to set the App's volume within the mixer"? Use SendMessage to tell my app to call SendMessage to call SendMessage to [insert infinite loop here]. How to actually adjust the volume of an APP rather than the system volume? And how to GET the current volume? – JansenSensei Aug 07 '19 at 10:06
  • Setting Unity's volume to 100% inside the app and then opening up the Mixer and saying "Play unity @ max 5% of the system volume" defeats the purpose. I don't want to know how to change the volume *inside* my app, I want to know hwo to modify the values of the apps inside the *Volume Mixer* – JansenSensei Aug 07 '19 at 10:10
  • I think this will explain it best: [https://ibb.co/3Wbm6fF](https://ibb.co/3Wbm6fF) The issue I am having is that I want complete control over the ACTUAL physicsal real world volume but the mixer allows you to change the volume relatively on a per-device basis. I want the headphones and the speakers and the monitor and [insert whatever else here] to always be at 100% relative to the system volume. Thus, I am asking how to change the volume inside the Audio Mixer. – JansenSensei Aug 07 '19 at 10:17
  • @JansenSensei Why doesn't setting the static property "AudioListener.volume" to 1 work for you? – NineBerry Aug 07 '19 at 10:37
  • Because Windows comes with an app called Volume Mixer that is used to change the volume of individual apps... This is why I posted the image, to avoid the confusion between changing the volume of a Unity component and setting the value of your audio inside of Windows directly. "My app" != Windows. UnityEngine.AudioLister != C:\Windows\System32\SndVol.exe – JansenSensei Aug 07 '19 at 10:51
  • The app volume shown in the windows mixer is the same that you set in unity via AudioListener.volume – NineBerry Aug 07 '19 at 11:16
  • If you say so... but then I ask the same question: How am I setting AudioListener.volume to 1 during Update() and yet I was still able to take a screenshot of Unity's volume NOT being at 100? Are you saying that Unity's audio__listener__ class should actually override a user manually __changing__ the volume inside of Windows using that slider? If only life were that simple you would be my hero! :D Allow me to rephrase my original question to make it less esoteric: I want to add native volume controls to my app that adjusts the different values inside Windows Volume Mixer. How can I do that? – JansenSensei Aug 07 '19 at 11:36
0

https://github.com/naudio/NAudio

This lib includes all the functionality I need! It also contains a demo that does EXACTLY what I posted in my edited original post... Gives me access to each audio device, lists it in a drop down, shows the volume of each individual app for the selected device and allows me to finally GET the audio volume (something that seemed impossible for some reason, up to now).

Compiling the source is a real pain as you need to install and then update FAKE before fixing the fake installer using F# (which I don't know) downloading dependencies every step of the way... before you can finally build the dlls that you need to build the demos... but downloading the binary and just dropping the dll in my Plugins folder works great! :D

Use the demo source as a guideline for how to use the DLL and it's all smooth sailing from there!

JansenSensei
  • 61
  • 1
  • 6