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? :)
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
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
I am using it myself, in the MovieBox project on VISTA 64.
...
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.
Bud,
Check this:
Endpoint Volume Controls (http://msdn.microsoft.com/en-us/library/dd370839(VS.85).aspx)
...
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.
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.
...
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. :-[
The old mixer API of XP, doesn't exist anymore on VISTA, hence the reason why you must go the COM way.
...
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?
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
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
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.
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
I have expanded this sample code, trying to get the volume of avilable Audio Channel.
I have used:
LOCAL ch AS LONG, vol AS SINGLE
pIAudioEndpointVolume.SetChannelVolumeLevelScalar(ch,vol,BYVAL %NULL)
But it seems not to produce the expected Result.
Has anybody tried this before and knows whats wrong (why the volume doesn't change in the Mixer?)