Set and Get volume system with event sound level modif

Share your advanced PureBasic knowledge/code with the community.
Nico
Enthusiast
Enthusiast
Posts: 274
Joined: Sun Jan 11, 2004 11:34 am
Location: France

Set and Get volume system with event sound level modif

Post by Nico »

Original code by ts-soft
Updated by netmaestro for Get and set Mute
Updated by Nico retrieve event sound level modification by user

For Vista and more.
Run as administrator account!

For compatibility x64 procedure InterlockedExchange asm by Mistrel implemented, here:
http://www.purebasic.fr/english/viewtop ... edExchange

Compile with thread activated.

Image

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

Enumeration ; Flow
  #eRender  ; se connecter sur audio
  #eCapture ; capturer audio
  #eAll     ; tous
EndEnumeration

Enumeration ;Role
  #eConsole ; system
  #eMultimedia ; (haut parleur ...)
  #eCommunications ; (micro...)
EndEnumeration

CompilerIf  #PB_Compiler_Thread = 0
  MessageRequester("Info", "Ce code se compile avec la Gestion des Threads cochée!")
  End 
CompilerEndIf

Interface IMMDeviceEnumerator Extends IUnknown
  EnumAudioEndpoints(Flow.l, dwStateMask.l, IMMDeviceCollection.i)
  GetDefaultAudioEndpoint(Flow.l, role.l, IMMDevice.i)       
  GetDevice(pwstrId.i, IMMDevice.i)        
  RegisterEndpointNotificationCallback(IMMNotificationClient.i) 
  UnregisterEndpointNotificationCallback(IMMNotificationClient.i)
EndInterface

Interface IAudioEndpointVolume Extends IUnknown
  RegisterControlChangeNotify(pNotify.i)
  UnregisterControlChangeNotify(pNotify.i)
  GetChannelCount(puchannelCount.i)
  SetMasterVolumeLevel(levelDB.f, *context.Guid)
  SetMasterVolumeLevelScalar(level.f, *context.Guid)
  GetMasterVolumeLevel(pflevelDB.i)
  GetMasterVolumeLevelScalar(pflevel.i)
  SetChannelVolumeLevel(uchannel.l, levelDB.f, *context.Guid)
  SetChannelVolumeLevelScalar(uchannel.l, level.f, *context.Guid)
  GetChannelVolumeLevel(uchannel.l, plevelDB.i)
  GetChannelVolumeLevelScalar(uchannel.l, pflevel.i)
  SetMute(mute.l, *context.Guid)
  GetMute(pmute.i)
  GetVolumeStepInfo(puStep.i, pustepCount.i)
  VolumeStepUp(*context.Guid)
  VolumeStepDown(*context.Guid)
  QueryHardwareSupport(pdwHardwareSupportMask.i)
  GetVolumeRange(pflVolumeMindB.i, pflVolumeMaxdB.i, pflVolumeIncrementdB.i)
EndInterface

Interface IMMDevice Extends IUnknown
  Activate(iid.i, dwClsCtx.l, pActivationParams.i, ppInterface.i)
  OpenPropertyStore(stgmAccess.i, IPropertyStore.i) 
  GetId(ppstrId.i)
  GetState(pdwState.i)
EndInterface  

Structure AUDIO_VOLUME_NOTIFICATION_DATA Align #PB_Structure_AlignC
  guidEventContext.GUID
  bMuted.l
  fMasterVolume.f
  nChannels.l
  afChannelVolumes.f[1]
EndStructure 

Interface IAudioEndpointVolumeCallback Extends IUnknown
  OnNotify(*pNotify.AUDIO_VOLUME_NOTIFICATION_DATA)
EndInterface

Structure AudioEndpointVolumeCallback Align #PB_Structure_AlignC
  *pAudioEndpointVolume
  QueryInterface.i
  AddRef.i
  Release.i
  OnNotify.i
  pAudioEndpointVolumeCallback.IAudioEndpointVolumeCallback
  Refcount.i
  Event.l
  MyGuid.GUID
EndStructure

Global AudioEndpointVolumeCallback.AudioEndpointVolumeCallback

Procedure.i ConvertToUnicode(AsciiOrUnicode.s)
  Protected *UnicodeString
  Protected LenMemoryUnicode.l
  
  LenMemoryUnicode = StringByteLength(AsciiOrUnicode , #PB_Unicode)
  *UnicodeString = AllocateMemory(LenMemoryUnicode + 2)
  If *UnicodeString <> 0
    PokeS(*UnicodeString, AsciiOrUnicode, Len(AsciiOrUnicode), #PB_Unicode)
    ProcedureReturn *UnicodeString
  EndIf
  
  ProcedureReturn 0
EndProcedure

Procedure.i CLSIDFromString(ClsidString.s, *CLSID.CLSID)
  Protected *UnicodeString
  
  If *CLSID <> 0
    If ClsidString <> ""
      CompilerIf #PB_Compiler_Unicode =0
        *UnicodeString = ConvertToUnicode(ClsidString)
      CompilerElse
        *UnicodeString = @ClsidString
      CompilerEndIf 
      If *UnicodeString <> 0
        If CLSIDFromString_(*UnicodeString, @REFIID.CLSID) >= #S_OK
          CopyMemory(REFIID, *CLSID, SizeOf(CLSID))
          ProcedureReturn *CLSID
        EndIf 
        CompilerIf #PB_Compiler_Unicode =0
          FreeMemory(*UnicodeString)
        CompilerEndIf
      EndIf
    EndIf
  Else
    ProcedureReturn 0
  EndIf
  
  FillMemory(*CLSID, SizeOf(CLSID),  #Null)
  ProcedureReturn *CLSID
EndProcedure

Procedure.i InterlockedExchange(*Destination.Integer, Value.i)
  
  EnableASM 
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !mov rcx, [p.p_Destination]
    !mov rax, [p.v_Value]
    !lock xchg [rcx], rax
  CompilerElse
    !mov ecx, [p.p_Destination]
    !mov eax, [p.v_Value]
    !lock xchg [ecx], eax
  CompilerEndIf 
  DisableASM 
  
  ProcedureReturn 
EndProcedure


Procedure.i AudioEndpointVolumeCallback_AddRef(*pObject.AudioEndpointVolumeCallback)
  Protected ulRef.i
  
  Debug "AudioEndpointVolumeCallback_AddRef"
  ulRef = InterlockedExchange(@*pObject\Refcount, *pObject\Refcount + 1)
  Debug ulRef + 1
  Debug "*pObject\Refcount = " + Str(*pObject\Refcount)
  
  ProcedureReturn ulRef + 1
EndProcedure

Procedure.l AudioEndpointVolumeCallback_QueryInterface(*pObject.AudioEndpointVolumeCallback, *REFIID.CLSID, *ppvObj.INTEGER)
  Protected REFIID.CLSID, Null.CLSID
  
  Debug "AudioEndpointVolumeCallback_QueryInterface +++++++++++++++++++++++++++++++++++"
  
  If *ppvObj = 0
    ProcedureReturn #E_POINTER 
    
  ElseIf CompareMemory(CLSIDFromString("{657804fa-d6ad-4496-8a60-352752af4f89}", @REFIID), *REFIID, 16)
    AudioEndpointVolumeCallback_AddRef(*pObject)
    *ppvObj\i = *pObject
    *pObject\pAudioEndpointVolumeCallback\AddRef()
  ElseIf CompareMemory(CLSIDFromString("{00000000-0000-0000-0000-000000000000}", @Null), *REFIID, 16)
    AudioEndpointVolumeCallback_AddRef(*pObject)
    *ppvObj\i = *pObject
    *pObject\pAudioEndpointVolumeCallback\AddRef()
  Else
    *ppvObj\i = #Null
    ProcedureReturn #E_NOINTERFACE
  EndIf
  
  ProcedureReturn #S_OK
EndProcedure

Procedure.i AudioEndpointVolumeCallback_Release(*pObject.AudioEndpointVolumeCallback)
  Protected ulRef.i
  
  Debug "AudioEndpointVolumeCallback_Release"
  
  ulRef = InterlockedExchange(@*pObject\Refcount, *pObject\Refcount - 1)
  Debug ulRef + 1
  
  Debug "*pObject\Refcount = " + Str(*pObject\Refcount)
  
  If ulRef = 0
    AudioEndpointVolumeCallback\pAudioEndpointVolumeCallback = 0
    Debug "delete element --------------------------------"
    *pObject\Refcount = 0
    *pObject = 0
  EndIf
  
  ProcedureReturn ulRef + 1
EndProcedure

Procedure.l AudioEndpointVolumeCallback_OnNotify(*pObject.AudioEndpointVolumeCallback, *pNotify.AUDIO_VOLUME_NOTIFICATION_DATA)
  Debug "AudioEndpointVolumeCallback_OnNotify"
  
  ;   Debug *pNotify\fMasterVolume
  ;   Debug "----------------------------------------------------"
  ;   Debug *pObject\MyGuid\Data1
  ;   Debug *pObject\MyGuid\Data2
  ;   Debug *pObject\MyGuid\Data3
  ;   Debug *pObject\MyGuid\Data4
  ;   Debug "----------------------------------------------------"
  ;   
  ;   Debug *pNotify\guidEventContext\Data1
  ;   Debug *pNotify\guidEventContext\Data2
  ;   Debug *pNotify\guidEventContext\Data3
  ;   Debug *pNotify\guidEventContext\Data4
  ;   Debug "----------------------------------------------------"
  
  If CompareMemory(*pObject\MyGuid, *pNotify\guidEventContext, 16) = 0
    SetGadgetState(0, 100 * *pNotify\fMasterVolume)
    
    If *pNotify\bMuted = 0
      SetGadgetText(1, "Mute Off")
    ElseIf *pNotify\bMuted = 1
      SetGadgetText(1, "Mute On") 
    EndIf
  EndIf 
  
  ;guidEventContext.GUID
  Debug *pNotify\bMuted.l
  Debug *pNotify\nChannels
  ; afChannelVolumes.f[1]
  ProcedureReturn #S_OK
EndProcedure

Procedure.i DataAudioEndPointVolume(Option.l, InterfaceCom.i=0)
  Static AudioEndPointVolume.i
  
  If Option = 1
    If InterfaceCom <> 0
      AudioEndPointVolume = InterfaceCom
      ProcedureReturn #True
    EndIf 
  ElseIf Option = 2
    If AudioEndPointVolume <> 0
      ProcedureReturn AudioEndPointVolume
    EndIf 
  ElseIf Option = 3
    AudioEndPointVolume = 0
    ProcedureReturn 0
  EndIf 
  
  ProcedureReturn #False 
EndProcedure

Procedure.l ComEvent_SetAudioEndpointVolumeCallback()
  Protected Volume.IAudioEndpointVolume
  Protected Guid.GUID
  
  Volume = DataAudioEndPointVolume(2)
  If Volume <> 0
    If AudioEndpointVolumeCallback <> 0
      If AudioEndpointVolumeCallback\Refcount = 0
        
        CopyMemory(CLSIDFromString("{00000000-1111-5555-0000-000000000000}", @Guid), AudioEndpointVolumeCallback\MyGuid, 16)
        
        AudioEndpointVolumeCallback\QueryInterface = @AudioEndpointVolumeCallback_QueryInterface()
        AudioEndpointVolumeCallback\AddRef = @AudioEndpointVolumeCallback_AddRef()
        AudioEndpointVolumeCallback\Release = @AudioEndpointVolumeCallback_Release()
        AudioEndpointVolumeCallback\OnNotify = @AudioEndpointVolumeCallback_OnNotify()
        
        AudioEndpointVolumeCallback\pAudioEndpointVolume = @AudioEndpointVolumeCallback\QueryInterface
        AudioEndpointVolumeCallback\pAudioEndpointVolumeCallback = @AudioEndpointVolumeCallback\pAudioEndpointVolume
        
        AudioEndpointVolumeCallback\pAudioEndpointVolumeCallback\AddRef()
      EndIf
      
      If Volume\RegisterControlChangeNotify(@AudioEndpointVolumeCallback) >= #S_OK
        ProcedureReturn #S_OK
      EndIf 
      
    EndIf 
  EndIf
  
  ProcedureReturn -1
EndProcedure

Procedure.l ComEvent_FreeAudioEndpointVolumeCallback()
  Protected Volume.IAudioEndpointVolume
  
  Volume = DataAudioEndPointVolume(2)
  If Volume <> 0
    If Volume\UnregisterControlChangeNotify(AudioEndpointVolumeCallback) >= #S_OK
      ProcedureReturn #S_OK
    EndIf 
  EndIf 
  
  ProcedureReturn -1 
EndProcedure

Procedure.l Com_GetDefaultAudioEndPointVolume()
  Protected pdeviceEnumerator.IMMDeviceEnumerator
  Protected pAudioEndpointVolume.IAudioEndpointVolume
  Protected pdefaultDevice.IMMDevice
  Protected AudioEndpointVolumeCallback.IAudioEndpointVolumeCallback, RET.l=-1
  
  If  CoCreateInstance_(?uuidof_MMDeviceEnumerator, #Null, #CLSCTX_INPROC_SERVER, ?uuidof_IMMDeviceEnumerator,@pdeviceEnumerator) < #S_OK
    Goto error
  ElseIf pdeviceEnumerator = 0
    Goto error
  EndIf
  
  If  pdeviceEnumerator\GetDefaultAudioEndpoint(#eRender, #eMultimedia, @pdefaultDevice) < #S_OK
    Goto error:
  ElseIf pdefaultDevice = 0
    Goto error
  EndIf
  
  If pdefaultDevice\Activate(?uuidof_IAudioEndpointVolume, #CLSCTX_INPROC_SERVER, #Null, @pAudioEndpointVolume.IAudioEndpointVolume) < #S_OK
    Goto error
  ElseIf pAudioEndpointVolume = 0
    Goto error
  Else
    RET = #S_OK
    Goto NoError
  EndIf
  
  Error:
  If pAudioEndpointVolume <> 0
    pAudioEndpointVolume\Release()
  EndIf 
  
  NoError:
  DataAudioEndPointVolume(1, pAudioEndpointVolume)
  
  If pdefaultDevice <> 0
    pdefaultDevice\Release()
  EndIf
  
  If pdeviceEnumerator <> 0
    pdeviceEnumerator\Release()
  EndIf
  
  ProcedureReturn RET
EndProcedure

Procedure.l Com_FreeDefaultAudioEndPointVolume()
  Protected Volume.IAudioEndpointVolume
  
  Volume = DataAudioEndPointVolume(2)
  If Volume <> 0
    Volume\release()
    DataAudioEndPointVolume(3)
    ProcedureReturn #S_OK
  EndIf
  ProcedureReturn -1
EndProcedure

Procedure.l MediaSystemGetVolume()
  Protected Volume.IAudioEndpointVolume
  Protected volf.f
  
  Volume = DataAudioEndPointVolume(2)
  If Volume <> 0
    If Volume\GetMasterVolumeLevelScalar(@volf) >= #S_OK
      volf = Round(volf * 100, #PB_Round_Nearest)
      ProcedureReturn Int(volf)
    EndIf 
  EndIf 
  ProcedureReturn -1 
EndProcedure

Procedure.l MediaSystemGetMute()
  Protected Volume.IAudioEndpointVolume
  Protected mute.l
  
  Volume = DataAudioEndPointVolume(2)
  If Volume <> 0
    If Volume\GetMute(@mute) >= #S_OK
      ProcedureReturn mute
    EndIf 
  EndIf 
  ProcedureReturn -1 
  
EndProcedure

Procedure.l MediaSystemSetVolume(vol.l)
  Protected Volume.IAudioEndpointVolume
  Protected volf.f
  
  If vol < 0 : vol = 0 : EndIf
  If vol > 100 : vol = 100 : EndIf
  
  Volume = DataAudioEndPointVolume(2)
  If Volume <> 0
    volf = vol / 100
    If Volume\SetMasterVolumeLevelScalar(volf, AudioEndpointVolumeCallback\MyGuid) >= #S_OK
      ProcedureReturn #True
    EndIf 
  EndIf 
  ProcedureReturn -1 
EndProcedure

Procedure.l MediaSystemSetMute(value.l)
  Protected Volume.IAudioEndpointVolume
  Protected mute.l=1
  
  Volume = DataAudioEndPointVolume(2)
  If Volume <> 0
    If Volume\SetMute(value, AudioEndpointVolumeCallback\MyGuid) >= #S_OK
      ProcedureReturn #True
    EndIf 
  EndIf 
  ProcedureReturn -1 
EndProcedure

; Procedure test()
;   Protected Volume.IAudioEndpointVolume
;   
;   ;test fonction queryinterface
;     Volume2 = DataAudioEndPointVolume(2)
;     AudioEndpointVolumeCallback_QueryInterface(Volume2, @REFIID.CLSID, @Volume)
;     
;     Volume\AddRef()
; EndProcedure

CoInitialize_(#Null)


If OpenWindow(0, 100, 200, 400, 400, "PureBasic Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  
  TrackBarGadget(0, 40, 40, 20, 200, 0, 100, #PB_TrackBar_Vertical | #PB_TrackBar_Ticks)
  ButtonGadget(1, 20, 280, 60, 60, "Mute Off")
  ButtonGadget(2, 100, 10, 100, 20,"Set Event")
  ButtonGadget(3, 100, 40, 100, 20,"Free Event")
  
  
  If Com_GetDefaultAudioEndPointVolume() = #S_OK
    Vol = MediaSystemGetVolume()
    SetGadgetState(0, Vol)
    
    Mute = MediaSystemGetMute()
    If Mute = 1
      SetGadgetText(0, "Mute On")
    EndIf 
    
    If ComEvent_SetAudioEndpointVolumeCallback() = #S_OK
      DisableGadget(2, 1)
    Else
      DisableGadget(2, 1)
      DisableGadget(3, 1)
    EndIf
    
  Else
    DisableGadget(0, 1)
    DisableGadget(1, 1)
    DisableGadget(2, 1)
    DisableGadget(3, 1)
  EndIf
  
  Repeat
    Event = WaitWindowEvent()
    
    Select Event 
      Case #PB_Event_Gadget
        Select EventGadget()
          Case 0
            MediaSystemSetVolume(GetGadgetState(0))
            
          Case 1
            If Mute = 0
              If  MediaSystemSetMute(1)
                Mute = 1
                SetGadgetText(1, "Mute On")
              EndIf 
            ElseIf Mute = 1
              If MediaSystemSetMute(0) 
                Mute = 0
                SetGadgetText(1, "Mute Off")
              EndIf 
            EndIf 
            
          Case 2
            If ComEvent_SetAudioEndpointVolumeCallback() = #S_OK
              DisableGadget(2, 1)
              DisableGadget(3, 0)
            EndIf
            
          Case 3
            If ComEvent_FreeAudioEndpointVolumeCallback() = #S_OK
              DisableGadget(3, 1)
              DisableGadget(2, 0)     
            EndIf
            
        EndSelect
        
      Case #PB_Event_CloseWindow 
        Quit = 1
    EndSelect
    
  Until Quit = 1
  
EndIf

Com_FreeDefaultAudioEndPointVolume()
CoUninitialize_()


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 

Nico
Enthusiast
Enthusiast
Posts: 274
Joined: Sun Jan 11, 2004 11:34 am
Location: France

Re: Set and Get volume system with event sound level modif

Post by Nico »

Code updated for compatibility x64, see first post.
Post Reply