Get/Set Sound volume level programmatically

Just starting out? Need help? Post your questions and find answers here.
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Get/Set Sound volume level programmatically

Post by luis »

netmaestro wrote:... and my wife is very impressed.
:)

Thanks for sharing the result. Any idea if this works for a limited account too ? After all it's changing a system wide setting.
"Have you tried turning it off and on again ?"
A little PureBasic review
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Get/Set Sound volume level programmatically

Post by netmaestro »

You raise a good point, one I confess I didn't think of :oops: I logged out of my admin account and logged in as guest. I was unable to change any files, though I could read them and I wasn't allowed to change the system time. However, I was allowed to mute the speakers. I did that and then ran the QuickAlarm program. When the alarm time came it successfully turned on the speakers and turned them up, so I'm going to assume that user privilege level isn't going to affect it. Thanks for the heads up!
BERESHEIT
User avatar
luis
Addict
Addict
Posts: 3893
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Get/Set Sound volume level programmatically

Post by luis »

That's good to know thank you :wink:
"Have you tried turning it off and on again ?"
A little PureBasic review
Joris
Addict
Addict
Posts: 890
Joined: Fri Oct 16, 2009 10:12 am
Location: BE

Re: Get/Set Sound volume level programmatically

Post by Joris »

netmaestro wrote:@Joris: Thanks, I'll encase the new sound stuff in a CompilerIf block for the windows version to prevent crashes and include a note in the About box saying that for XP and earlier it won't force the sound.
That would be nice.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
User avatar
minimy
Enthusiast
Enthusiast
Posts: 552
Joined: Mon Jul 08, 2013 8:43 pm
Location: off world

Quest: Speaker Output VU Meter - Who know how?

Post by minimy »

Very good topic!
Someone knows how to get the values ​​of the speaker output.
volume changes in the sound as a vumeter.
I saw a topic, very good indeed, that of metergadget, but only talk about the mic input and output nothing about.
sourceforge is an example in the Core Audio API, but is in C.
You can use coreaudioapi.dll with PureBasic?
I think that's the way you and I see you know a lot about.
Can anyone help?
If translation=Error: reply="Sorry, Im Spanish": Endif
User avatar
DarkRookie
User
User
Posts: 20
Joined: Wed Nov 24, 2010 5:47 pm

Re: Get/Set Sound volume level programmatically

Post by DarkRookie »

Saw this and thought I could use it. Tried code in DEMO 5.21, but it is not working.
Get the following error
Image

Is this a limitation of the demo version?
It is better to have a dumb question than a wrong answer!
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Get/Set Sound volume level programmatically

Post by TI-994A »

DarkRookie wrote:Is this a limitation of the demo version?
Hi DarkRookie. Yes, it is. The demo version does not support constants, interfaces or structures, and there're no predefined API functions.
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Get/Set Sound volume level programmatically

Post by BarryG »

I'm getting an occasional crash ("Access violation") with this procedure on the "hr=defaultDevice\[...]" line:

Code: Select all

Procedure GetEndPointVolume(*endpointvolume)
  Protected deviceEnumerator.IMMDeviceEnumerator
  Protected hr,defaultDevice.IMMDevice
  CoInitialize_(#Null)
  hr=CoCreateInstance_(?uuidof_MMDeviceEnumerator,#Null,#CLSCTX_INPROC_SERVER,?uuidof_IMMDeviceEnumerator,@deviceEnumerator)
  hr=deviceEnumerator\GetDefaultAudioEndpoint(0,1,@defaultDevice)
  deviceEnumerator\release()
  hr=defaultDevice\Activate(?uuidof_IAudioEndpointVolume,#CLSCTX_INPROC_SERVER,#Null,*endpointVolume) ; <<< CRASH!
  defaultDevice\release()
  ProcedureReturn *endpointvolume
EndProcedure
Is the variable "hr" meant to be of ".i" type maybe? The crash doesn't happen all the time so I can't test if ".i" will make a difference. Windows 11 Pro.
User avatar
idle
Always Here
Always Here
Posts: 5836
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: Get/Set Sound volume level programmatically

Post by idle »

maybe you should check the return value hr to see if it got the defaultDevice interface

Code: Select all

Procedure GetEndPointVolume(*endpointvolume)
  Protected deviceEnumerator.IMMDeviceEnumerator
  Protected hr, defaultDevice.IMMDevice
  
  CoInitialize_(#Null)
 
  hr = CoCreateInstance_(?uuidof_MMDeviceEnumerator, #Null, #CLSCTX_INPROC_SERVER, ?uuidof_IMMDeviceEnumerator,@deviceEnumerator);#CLSCTX_ALL
  If hr = 0
    hr = deviceEnumerator\GetDefaultAudioEndpoint(0, 1, @defaultDevice)
    deviceEnumerator\Release()
    If hr = 0 
      hr = defaultDevice\Activate(?uuidof_IAudioEndpointVolume, #CLSCTX_INPROC_SERVER, #Null, *endpointVolume);
      defaultDevice\release()
    EndIf 
  EndIf   
  ProcedureReturn *endpointvolume
EndProcedure
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Get/Set Sound volume level programmatically

Post by BarryG »

Thanks, Idle! My Win 11 laptop does have some sound issues so I will use your error-checking version and see how it goes.
pthien
Enthusiast
Enthusiast
Posts: 148
Joined: Sun Jun 29, 2003 9:39 pm

Re: Get/Set Sound volume level programmatically

Post by pthien »

netmaestro wrote: Sat Feb 09, 2013 8:10 am All replies are much appreciated. The code from ts-soft works well but misses a couple of procedures, namely MediaGetMute() and MediaSetMute(), which if were included would make it perfect for my needs. I really don't need to support XP and earlier anyway and it would be a fair amount of work to put an include together that would serve both XP and Vista+ equally well. So I added the needed procs to ts-soft's code and now I have a complete working solution, which I have implemented in my alarm clock and now it's "mute-proof" and my wife is very impressed.

This is my SoundControls.pbi, most of it by ts-soft:

Code: Select all

#CLSCTX_INPROC_SERVER  = $01
#CLSCTX_INPROC_HANDLER = $02
#CLSCTX_LOCAL_SERVER   = $04
#CLSCTX_REMOTE_SERVER  = $10

#CLSCTX_ALL    = #CLSCTX_INPROC_SERVER|#CLSCTX_INPROC_HANDLER|#CLSCTX_LOCAL_SERVER|#CLSCTX_REMOTE_SERVER
#CLSCTX_INPROC = #CLSCTX_INPROC_SERVER|#CLSCTX_INPROC_HANDLER
#CLSCTX_SERVER = #CLSCTX_INPROC_SERVER|#CLSCTX_LOCAL_SERVER|#CLSCTX_REMOTE_SERVER

Interface IMMDeviceEnumerator Extends IUnknown
  EnumAudioEndpoints(dataFlow, dwStateMask, *ppDevices)
  GetDefaultAudioEndpoint(dataFlow, role, ppEndpoint)
  GetDevice(pwstrId, ppDevice)
  RegisterEndpointNotificationCallback(pClient)
  UnregisterEndpointNotificationCallback(pClient)
EndInterface

Interface IAudioEndpointVolume Extends IUnknown
  RegisterControlChangeNotify( *pNotify)
  UnregisterControlChangeNotify( *pNotify)
  GetChannelCount( *pnChannelCount)
  SetMasterVolumeLevel( fLevelDB.f)
  SetMasterVolumeLevelScalar( fLevel.f,pguidEventContext)
  GetMasterVolumeLevel(*pfLevelDB)
  GetMasterVolumeLevelScalar(*pfLevel)
  SetChannelVolumeLevel(nChannel,fLevelDB.f,pguidEventContext)
  SetChannelVolumeLevelScalar( nChannel,fLevel.f,pguidEventContext)
  GetChannelVolumeLevel( nChannel,*pfLevelDB.f)
  GetChannelVolumeLevelScalar( nChannel,*pfLevel.f)
  SetMute( bMute, pguidEventContext)
  GetMute( *pbMute)
  GetVolumeStepInfo( *pnStep, *pnStepCount)
  VolumeStepUp( pguidEventContext)
  VolumeStepDown( pguidEventContext)
  QueryHardwareSupport( *pdwHardwareSupportMask)
  GetVolumeRange( *pflVolumeMindB, *pflVolumeMaxdB, *pflVolumeIncrementdB)
EndInterface

Interface IMMDevice Extends IUnknown
  Activate(iid, dwClsCtx, pActivationParams, ppInterface);
  OpenPropertyStore( stgmAccess, ppProperties);
  GetId(ppstrId);
  GetState(pdwState);
EndInterface

Procedure GetEndPointVolume(*endpointvolume)
  Protected deviceEnumerator.IMMDeviceEnumerator
  Protected hr, defaultDevice.IMMDevice
  
  CoInitialize_(#Null)
  
  hr = CoCreateInstance_(?uuidof_MMDeviceEnumerator, #Null, #CLSCTX_INPROC_SERVER, ?uuidof_IMMDeviceEnumerator,@deviceEnumerator);#CLSCTX_ALL
  
  hr = deviceEnumerator\GetDefaultAudioEndpoint(0, 1, @defaultDevice)
  deviceEnumerator\Release()
  
  hr = defaultDevice\Activate(?uuidof_IAudioEndpointVolume, #CLSCTX_INPROC_SERVER, #Null, *endpointVolume);
  defaultDevice\release()
  
  ProcedureReturn *endpointvolume
EndProcedure

Procedure FreeEndPointVolume(*endpointvolume.IAudioEndpointVolume) 
  *endpointvolume\release()
  CoUninitialize_()
EndProcedure

Procedure MediaGetVolume()
  Protected Volume.IAudioEndpointVolume
  Protected volf.f
  
  GetEndPointVolume(@Volume)
  Volume\GetMasterVolumeLevelScalar(@volf)
  volf = Round(volf * 100, #PB_Round_Nearest)
  FreeEndPointVolume(Volume)
  ProcedureReturn Int(volf)
EndProcedure

Procedure MediaGetMute()
  Protected Volume.IAudioEndpointVolume
  Protected mute.i
  GetEndPointVolume(@Volume)
  Volume\GetMute(@mute)
  FreeEndPointVolume(Volume)
  ProcedureReturn mute
EndProcedure

Procedure MediaSetVolume(vol)
  Protected Volume.IAudioEndpointVolume
  Protected volf.f
  If vol < 0 : vol = 0 : EndIf
  If vol > 100 : vol = 100 : EndIf
  GetEndPointVolume(@Volume)
  volf = vol / 100
  Volume\SetMasterVolumeLevelScalar(volf, #Null)
  FreeEndPointVolume(Volume)
EndProcedure

Procedure MediaSetMute(value)
  Protected Volume.IAudioEndpointVolume
  Protected mute.i=1
  GetEndPointVolume(@Volume)
  Volume\SetMute(value, #Null)
  FreeEndPointVolume(Volume)
EndProcedure

DataSection
  uuidof_IAudioEndpointVolume:
  Data.l $5CDF2C82
  Data.w $841E,$4546
  Data.b $97,$22,$0C,$F7,$40,$78,$22,$9A
  
  uuidof_MMDeviceEnumerator:
  Data.l $BCDE0395
  Data.w $E52F,$467C
  Data.b $8E,$3D,$C4,$57,$92,$91,$69,$2E
  
  uuidof_IMMDeviceEnumerator:
  Data.l $A95664D2
  Data.w $9614
  Data.w $4F35
  Data.b $A7,$46,$DE,$8D,$B6,$36,$17,$E6
  
EndDataSection 

; Test prog: Set volume to 10% and mute the speakers, this should play loudly anyway...
If MediaGetMute()
  MediaSetMute(0)
EndIf
MediaSetVolume(100)
Beep_(2500,500)
This code only seems to actually set mute, if I've already done so using the mute key on my keyboard. That is, if I reboot and use the MediaSetMute(1) function, the audio is not muted. If, OTOH, I mute and then unmute the audio via my keyboard, THEN the MediaSetMute(1) seems to work fine.

Any ideas?
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Get/Set Sound volume level programmatically

Post by BarryG »

The code here (in this topic) no longer compiles with PureBasic 6.20 -> https://www.purebasic.fr/english/viewto ... 65#p403765

It's by ts-soft. If you're here, can you maybe fix it, please? The error -> "Line 30: Native types can't be used with pointers."
Fred
Administrator
Administrator
Posts: 18153
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: Get/Set Sound volume level programmatically

Post by Fred »

Just remove the type and it should work
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Get/Set Sound volume level programmatically

Post by BarryG »

Oh, I see. I did that and it works! Thanks, Fred. :) I originally didn't know what that error message meant.

Old lines:

Code: Select all

GetChannelVolumeLevel(nChannel,*pfLevelDB.f)
GetChannelVolumeLevelScalar(nChannel,*pfLevel.f)
New lines:

Code: Select all

GetChannelVolumeLevel(nChannel,*pfLevelDB)
GetChannelVolumeLevelScalar(nChannel,*pfLevel)
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Get/Set Sound volume level programmatically

Post by BarryG »

Just a heads-up: if your PC does not have a sound card, or if it's disabled in Device Manager, then the below crash will occur. Took me forever to work out this problem - who would've guessed that some PCs run without audio! :( So now I just need a reliable way to detect if a sound card is present or enabled so I can prevent this crash occurring. Anyone know?

Code: Select all

Procedure MediaGetVolume()
  Protected Volume.IAudioEndpointVolume
  Protected volf.f
  GetEndPointVolume(@Volume)
  Volume\GetMasterVolumeLevelScalar(@volf) ; Invalid access 0x3221225477 due to no sound card.
  volf=Round(volf*100,#PB_Round_Nearest)
  FreeEndPointVolume(Volume)
  ProcedureReturn Int(volf)
EndProcedure
Post Reply