Announcement

Collapse
No announcement yet.

RegisterControlChangeNotify does not appear to work

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • RegisterControlChangeNotify does not appear to work

    Yes, I know there are some old work arounds prior to PBWIN 9; but, I'd like to figure out why it is not doing what it is supposed to do. I've been pulling my hair out. Is it the include files or my method?

    It is in endpointvolume.inc under INTERFACE IAudioEndpointVolume $IID_IAudioEndpointVolume. Any ideas?

    This is how I'm using it:
    Given ppNotify is defined as IAudioEndpointVolumeCallback
    Code:
    xhr = gpIAudioEndpointVolumeStereoSpeakers.RegisterControlChangeNotify( _
                 BYVAL ppNotify) 'IAudioEndpointVolumeCallback
      IF ISOBJECT(ppNotify) THEN
        ? "RegisterControlChangeNotify"
      END IF
    The interface is this:
    Code:
    METHOD RegisterControlChangeNotify ( _ ' VTable offset = 12
      BYVAL pNotify AS IAudioEndpointVolumeCallback _ ' __in IAudioEndpointVolumeCallback *pNotify
      ) AS LONG ' HRESULT
    Which in turn calls this:
    Code:
    INTERFACE IAudioEndpointVolumeCallback $IID_IAudioEndpointVolumeCallback
    
    INHERIT IUNKNOWN
    
    ' =====================================================================================
    METHOD OnNotify ( _ ' VTable offset = 12
      BYREF pNotify AS AUDIO_VOLUME_NOTIFICATION_DATA _ ' __in PAUDIO_VOLUME_NOTIFICATION_DATA pNotify
      ) AS LONG ' HRESULT
    ' =====================================================================================
    
    END INTERFACE
    Which populates this:
    Code:
    ' // Size = 32 bytes
    TYPE AUDIO_VOLUME_NOTIFICATION_DATA DWORD
        guidEventContext AS GUID ' GUID
        bMuted AS LONG ' BOOL
        fMasterVolume AS SINGLE ' float
        nChannels AS DWORD ' UINT
        afChannelVolumes(0) AS SINGLE ' float
    END TYPE

  • #2
    An absolute WAG, but do you need to pass OBJPTR(ppNotify) ?

    Comment


    • #3
      RegisterControlChangeNotify works pretty well on my side.
      Here is my callback...

      Click image for larger version  Name:	Volume.png Views:	203 Size:	18.4 KB ID:	793551

      Code:
      '_____________________________________________________________________________
      
      CLASS AudioEndpointVolumeCallbackOnNotify AS COM
      
       INTERFACE IAudioEndpointVolumeCallback $IID_IAudioEndpointVolumeCallback
      
         INHERIT IUNKNOWN 'Interface inherits from
      
         METHOD OnNotify(BYREF pNotify AS AUDIO_VOLUME_NOTIFICATION_DATA) AS LONG
      
           IF VARPTR(pNotify) = %NULL THEN
             METHOD = %E_INVALIDARG
           ELSE
             IF (hDlg <> %NULL) AND (pNotify.guidEventContext <> guidWinMasterVolContext) THEN
      
               'Post notification to main dialog
               'PostMessage(hDlg, %WM_APP, %ToolbarWinMasterWinMasterMuteButton, pNotify.bMuted)
               'PostMessage(hDlg, %WM_APP, %TrackbarWinMasterVol, pNotify.fMasterVolume * %TrackbarWinMasterMax) '%TrackbarWinMasterMax = 100
      
             END IF
             METHOD = %S_OK
           END IF
      
         END METHOD
      
       END INTERFACE
      
      END CLASS
      '_____________________________________________________________________________
      '
      Last edited by Pierre Bellisle; 13 Apr 2021, 04:10 AM.

      Comment


      • #4
        Hmm, Interesting Pierre! I was looking into producing a class but had not finished it yet. Thank you!
        Thank you Stuart

        Comment


        • #5
          Pierre,
          Can you register for both Speakers Volume/Mute and Microphone Volume/Mute? Likely I should use 2 classes to do that referencing AUDIO_VOLUME_NOTIFICATION_DATA1 for Speakers Volume/Mute and AUDIO_VOLUME_NOTIFICATION_DATA2 for Microphone Volume/Mute.

          If I get that far then making a third class for microphone boost should not be a problem. Is that what you would do?

          Comment


          • #6
            I did a quick and dirty test registering speaker volume/mute
            and mike volume/mute to the same callback and it was triggered by both.
            As said quick and dirty, I only add a beep in the callback.

            So all should go real fine with two or more classes.

            Comment


            • #7
              Ok Thanks

              This is the Class version I have:
              Code:
              ' ########################################################################################
              ' Class CIAudioEndpointVolumeCallback
              ' Interface name = _IAudioEndpointVolumeCallback
              ' IID = {657804FA-D6AD-4496-8A60-352752AF4F89}
              ' Inherited interface = IUnknown
              ' ########################################################################################
              
              CLASS CIAudioEndpointVolumeCallback AS COM
              
               INTERFACE IAudioEndpointVolumeCallback $IID_IAudioEndpointVolumeCallback
              
                INHERIT IUNKNOWN
              
                 ' =====================================================================================
                 METHOD OnNotify ( _                                    ' VTable offset = 12
                   BYREF pNotify AS AUDIO_VOLUME_NOTIFICATION_DATA _  ' __in PAUDIO_VOLUME_NOTIFICATION_DATA pNotify
                 ) AS LONG                                            ' HRESULT
              
                   'Method-Callback  that receives a pointer to the sample buffer.
                   'Callback method for endpoint-volume-change notifications.
                   'LOCAL TrackBarPos AS LONG
              
                       WINBEEP(7000, 75)
                   IF VARPTR(pNotify) = %NULL THEN
                     METHOD = %E_INVALIDARG
                   ELSE
                     IF (hDlg <> %NULL) AND (pNotify.guidEventContext <> g_guid_1_Context) THEN
              
                       'Post notification to main dialog
                       PostMessageA (hDlg, %WM_APP, %CheckboxVolOnOffSpeaker, pNotify.bMuted)
                       PostMessageA (hDlg, %WM_APP, %TrackbarVolSpeaker, pNotify.fMasterVolume * %TrackbarMax) '%TrackbarWinMasterMax = 100
              
                     END IF
                     METHOD = %S_OK
                   END IF
                 END METHOD
                 ' =====================================================================================
              
               END INTERFACE
              
              END CLASS
              This is how I'm calling it.
              Code:
              FUNCTION SetVolumeCallback AS LONG
                  'Pointer to the IAudioEndpointVolumeCallback interface that the client
                  'is registering for notification callbacks. If the
                  'RegisterControlChangeNotify method succeeds, it calls the AddRef
                  'method on the client's IAudioEndpointVolumeCallback interface.
              
                  LOCAL xhr   AS LONG
              
                  IF ISOBJECT(gpIAudioEndpointVolumeStereoSpeakers) THEN
              
                      xhr = CoCreateGuid (g_guid_1_Context)
              
                      LET ppNotify = CLASS "CIAudioEndpointVolumeCallback"
              
                      xhr = gpIAudioEndpointVolumeStereoSpeakers.RegisterControlChangeNotify(BYVAL ppNotify) 'IAudioEndpointVolumeCallback
                      IF ISOBJECT(ppNotify) THEN
                          ? "RegisterControlChangeNotify"
                      END IF
              
                      IF FAILED(xhr) THEN
                          ? "Unable to set stereo speaker callback."
                          FUNCTION = 0
                          EXIT FUNCTION
                      ELSE
                          '? "Default stereo speaker callback."
                      END IF
              
                  END IF
                  FUNCTION = 1
              END FUNCTION
              Initial test show it is working. Thank Heavens.

              Comment


              • #8
                These work exceedingly well:
                The first for speakers.
                The second for microphone.
                Now I just have to do a third for microphone Boost.
                All with the same AUDIO_VOLUME_NOTIFICATION_DATA.
                Very cool!
                Code:
                ' ########################################################################################
                ' Class CIAudioEndpointVolumeCallback
                ' Interface name = _IAudioEndpointVolumeCallback
                ' IID = {657804FA-D6AD-4496-8A60-352752AF4F89}
                ' Inherited interface = IUnknown
                ' ########################################################################################
                
                CLASS CIAudioEndpointVolumeCallback_1 AS COM
                
                 INTERFACE IAudioEndpointVolumeCallback $IID_IAudioEndpointVolumeCallback
                
                  INHERIT IUNKNOWN
                
                   ' =====================================================================================
                   METHOD OnNotify ( _                                      ' VTable offset = 12
                     BYREF pNotify_1 AS AUDIO_VOLUME_NOTIFICATION_DATA _    ' __in PAUDIO_VOLUME_NOTIFICATION_DATA pNotify
                   ) AS LONG                                            ' HRESULT
                
                     'Method-Callback  that receives a pointer to the sample buffer.
                     'Callback method for endpoint-volume-change notifications.
                
                     IF VARPTR(pNotify_1) = %NULL THEN
                       METHOD = %E_INVALIDARG
                     ELSE
                       IF (hDlg <> %NULL) THEN
                           IF (pNotify_1.guidEventContext <> g_guid_1_Context) THEN
                             PostMessageA (hDlg, %WM_APP, %CheckboxVolOnOffSpeaker, pNotify_1.bMuted)
                             PostMessageA (hDlg, %WM_APP, %TrackbarVolSpeaker, pNotify_1.fMasterVolume * %TrackbarMax)
                           END IF
                       END IF
                
                       METHOD = %S_OK
                     END IF
                   END METHOD
                   ' =====================================================================================
                
                 END INTERFACE
                
                END CLASS
                
                ' ########################################################################################
                ' Class CIAudioEndpointVolumeCallback
                ' Interface name = _IAudioEndpointVolumeCallback
                ' IID = {657804FA-D6AD-4496-8A60-352752AF4F89}
                ' Inherited interface = IUnknown
                ' ########################################################################################
                
                CLASS CIAudioEndpointVolumeCallback_2 AS COM
                
                 INTERFACE IAudioEndpointVolumeCallback $IID_IAudioEndpointVolumeCallback
                
                  INHERIT IUNKNOWN
                
                   ' =====================================================================================
                   METHOD OnNotify ( _                                      ' VTable offset = 12
                     BYREF pNotify_2 AS AUDIO_VOLUME_NOTIFICATION_DATA _    ' __in PAUDIO_VOLUME_NOTIFICATION_DATA pNotify
                   ) AS LONG                                            ' HRESULT
                
                     'Method-Callback  that receives a pointer to the sample buffer.
                     'Callback method for endpoint-volume-change notifications.
                
                     IF VARPTR(pNotify_2) = %NULL THEN
                       METHOD = %E_INVALIDARG
                     ELSE
                       IF (hDlg <> %NULL) THEN
                           IF (pNotify_2.guidEventContext <> g_guid_2_Context) THEN
                             'Post notification to main dialog
                             PostMessageA (hDlg, %WM_APP, %CheckboxVolOnOffMicro, pNotify_2.bMuted)
                             PostMessageA (hDlg, %WM_APP, %TrackbarVolMicro, pNotify_2.fMasterVolume * %TrackbarMax)
                           END IF
                       END IF
                
                       METHOD = %S_OK
                     END IF
                   END METHOD
                   ' =====================================================================================
                
                 END INTERFACE
                
                END CLASS

                Comment


                • #9
                  Looking at the code again, I removed some old experiment residue from it.
                  Added more infos on how to treat sub channels if needed...


                  Code:
                  '_____________________________________________________________________________
                  
                  CLASS AudioEndpointVolumeCallbackOnNotify AS COM
                  
                   INTERFACE IAudioEndpointVolumeCallback $IID_IAudioEndpointVolumeCallback
                  
                     INHERIT IUNKNOWN 'Interface inherits from
                  
                     METHOD OnNotify(NotificationData AS AUDIO_VOLUME_NOTIFICATION_DATA) AS LONG
                  
                       PostMessage(hDlg, %WM_APP, %ButtonMute,  NotificationData.bMuted)
                       PostMessage(hDlg, %WM_APP, %TrackbarVol, NotificationData.fMasterVolume * 100)
                       METHOD = %S_OK
                  
                       'OutputDebugString("guidEventContext "        & GUIDTXT$(NotificationData.guidEventContext))
                       'OutputDebugString("guidWinMasterVolContext " & GUIDTXT$(guidWinMasterVolContext))
                       'OutputDebugString("Muted "          & FORMAT$(NotificationData.bMuted))
                       'OutputDebugString("MasterVolume "   & FORMAT$(NotificationData.fMasterVolume, "0.000"))
                       'OutputDebugString("Channels count " & FORMAT$(NotificationData.nChannels))
                       'LOCAL index          AS LONG
                       'LOCAL pChannelVolume AS SINGLE POINTER
                       'pChannelVolume = VARPTR(NotificationData.afChannelVolumes(0))
                       'FOR index = 0 TO NotificationData.nChannels - 1
                       '  OutputDebugString(FORMAT$(index) & ") Volume " & FORMAT$(@pChannelVolume[index], "0.000"))
                       'NEXT
                  
                     END METHOD
                  
                   END INTERFACE
                  
                  END CLASS
                  '_____________________________________________________________________________
                  '
                  Last edited by Pierre Bellisle; 13 Apr 2021, 04:11 AM.

                  Comment


                  • #10
                    Nice, I'm not there yet. Speakers Volume plus Mute/Unmute are good. Microphone Volume plus Mute/Unmute are good.
                    Where I'm having a problem is the Microphone Boost.

                    Hmm I think I figured it out.

                    from IPart there is this:
                    Code:
                       METHOD RegisterControlChangeCallback ( _             ' VTable offset = 56
                         BYREF riid AS GUID _                               ' __in REFGUID riid
                       , BYVAL pNotify AS IControlChangeNotify _            ' __in IControlChangeNotify *pNotify
                       ) AS LONG                                            ' HRESULT
                    Looks like I'll have to create another class for that.

                    Comment


                    • #11
                      Jim, I guess you are referring to this thread of yours... Microphone with boost controls
                      After a quick test on my side under Windows 7, mute and vol works ok, the boost no.
                      Did you test on many machine?

                      Comment


                      • #12
                        Actually that post was a precursor to what I'm working on now. It had no callbacks. Currently I'm upgrading it. I only have WIN 10.

                        Comment


                        • #13
                          I found why boost did not work for me. The precursor code was not language independent.
                          You uses "Microphone Boost" and I got "Ampli microphone".


                          Comment


                          • #14
                            Yes, correct.

                            Comment


                            • #15
                              Still working on a callback for the Microphone Boost among other things. I implemented Speech Recognition so the microphone shows up solidly in the taskbar notification area. The speech recognition is just there to get the microphone hot. That is all. Recognition results are not interpreted.
                              All that is gained from this will be applied to the new VOX. VOX3. I figured it would be a pain.

                              This is something new to the recognition portion:
                              Code:
                              '_________________________________________________________________
                              '
                              ' SUB RECOGNITION_ENABLE                 new 2020 MAY 13 new
                              '
                              ' Includes workaround for recognition volume AGC forcing volume to 91
                              ' The solution was to capture the volume level before using %SGDSActive
                              ' and then reapplying the value after %SGDSActive was executed.
                              ' For some unknown reason the callback didn't get a new level so the
                              ' trackbar had to be refreshed as well.
                              '_________________________________________________________________
                              
                              SUB RECOGNITION_ENABLE
                                LOCAL LastValue AS SINGLE
                              
                                'NOTE: oMyGrammar.DictationSetState(%SGDSActive)           'Turn Dictation on.
                                'This causes the microphone volume to move to 91 the second time it is enabled
                                'instead of leaving the volume where it was originally set.
                                'Microsoft's way of making speech recognition idiot proof.
                              
                                'Start the recognition by activating one of the grammars
                              
                                IF ISTRUE ISOBJECT(oRecoContext) THEN
                                    CALL GetVolumeMicrophone                   'workaround
                                    LastValue = giMasterMicrophoneLevel/100    'workaround
                              
                                    IF giCCGrammarLoaded = 1 THEN
                                        IF gsBannerDictation = "*" THEN
                                            oMyGrammar.DictationSetState(%SGDSInactive)     'Turn Dictation off.
                                            oMyGrammar.CmdSetRuleIdState(0, %SGDSActive)    'turn command / contol on
                                        ELSE
                                            oMyGrammar.CmdSetRuleIdState(0, %SGDSInactive)  'turn command / contol off
                                            oMyGrammar.DictationSetState(%SGDSActive)       'Turn Dictation on.
                                        END IF
                                    ELSE
                                        oMyGrammar.DictationSetState(%SGDSActive)           'Turn Dictation on.
                                    END IF
                              
                                    CALL SetVolumeMicrophone(LastValue)        'workaround
                                    CALL RefreshVolumeTrackbarPos(LastValue)   'workaround
                                ELSE
                                    CALL PutEqualSignOnMarque
                                END IF
                              END SUB
                              I'm loading Dictation grammar only but this change will also work with a Command and Control Grammar.

                              Comment


                              • #16
                                Yeah Haw success.
                                Here is the class:
                                Code:
                                ' ########################################################################################
                                ' Class CIControlChangeNotify
                                ' Interface name = IControlChangeNotify
                                ' IID = A09513ED-C709-4D21-BD7B-5F34C47F3947
                                ' Inherited interface = IUnknown
                                ' ########################################################################################
                                
                                CLASS CIControlChangeNotify AS COM
                                
                                 INTERFACE IControlChangeNotify $IID_IControlChangeNotify
                                
                                    INHERIT IUNKNOWN
                                
                                    ' =====================================================================================
                                    METHOD OnNotify ( _                                  ' VTable offset = 12
                                      BYVAL dwSenderProcessId AS DWORD _                 ' __in DWORD dwSenderProcessId
                                    , BYREF pguidEventContext AS GUID _                  ' __in LPCGUID pguidEventContext
                                    ) AS LONG                                            ' HRESULT
                                    ' =====================================================================================
                                       IF (hDlg <> %NULL) THEN
                                           IF (pguidEventContext <> g_guid_3_Context) THEN
                                             'Get the microphone boost values
                                             CALL GetVolumeMicrophoneBoost
                                           END IF
                                       END IF
                                
                                       METHOD = %S_OK
                                     END METHOD
                                
                                 END INTERFACE
                                
                                END CLASS
                                Here is the way the Microphone Boost callback is set:

                                Code:
                                FUNCTION SetMicrophoneBoostCallback AS LONG
                                
                                    LOCAL xhr   AS LONG
                                
                                    IF ISOBJECT(pPartNext) THEN
                                
                                        xhr = CoCreateGuid (g_guid_3_Context)
                                
                                        LET ppNotify_3 = CLASS "CIControlChangeNotify"
                                
                                        xhr = pPartNext.RegisterControlChangeCallback( _
                                                 IID_IAudioVolumeLevel, _
                                                 BYVAL ppNotify_3)
                                
                                        IF FAILED(xhr) THEN
                                            ? "Unable to set microphone boost callback."
                                            FUNCTION = 0
                                            EXIT FUNCTION
                                        ELSE
                                            ? "Default microphone boost callback."
                                        END IF
                                
                                    END IF
                                    FUNCTION = 1
                                END FUNCTION

                                Comment


                                • #17
                                  This looks real good Jim!
                                  Thanks for sharing.
                                  Nice work... Microphone with boost controls v2

                                  To my copy, I added...
                                  Code:
                                  [noparse]
                                  CASE %ButtonSndRecord
                                    pid = SHELL("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,1")
                                  [/noparse]

                                  Comment


                                  • #18
                                    Thank you, Sir! It was a hard won battle.
                                    Next will be the integration into VOX3 an SDK app and eventually into xBot.

                                    Comment


                                    • #19
                                      I believe you about the hard won battle...
                                      Good news is those are the ones that taste so much better...
                                      Enjoy!
                                      You deserve it!

                                      Comment


                                      • #20
                                        Jim, those might be nice to add to avoid leaks...
                                        IAudioEndpointVolume::unRegisterControlChangeNotify
                                        IPart::unRegisterControlChangeCallback

                                        Comment

                                        Working...
                                        X