• Welcome to Theos PowerBasic Museum 2017.

News:

Attachments are only available to registered users.
Please register using your full, real name.

Main Menu

Reading and changing the master volume on Vista/7

Started by Bud Meyer, July 07, 2009, 04:59:30 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Bud Meyer

I'm trying to get/set/mute the master volume on Vista, but I don't know how to do direct interface stuff.

This is the file:
endpointvolume.inc

This is the interface:
IAudioEndpointVolume

With these methods:
SetMasterVolumeLevel
SetMasterVolumeLevelScalar
GetMasterVolumeLevel
GetMasterVolumeLevelScalar
SetMute
GetMute

http://blogs.msdn.com/larryosterman/archive/2007/03/06/how-do-i-change-the-master-volume-in-windows-vista.aspx

I think it's something like...
DIM aev AS IAudioEndpointVolume
aev = NEWCOM $IID_IAudioEndpointVolume
LOCAL vol AS SINGLE
aev.GetMasterVolumeLevelScalar(vol)
? STR$(vol)


But it crashes. :(

Any help please? :)

Patrice Terrier

    LOCAL pIAudioEndpointVolume AS IAudioEndpointVolume

    LET pIAudioEndpointVolume = NEWCOM CLSID $IID_IAudioEndpointVolume
    IF NOT ISNOTHING(pIAudioEndpointVolume) THEN
       LOCAL currentVolume AS SINGLE
       pIAudioEndpointVolume.GetMasterVolumeLevel(currentVolume)
       pIAudioEndpointVolume.SetMasterVolumeLevel(currentVolume, "")
       pIAudioEndpointVolume = NOTHING
    END IF
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Bud Meyer

#2
Thanks Patrice, but it doesn't work for me. I'm using Vista x64 if it matters.


#COMPILE EXE
#DIM ALL
#INCLUDE "endpointvolume.inc"

FUNCTION PBMAIN () AS LONG
   LOCAL pIAudioEndpointVolume AS IAudioEndpointVolume

   LET pIAudioEndpointVolume = NEWCOM CLSID $IID_IAudioEndpointVolume
   IF NOT ISNOTHING(pIAudioEndpointVolume) THEN
      LOCAL currentVolume AS SINGLE
      pIAudioEndpointVolume.GetMasterVolumeLevel(currentVolume)
      'pIAudioEndpointVolume.SetMasterVolumeLevel(currentVolume, "")
      pIAudioEndpointVolume = NOTHING
       ? FORMAT$(currentVolume)
   ELSE
       ? "failure."
   END IF
END FUNCTION

Patrice Terrier

I am using it myself, in the MovieBox project on VISTA 64.

...

Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Bud Meyer

hmm.. strange. Would you compile the exe in my previous post and test it out? If it works, please post the exe so I can figure out if the problem is on my end.

Patrice Terrier

Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Bud Meyer

I've read that link but it doesn't help much since I don't know C++ very well. If the simplest of code doesn't work for me, then I assume either my integrated sound is not supported, or my Windows installation has a problem.

Patrice Terrier

Bud,

Anyway it is not a good idea to change the whole audio level on Vista and Windows 7, since now you can setup individualy for each running application.

I remember from the top of my head, that is the reason why i changed to the IBasicAudio interface in MovieBox when i switched to VISTA.

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Bud Meyer

I specifically want to control the master volume, just like I could on XP. I just wish there was a non-COM way of doing it, since it doesn't work for me.  :-[

Patrice Terrier

The old mixer API of XP, doesn't exist anymore on VISTA, hence the reason why you must go the COM way.

...
Patrice Terrier
GDImage (advanced graphic addon)
http://www.zapsolution.com

Bud Meyer

Yeah, that's mostly true.

I can mute the master volume with this, so that's no problem for me:
keybd_event(%VK_VOLUME_MUTE, 0, 0, 0)

And I use this to get/set the volume:
mixerSetControlDetails(hMixer, mcd, %MIXER_GETCONTROLDETAILSF_VALUE)
mixerSetControlDetails(hMixer, mcd, %MIXER_SETCONTROLDETAILSF_VALUE)
but of course in Vista that only works for my program instead of the main volume.

I tried your MusicBox which is nice, but the included code has IAudioEndpointVolume commented out. Do you have a working program that uses it successfully?

Bud Meyer

OK, I've made progress! :D
This successfully reads the main volume level and returns a value between 0 and 1.
I still need to do more work on setting the volume, and getting/setting mute.
I'm brand new to this COM stuff, so if anyone sees something wrong, please let me know.

#COMPILE EXE
#DIM ALL
#INCLUDE "mmdeviceapi.inc"
#INCLUDE "endpointvolume.inc"

FUNCTION PBMAIN () AS LONG
    LOCAL pIMMDeviceEnumerator  AS IMMDeviceEnumerator
    LOCAL pIMMDevice            AS IMMDevice
    LOCAL pIAudioEndpointVolume AS IAudioEndpointVolume

    LET pIMMDeviceEnumerator  = NEWCOM CLSID $IID_IMMDeviceEnumerator
    LET pIMMDevice            = NEWCOM CLSID $IID_IMMDevice
    LET pIAudioEndpointVolume = NEWCOM CLSID $IID_IAudioEndpointVolume

    LOCAL hr AS LONG
    LOCAL x AS IUNKNOWN
    hr = CoCreateInstance(BYREF $CLSID_MMDeviceEnumerator, x, %CLSCTX_INPROC_SERVER, $IID_IMMDeviceEnumerator, pIMMDeviceEnumerator)
    pIMMDeviceEnumerator.GetDefaultAudioEndpoint(%eRender, %eConsole, pIMMDevice)
    pIMMDevice.Activate(BYREF $IID_IAudioEndpointVolume, %CLSCTX_ALL, BYVAL %NULL, pIAudioEndpointVolume)

    IF NOT ISNOTHING(pIAudioEndpointVolume) THEN
        LOCAL currentVolume AS SINGLE
        pIAudioEndpointVolume.GetMasterVolumeLevelScalar(currentVolume)
        ? FORMAT$(currentVolume)
        'currentVolume = 0.5
        'pIAudioEndpointVolume.SetMasterVolumeLevelScalar(currentVolume)
    ELSE
        ? "failure."
    END IF

    pIMMDeviceEnumerator  = NOTHING
    pIMMDevice            = NOTHING
    pIAudioEndpointVolume = NOTHING
END FUNCTION
                                                                         

José Roca

Remove


    LET pIMMDeviceEnumerator  = NEWCOM CLSID $IID_IMMDeviceEnumerator
    LET pIMMDevice            = NEWCOM CLSID $IID_IMMDevice
    LET pIAudioEndpointVolume = NEWCOM CLSID $IID_IAudioEndpointVolume


and change


    LOCAL hr AS LONG
    LOCAL x AS IUNKNOWN
    hr = CoCreateInstance(BYREF $CLSID_MMDeviceEnumerator, x, %CLSCTX_INPROC_SERVER, $IID_IMMDeviceEnumerator, pIMMDeviceEnumerator)


to


    pIMMDeviceEnumerator  = NEWCOM CLSID $CLSID_MMDeviceEnumerator


José Roca

#13
 
Just to help you a little with your learning of COM:

NEWCOM CLSID does the same that CoCreateInstance and must be used with ClsIDs (class identifiers), not with IIDs (interface identifiers).

Therefore, the following statements are incorrect and will fail:


    LET pIMMDeviceEnumerator  = NEWCOM CLSID $IID_IMMDeviceEnumerator
    LET pIMMDevice            = NEWCOM CLSID $IID_IMMDevice
    LET pIAudioEndpointVolume = NEWCOM CLSID $IID_IAudioEndpointVolume


You can use CoCreateInstance if you prefer it, but NEWCOM CLSID is shorter and easier to use.

When using CoCreateInstance or other API functions that have a parameter declared as IUnknown, IDispatch or another interface name and you want to pass a null reference, you don't need to declare an uninitialized object variable and pass it, but you can pass NOTHING.

Therefore, instead of:


   LOCAL hr AS LONG
   LOCAL x AS IUNKNOWN
   hr = CoCreateInstance(BYREF $CLSID_MMDeviceEnumerator, x, %CLSCTX_INPROC_SERVER, $IID_IMMDeviceEnumerator, pIMMDeviceEnumerator)


you can use:


   LOCAL hr AS LONG
   hr = CoCreateInstance($CLSID_MMDeviceEnumerator, NOTHING, %CLSCTX_INPROC_SERVER, $IID_IMMDeviceEnumerator, pIMMDeviceEnumerator)


Notice also that the BYREFs in


   hr = CoCreateInstance(BYREF $CLSID_MMDeviceEnumerator, x, %CLSCTX_INPROC_SERVER, $IID_IMMDeviceEnumerator, pIMMDeviceEnumerator)
   pIMMDevice.Activate(BYREF $IID_IAudioEndpointVolume, %CLSCTX_ALL, BYVAL %NULL, pIAudioEndpointVolume)


are unneeded.


Bud Meyer

Thanks for the wisdom José. :)

I think I've completed my goal. If anything looks wrong, such as error checking, cleanup, etc, please let me know.

#COMPILE EXE
#DIM ALL
#INCLUDE "mmdeviceapi.inc"
#INCLUDE "endpointvolume.inc"

FUNCTION PBMAIN () AS LONG
    LOCAL pIMMDeviceEnumerator  AS IMMDeviceEnumerator
    LOCAL pIMMDevice            AS IMMDevice
    LOCAL pIAudioEndpointVolume AS IAudioEndpointVolume
    LOCAL vol AS SINGLE
    LOCAL mute AS LONG
   
    pIMMDeviceEnumerator = NEWCOM CLSID $CLSID_MMDeviceEnumerator
    IF NOT ISNOTHING(pIMMDeviceEnumerator) THEN

        pIMMDeviceEnumerator.GetDefaultAudioEndpoint(%eRender, %eConsole, pIMMDevice)
        IF NOT ISNOTHING(pIMMDevice) THEN

            pIMMDevice.Activate($IID_IAudioEndpointVolume, %CLSCTX_ALL, BYVAL %NULL, pIAudioEndpointVolume)
            IF NOT ISNOTHING(pIAudioEndpointVolume) THEN
                vol = 0.75
                pIAudioEndpointVolume.SetMasterVolumeLevelScalar(vol, BYVAL %NULL)
                pIAudioEndpointVolume.GetMasterVolumeLevelScalar(vol)
                ? "Volume level set to: " & FORMAT$(vol * 100) & "%" & $CRLF & "Click OK to toggle mute"
                pIAudioEndpointVolume.GetMute(mute)
                pIAudioEndpointVolume.SetMute(IIF&(mute=0, 1, 0), BYVAL %NULL)
            END IF

        END IF

    END IF

    pIMMDeviceEnumerator  = NOTHING
    pIMMDevice            = NOTHING
    pIAudioEndpointVolume = NOTHING
END FUNCTION