Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Microphone with boost controls

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

  • PBWin Microphone with boost controls

    Click image for larger version

Name:	New Bitmap Image.jpg
Views:	576
Size:	16.1 KB
ID:	793416

    Code:
    #COMPILE EXE "MicrophoneWithBoost.exe"
    #DIM ALL
    
    'FINDING AND ADJUSTING MICROPHONE BOOST CODE TRANSLATED FROM THESE LINKS
    'http://dhavalbc.blogspot.com/2014/05/getting-and-setting-microphone-gain-or.html and
    'https://docs.microsoft.com/en-us/windows/win32/coreaudio/device-topologies
    
    'Works with PBWIN 10.4 and Jose' includes
    
    #REGISTER NONE
    
    '#INCLUDE ONCE "MB_API_A.inc"    'WHEN USED {2,640 total lines} COMPILE TIME 0.1 seconds
    'OR THESE
    #INCLUDE ONCE "Win32Api.inc"     'WHEN USED {278,386 total lines} COMPILE TIME 1.4 seconds
    #INCLUDE ONCE "CommCtrl.inc"       'Needed if manifest and WinXP
    #INCLUDE ONCE "mmdeviceapi.inc"    'used for WASAPI
    #INCLUDE ONCE "devpkey.inc"        'used for WASAPI
    #INCLUDE ONCE "propvarutil.inc"    'used for WASAPI
    #INCLUDE ONCE "endpointvolume.inc" 'used for WASAPI
    
    
    GLOBAL hWndMain              AS DWORD
    GLOBAL dwNoRetMLTextBoxStyle AS DWORD
    GLOBAL giProgStat            AS LONG
    GLOBAL hPeakMeter            AS DWORD
    GLOBAL PEAK                  AS SINGLE
    GLOBAL dwSProgressStyle      AS DWORD
    GLOBAL dwSNBProgressStyle    AS DWORD
    GLOBAL dwProgressStyle       AS DWORD
    GLOBAL dwNBProgressStyle     AS DWORD
    GLOBAL dwButtonStyle         AS DWORD
    
    GLOBAL SCALER                AS SINGLE
    GLOBAL SCALER_MB             AS SINGLE
    GLOBAL pDevId                AS WSTRINGZ PTR
    GLOBAL devName               AS WSTRING
    GLOBAL pName                 AS WSTRINGZ PTR  'LPWSTR pName
    
    GLOBAL giMicroMute           AS LONG 'Tracks Microphone Mute status
    GLOBAL pflow                 AS LONG 'DataFlow pflow
    GLOBAL pPartType             AS LONG 'PartType
    GLOBAL bConnected            AS LONG 'BOOL bConnected
    GLOBAL connType              AS LONG 'ConnectorType
    GLOBAL ComponentName         AS WSTRINGZ * 255
    
    GLOBAL XFill                 AS STRING  'used to post text to clipboard
    
    GLOBAL giPRTDMessage         AS LONG
    GLOBAL giPumpingMessages     AS LONG
    
    GLOBAL giMicrophone                      AS LONG   ' default microphone availablility  yes = 1, no = 0
    GLOBAL giMicrophoneHr                    AS LONG
    GLOBAL giMasterMicrophoneLevel           AS LONG
    GLOBAL gsTheMicrophoneName               AS STRING
    
    GLOBAL gpIMMDeviceEnumMicrophone         AS IMMDeviceEnumerator
    GLOBAL gpIMMDeviceMicrophone             AS IMMDevice
    GLOBAL gpIAudioEndpointVolumeMicrophone  AS IAudioEndpointVolume
    GLOBAL gpMeterInfo                       AS IAudioMeterInformation
    GLOBAL pDeviceTopology                   AS IDeviceTopology
    GLOBAL pConnFrom                         AS IConnector
    GLOBAL pConnTo                           AS IConnector
    GLOBAL pPartPrev                         AS IPart
    GLOBAL pPartNext                         AS IPart
    GLOBAL ppParts                           AS IPartsList
    GLOBAL pIaudioVolumeLevel                AS IAudioVolumeLevel
    GLOBAL pSelector                         AS IAudioInputSelector
    
    GLOBAL CLSID_MMDeviceEnumerator   AS GUID
    GLOBAL IID_IPart                  AS GUID
    GLOBAL IID_IAudioVolumeLevel      AS GUID
    GLOBAL IID_IConnector             AS GUID
    GLOBAL IID_IAudioInputSelector    AS GUID
    GLOBAL IID_IAudioEndpointVolume   AS GUID
    GLOBAL IID_IDeviceTopology        AS GUID
    GLOBAL IID_IAudioMeterInformation AS GUID
    
    %IDC_STATIC_MINVOL = 1001
    %IDC_STATIC_MAXVOL = 1002
    %IDC_PEAK_METER    = 1003
    %IDC_Timer0        = 1004
    %ID_VOLUME         = 1005
    %ID_MUTE           = 1006
    %ID_VUP            = 1007
    %ID_VDN            = 1008
    %ID_VUP_MB         = 1009
    %ID_VDN_MB         = 1010
    
    DECLARE SUB DrawPeakMeter(HWND AS DWORD, float AS SINGLE)
    
    
    '****************************
    ' **** MAIN ENTRY POINT ****
    '****************************
    
    FUNCTION WINMAIN (BYVAL hInstance AS DWORD, _
                      BYVAL hPrevInstance AS DWORD, _
                      BYVAL lpCmdLine AS ASCIIZ PTR, _
                      BYVAL iCmdShow AS LONG) AS LONG
    
        LOCAL Msg          AS tagMsg
        LOCAL W            AS WndClassExA
        LOCAL szAppName    AS ASCIIZ * 80
        LOCAL szClassName  AS ASCIIZ * 80
        LOCAL WndStyle     AS LONG
        LOCAL WndStyleX    AS LONG
    
        giProgStat = 1
    
        CLSID_MMDeviceEnumerator   = GUID$("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
        IID_IAudioMeterInformation = GUID$("{C02216F6-8C67-4B5B-9D00-D008E73E0064}")
        IID_IDeviceTopology        = GUID$("{2A07407E-6497-4A18-9787-32F79BD0D98F}")
        IID_IAudioEndpointVolume   = GUID$("{5CDF2C82-841E-4546-9722-0CF74078229A}")
        IID_IPart                  = GUID$("{AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9}")
        IID_IAudioVolumeLevel      = GUID$("{7FB7B48F-531D-44A2-BCB3-5AD5A134B3DC}")
        IID_IConnector             = GUID$("{9C2C4058-23F5-41DE-877A-DF3AF236A09E}")
        IID_IAudioInputSelector    = GUID$("{4F03DC02-5E6E-4653-8F72-A030C123D598}")
    
        'register window class
        szAppName = "Microphone with boost"
        szClassName = "#32770"
    
        W.cbSize        = SIZEOF(W)
        W.Style         = %CS_HREDRAW OR %CS_VREDRAW   '%WS_OverlappedWindow Or %WS_HScroll Or %WS_VScroll  '
        W.lpfnWndProc   = CODEPTR(WndProc)
        W.cbClsExtra    = 0
        W.cbWndExtra    = 0
        W.hInstance     = hInstance
        W.hIcon         = 0 'LoadImage(hInstance, BYVAL 3000, %IMAGE_ICON, 0, 0, %LR_LOADMAP3DCOLORS OR %LR_DEFAULTSIZE)
        W.hCursor       = LoadCursorA(%NULL, BYVAL %IDC_ARROW)
        W.hbrBackground = GetStockObject(%DKGRAY_BRUSH) '(%LTGRAY_BRUSH) '%COLOR_3DDKSHADOW 'COLOR_BTNSHADOW'%Color_BtnFace+1 (%COLOR_ACTIVEBORDER)
        W.lpszMenuName  = %NULL
        W.lpszClassName = VARPTR(szClassName)
        W.hIconSm       = W.hIcon
        RegisterClassExA W
    
    
        'create window of that class
        WndStyle  = %WS_VISIBLE OR %DS_MODALFRAME OR %WS_CAPTION OR %WS_SYSMENU OR %DS_SETFONT OR %DS_NOFAILCREATE 'OR %WS_POPUP OR %WS_BORDER OR %DS_MODALFRAME
        WndStyleX = %WS_EX_LEFT OR %WS_EX_WINDOWEDGE OR %WS_EX_APPWINDOW
    
        hWndMain = CreateWindowExA( _
            WndStyleX,     _  ' extended styles
            szClassName,   _  ' window class name
            szAppName,     _  ' caption
            WndStyle,      _  ' window styles
            300,           _  ' left
            300,           _  ' top
            270,           _  ' width
            190,           _  ' height
            %HWND_DESKTOP, _  ' parent window handle
            %NULL,         _  ' window menu handle
            hInstance,     _  ' program instance handle
            BYVAL %NULL)      ' creation parameters
    
        CALL CreateDlgControls
    
        ShowWindow hWndMain, iCmdShow   'controls how window is to be shown. 1st must use iCmdShow
        UpdateWindow hWndMain           'sends the window it's first WM_Create to display the window on the screen
    
        'message pump - calls WndProc whenever an application-specific message is received
        'WndProc can process the message, or pass it on to a the default window procedure that is built into Windows.
        WHILE GetMessageA(Msg, %NULL, 0, 0) > 0
          IF ISFALSE IsDialogMessageA (hWndMain, Msg) THEN
             TranslateMessage Msg
             DispatchMessageA  Msg
          END IF
        WEND
    END FUNCTION
    
    
    '//-----------------------------------------------------------
    '// DlgProc -- Dialog box procedure
    '//-----------------------------------------------------------
    
    FUNCTION WndProc (BYVAL hWnd AS DWORD, _
                      BYVAL wMsg AS DWORD, _
                      BYVAL wParam AS DWORD, _
                      BYVAL lParam AS LONG) EXPORT AS LONG
    
        LOCAL xhr      AS LONG
    
        '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        'Create a timer event every 1/8 Sec = 125
        '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        SetTimer hWndMain, %IDC_Timer0, 125, BYVAL %NULL
    
        SELECT CASE wMsg
    
            CASE %WM_CREATE
    
                gpMeterInfo = NOTHING
                giMicrophone     = MicrophoneCheck
                SetVolumeMicrophone(SCALER)
    
                'If an application processes this message, it should return zero to continue creation of the window.
                FUNCTION = 0
                EXIT FUNCTION
    
    
    
            CASE %WM_COMMAND
                SELECT CASE LO(WORD, wParam)
                    CASE %IDCANCEL
                        gpIMMDeviceEnumMicrophone = NOTHING
                        gpIMMDeviceMicrophone = NOTHING
                        gpMeterInfo = NOTHING
                        KillTimer (hWnd, %IDC_Timer0)
                        EndDialog(hWnd, %TRUE)
                        'function = 1
    
                    CASE %ID_MUTE
                        IF giMicrophone = 1 THEN
                            IF giMicroMute = 0 THEN
                                'mute microphone
                                CALL MuteMicrophone
                            ELSE
                                'unmute microphone
                                CALL UnMuteMicrophone
                            END IF
                        END IF
    
                    CASE %ID_VUP
                        SCALER = SCALER + .05
                        IF SCALER > 1 THEN SCALER = 1
                        SetVolumeMicrophone(SCALER)
                        'SCALER RANGE IS 0 TO 1
    
                    CASE %ID_VDN
                        SCALER = SCALER - .05
                        IF SCALER < 0 THEN SCALER = 0
                        SetVolumeMicrophone(SCALER)
                        'SCALER RANGE IS 0 TO 1
    
                    CASE %ID_VUP_MB
                        IF ISOBJECT(pIaudioVolumeLevel) THEN
                            SCALER_MB = SCALER_MB + 10
                            IF SCALER_MB > 30 THEN SCALER_MB = 30
                            pIaudioVolumeLevel.SetLevel(0, SCALER_MB, BYVAL %NULL)
                            'SCALER RANGE IS 0 TO 30
                        END IF
    
                    CASE %ID_VDN_MB
                        IF ISOBJECT(pIaudioVolumeLevel) THEN
                            SCALER_MB = SCALER_MB - 10
                            IF SCALER_MB < 0 THEN SCALER_MB = 0
                            pIaudioVolumeLevel.SetLevel(0, SCALER_MB, BYVAL %NULL)
                            'SCALER RANGE IS 0 TO 30
                        END IF
    
                END SELECT
                'If an application processes this message, it should return zero.
                FUNCTION = 0
                EXIT FUNCTION
    
            CASE %WM_TIMER
               SELECT CASE wParam
                   CASE %IDC_Timer0
                       '// Update the peak meter in the dialog box.
                       IF ISOBJECT(gpMeterInfo) THEN
                           gpMeterInfo.GetPeakValue(PEAK)
                           CONTROL SET TEXT hWndMain, %IDC_PEAK_METER, USING$("##.#", PEAK * 100)
                           CONTROL SEND hWndMain, %ID_VOLUME, %PBM_SETPOS, (PEAK * 100), 0
                       END IF
    
               END SELECT
               'An application should return zero if it processes this message.
               FUNCTION = 0
               EXIT FUNCTION
    
            CASE %WM_CLOSE
                giProgStat = 0
                DestroyWindow hWnd
    
            CASE %WM_DESTROY         'window is being destroyed
                                     'after windows is removed from screen (children still exist)
                pSelector                            = NOTHING
                gpIMMDeviceEnumMicrophone            = NOTHING
                gpIMMDeviceMicrophone                = NOTHING
                gpIAudioEndpointVolumeMicrophone     = NOTHING
                gpMeterInfo                          = NOTHING
                pDeviceTopology                      = NOTHING
                pConnFrom                            = NOTHING
                pConnTo                              = NOTHING
                pPartPrev                            = NOTHING
                pPartNext                            = NOTHING
                ppParts                              = NOTHING
                pIaudioVolumeLevel                   = NOTHING
    
                PostQuitMessage 0
                'If an application processes this message, it should return zero.
                FUNCTION = 0
                EXIT FUNCTION
    
        END SELECT
    
    
        FUNCTION = DefWindowProcA(hWnd, wMsg, wParam, lParam)   'if not handled above, pass to Windows default message handler.
    END FUNCTION
    
    
    SUB CreateDLGControls
      LOCAL MyCtrl   AS DWORD
      dwNoRetMLTextBoxStyle  = %WS_CHILD OR %WS_VISIBLE OR %ES_NOHIDESEL OR %ES_READONLY OR %WS_BORDER
      dwSProgressStyle       = %WS_CHILD OR %WS_VISIBLE OR %PBS_SMOOTH OR %WS_BORDER '%PBS_SMOOTH for undotted bar
      dwSNBProgressStyle     = %WS_CHILD OR %WS_VISIBLE OR %PBS_SMOOTH
      dwProgressStyle        = %WS_CHILD OR %WS_VISIBLE OR %WS_BORDER
      dwNBProgressStyle      = %WS_CHILD OR %WS_VISIBLE
      dwButtonStyle          = %WS_CHILD OR %WS_VISIBLE
    
    
      MyCtrl = addTextBox(hWndMain, %IDC_STATIC_MINVOL, "", 10, 12, 20, 24, dwNoRetMLTextBoxStyle OR %SS_LEFT)
      MyCtrl = addTextBox(hWndMain, %IDC_PEAK_METER, "", 31, 12, 40 + 164, 24, dwNoRetMLTextBoxStyle OR %SS_CENTER)
      MyCtrl = addTextBox(hWndMain, %IDC_STATIC_MAXVOL, "", 40 + 196, 12, 20, 24, dwNoRetMLTextBoxStyle OR %SS_RIGHT)
    
      MyCtrl = addProgress(hWndMain, %ID_VOLUME, "", 10, 40, 40 + 205, 12, dwSProgressStyle)
      CONTROL SEND hWndMain, %ID_VOLUME, %PBM_SETRANGE, 0, MAKLNG(0, 100)
      CONTROL SEND hWndMain, %ID_VOLUME, %PBM_SETSTEP, -1, 0
      CONTROL SEND hWndMain, %ID_VOLUME, %PBM_SETBKCOLOR, 0, RGB(50,50,50)
      CONTROL SEND hWndMain, %ID_VOLUME, %PBM_SETBARCOLOR, 0, RGB(55,224,230)
    
      MyCtrl = addButton(hWndMain, %ID_MUTE, "Mute/Unmute", 10, 60 + 13, 120, 24, dwButtonStyle)
      MyCtrl = addButton(hWndMain, %ID_VUP, "Volume Up", 10, 60 + 40, 120, 24, dwButtonStyle)
      MyCtrl = addButton(hWndMain, %ID_VDN, "Volume Down", 10, 60 + 67, 120, 24, dwButtonStyle)
    
      MyCtrl = addButton(hWndMain, %ID_VUP_MB, "Boost Up", 135, 60 + 40, 120, 24, dwButtonStyle)
      MyCtrl = addButton(hWndMain, %ID_VDN_MB, "Boost Down", 135, 60 + 67, 120, 24, dwButtonStyle)
    
    END SUB
    
    FUNCTION addTextBox(BYVAL hParent AS DWORD, BYVAL nID AS LONG, zCaption AS ASCIIZ, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL w AS LONG, BYVAL h AS LONG, BYVAL dwStyle AS DWORD) AS DWORD
        LOCAL hCtrl AS DWORD
        LOCAL hInstance AS DWORD
    
        hInstance = GetWindowLongA (hWndMain, %GWL_HINSTANCE)
    
        hCtrl = CreateWindowExA(%WS_EX_CLIENTEDGE, "Edit", zCaption, dwStyle, x, y, w, h, hParent, nID, hInstance, BYVAL %NULL)
        'DisableXPThemeControl hCtrl
        'IF hCtrl THEN SendMessage(hCtrl, %WM_SETFONT, gP.usefont, 0)
        FUNCTION = hCtrl
    END FUNCTION
    
    
    FUNCTION addProgress(BYVAL hParent AS DWORD, BYVAL nID AS LONG, zCaption AS ASCIIZ, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL w AS LONG, BYVAL h AS LONG, BYVAL dwStyle AS DWORD) AS DWORD
        LOCAL hCtrl AS DWORD
        LOCAL hInstance AS DWORD
    
        hInstance = GetWindowLongA (hWndMain, %GWL_HINSTANCE)
    
        hCtrl = CreateWindowExA (0, "MsCtls_Progress32", zCaption, dwStyle, x, y, w, h, hParent, nID, hInstance, BYVAL %NULL)
        'DisableXPThemeControl hCtrl
        'IF hCtrl THEN SendMessage(hCtrl, %WM_SETFONT, gP.usefont, 0)    '%WS_EX_TRANSPARENT
        FUNCTION = hCtrl
    END FUNCTION
    
    '_________________________________________________________________
    '
    ' FUNCTION addButton
    '_________________________________________________________________
    
    FUNCTION addButton( _
                BYVAL hParent AS DWORD _
              , BYVAL nID AS LONG      _
              , zCaption AS ASCIIZ     _
              , BYVAL x AS LONG        _
              , BYVAL y AS LONG        _
              , BYVAL w AS LONG        _
              , BYVAL h AS LONG        _
              , BYVAL dwStyle AS DWORD _
              ) AS DWORD
    
        LOCAL hCtrl AS DWORD
        LOCAL hInstance AS DWORD
    
        hInstance = GetWindowLongA (hWndMain, %GWL_HINSTANCE)
    
        hCtrl = CreateWindowExA( _
                     0            _
                   , "Button"     _
                   , zCaption     _
                   , dwStyle      _
                   , x, y, w, h   _
                   , hParent      _
                   , nID          _
                   , hInstance _
                   , BYVAL %NULL)
    
        'DisableXPThemeControl hCtrl
        'IF hCtrl THEN SendMessageA(hCtrl, %WM_SETFONT, gP.usefont, 0)
        FUNCTION = hCtrl
    END FUNCTION
    
    
    '_________________________________________________________________
    '
    '   FUNCTION  MuteMicrophone
    '_________________________________________________________________
    
    FUNCTION MuteMicrophone AS LONG
    
        LOCAL xhr   AS LONG
    
        IF giMicrophone = 1 THEN
    
            IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
                ' // Do whathever you wish, e.g. SetMasterVolumeLevel, SetMute, etc.
                ' // See available methods at http://msdn.microsoft.com/en-us/library/windows/desktop/dd370892%28v=vs.85%29.aspx
    
                xhr = gpIAudioEndpointVolumeMicrophone.SetMute(%TRUE, BYVAL %NULL)
                IF FAILED(xhr) THEN
                    'CALL SHOW_NOTES_NO_WAIT("Unable to mute default microphone.")
                    ? "Unable to set mute state on default microphone"
                ELSE
                    ' "Default microphone muted successfully!"
                    giMicroMute = 1
    
                    'CALL PutPeriodOnMarque
                END IF
    
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    '_________________________________________________________________
    '
    '   FUNCTION  UnMuteMicrophone
    '_________________________________________________________________
    
    FUNCTION UnMuteMicrophone AS LONG
    
        LOCAL xhr   AS LONG
    
        IF giMicrophone = 1 THEN
            IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
                ' // Do whathever you wish, e.g. SetMasterVolumeLevel, SetMute, etc.
                ' // See available methods at http://msdn.microsoft.com/en-us/library/windows/desktop/dd370892%28v=vs.85%29.aspx
    
                xhr = gpIAudioEndpointVolumeMicrophone.SetMute(%FALSE, BYVAL %NULL)
                IF FAILED(xhr) THEN
                    'CALL SHOW_NOTES_NO_WAIT("Unable to unmute default microphone.")
                    ? "Unable to set unmute state on default microphone"
                ELSE
                    ' "Default microphone unmuted successfully!"
                    giMicroMute = 0
    
                    'INCR giTestIt
                    'IF RecognitionStatus = 1 THEN
                    '    CALL PutQuestionMarkOnMarque
                    'ELSE
                    '    'recognition is not enabled
                    '    IF ISTRUE ISOBJECT(oRecoContext) THEN
                    '        CALL PutTildeOnMarque
                    '    ELSE
                    '        CALL PutEqualSignOnMarque
                    '    END IF
                    'END IF
                END IF
    
            END IF
        END IF
    
        FUNCTION = 1
    END FUNCTION
    '_________________________________________________________________
    '
    '   FUNCTION  SetVolumeMicrophone
    '_________________________________________________________________
    
    FUNCTION SetVolumeMicrophone(BYVAL MicroScalar AS SINGLE) AS LONG
    
        LOCAL xhr   AS LONG
    
        IF giMicrophone = 1 THEN
            IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
                ' // Do whathever you wish, e.g. SetMasterVolumeLevel, SetMute, etc.
                ' // See available methods at http://msdn.microsoft.com/en-us/library/windows/desktop/dd370892%28v=vs.85%29.aspx
    
                xhr = gpIAudioEndpointVolumeMicrophone.SetMasterVolumeLevelScalar(MicroScalar, BYVAL %NULL)
    
                IF FAILED(xhr) THEN
                    'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on default microphone.")
                    ? "Unable to set volume state on default microphone"
                    FUNCTION = 0
                    EXIT FUNCTION
                ELSE
                    ' "Default microphone volume set successfully!"
                END IF
    
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  MicrophoneCheck         new 2020 MAY 06 new
    '_________________________________________________________________
    
    FUNCTION MicrophoneCheck AS LONG
        LOCAL xhr         AS LONG
        LOCAL fMinDb      AS SINGLE 'float fMinDb
        LOCAL fMaxDb      AS SINGLE 'float fMaxDb
        LOCAL fStepDb     AS SINGLE 'float fStepDb
        LOCAL pfCurrentDb AS SINGLE 'float pfCurrentDb
    
        ' Get enumerator for audio endpoint devices.
        gpIMMDeviceEnumMicrophone = NEWCOM CLSID CLSID_MMDeviceEnumerator
        IF ISNOTHING(gpIMMDeviceEnumMicrophone) THEN
            ? "Unable to create audio device enumerator."
            FUNCTION = 0
            EXIT FUNCTION
        END IF
    
        ' // Enumerate the audio endpoints of interest (in this case audio capture endpoints)
        ' // The flags that can be used are:
        ' // %EDataFlow_eRender (for audio output endpoints),
        ' // %EDataFlow_eCapture (for audio capture endpoints),
        ' // %EDataFlow_eAll (for all audio endpoints)
    
        ' // %ERole_eConsole = Games, system notification sounds, and voice commands.
        ' // %ERole_eMultimedia = Music, movies, narration, and live music recording.
        ' // %ERole_eCommunications = Voice communications (talking to another person).
                                                                                                '%DEVICE_STATE_ACTIVE
        giMicrophoneHr = gpIMMDeviceEnumMicrophone.GetDefaultAudioEndpoint(%EDataFlow_eCapture, %ERole_eConsole, gpIMMDeviceMicrophone)
        IF FAILED(giMicrophoneHr) THEN
            ? "Unable to find default microphone device."
            FUNCTION = 0
            EXIT FUNCTION
        END IF
    
        xhr = gpIMMDeviceMicrophone.Activate(IID_IAudioEndpointVolume, %CLSCTX_INPROC_SERVER, BYVAL %NULL, gpIAudioEndpointVolumeMicrophone)
        IF FAILED(xhr) THEN
            'CALL SHOW_NOTES_NO_WAIT("Unable to activate a microphone.")
            ? "Unable to activate default microphone"
            FUNCTION = 0
            EXIT FUNCTION
        ELSE
            ' "Default microphone activated successfully!"
    
            ' // Get the device identifier
            xhr = gpIMMDeviceMicrophone.GetId(pDevId)
            IF pDevId THEN
                 XFill = "Device id = " & @pDevId  'Device id = {0.0.1.00000000}.{be4cea6b-aba4-4420-8e08-dbfb7a6be10c}
                 'call COPY_TO_CLIPBOARD
                CoTaskMemFree(pDevId)
            END IF
            ' // Get the device name
            gsTheMicrophoneName = AfxGetDeviceName(gpIMMDeviceMicrophone)
            '? gsTheMicrophoneName
    
            'Get the endpoint device's IDeviceTopology interface.
            xhr = gpIMMDeviceMicrophone.Activate(IID_IDeviceTopology, %CLSCTX_ALL, BYVAL %NULL, pDeviceTopology)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get microphone topology.")
                ? "Unable to get microphone topology."
                FUNCTION = 0
                EXIT FUNCTION
            END IF
    
            'The device topology for an endpoint device always
            'contains just one connector (connector number 0).
            xhr = pDeviceTopology.GetConnector(0, pConnFrom)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get the connector info.")
                ? "Unable to get the connector info."
                FUNCTION = 0
                EXIT FUNCTION
            END IF
            pDeviceTopology = NOTHING
    
            'Make sure that this is a capture device.
            xhr = pConnFrom.GetDataFlow(pflow)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get connector flow data.")
                ? "Unable to get connector flow data."
                FUNCTION = 0
                EXIT FUNCTION
            ELSE
                'For example, a typical rendering device on an adapter has a connector
                'with data-flow direction "In" through which the Windows audio engine
                'streams PCM data into the device. The same device has a connector with
                'data-flow direction "Out" through which the device transmits an audio
                'signal to speakers or headphones.
    
                '%DataFlow_In  = 0  Input stream. The audio stream flows into the device through the connector.
                '%DataFlow_Out = 1  Output stream. The audio stream flows out of the device through the connector.
                SELECT CASE pFlow
                    CASE 0  'In
                        'Error -- this is a rendering device.
                        'EXIT_ON_ERROR(xhr = %AUDCLNT_E_WRONG_ENDPOINT_TYPE)
                        'CALL SHOW_NOTES_NO_WAIT("Error! The connector flow data was wrong.")
                        ? "Error! The connector flow data was wrong."
                        FUNCTION = 0
                        EXIT FUNCTION
    
                    CASE 1  'Out
                        '? "DataFlow is out stream."
                END SELECT
            END IF
    
            '--------------------------------------
            ComponentName = "Microphone"
            pfCurrentDb = 0
            CALL SelectCaptureDevice
    
            IF ISOBJECT(pIaudioVolumeLevel) THEN
    
                pIaudioVolumeLevel.GetLevelRange(0, fMinDb, fMaxDb, fStepDb)
                '? "VMIN" & STR$(fMinDb)
                '? "VMAX" & STR$(fMaxDb)
                '? "VSTEP" & STR$(fStepDb)
    
                pIaudioVolumeLevel.GetLevel(0, pfCurrentDb)
                '? "VCUR" & STR$(pfCurrentDb)
                pfCurrentDb = (INT(10 * ((pfCurrentDb + 34.5)/46.5) * 100)) /10   'rescale values
    
                SELECT CASE pfCurrentDb
                    CASE 0 TO 17.2
                        '? STR$(pfCurrentDb)
                        SCALER = 0
                    CASE 17.3 TO 28.4
                        '? STR$(pfCurrentDb)
                        SCALER = .05
    
                    CASE 28.5 TO 36.8
                        '? STR$(pfCurrentDb)
                        SCALER = .1
                    CASE 36.9 TO 43.4
                        '? STR$(pfCurrentDb)
                        SCALER = .15
    
                    CASE 43.5 TO 48.9
                        '? STR$(pfCurrentDb)
                        SCALER = .2
                    CASE 49 TO 53.7
                        '? STR$(pfCurrentDb)
                        SCALER = .25
    
                    CASE 53.8 TO 57.8
                        '? STR$(pfCurrentDb)
                        SCALER = .3
                    CASE 57.9 TO 61.5
                        '? STR$(pfCurrentDb)
                        SCALER = .3
    
                    CASE 61.6 'to 67.7
                        '? STR$(pfCurrentDb)
                        SCALER = .4
                    CASE 64.8 'to 67.7
                        '? STR$(pfCurrentDb)
                        SCALER = .45
    
                    CASE 67.8 TO 70.5
                        '? STR$(pfCurrentDb)
                        SCALER = .5
                    CASE 70.6 TO 73
                        '? STR$(pfCurrentDb)
                        SCALER = .55
    
                    CASE 73.1 TO 75.4
                        '? STR$(pfCurrentDb)
                        SCALER = .6
                    CASE 75.5 TO 78
                        '? STR$(pfCurrentDb)
                        SCALER = .65
    
                    CASE 78.1 TO 80.7
                        '? STR$(pfCurrentDb)
                        SCALER = .7
                    CASE 80.8 TO 83.8
                        '? STR$(pfCurrentDb)
                        SCALER = .75
    
                    CASE 83.9 TO 87.1
                        '? STR$(pfCurrentDb)
                        SCALER = .8
                    CASE 87.2 TO 90.8
                        '? STR$(pfCurrentDb)
                        SCALER = .855
    
                    CASE 90.9 TO 95
                        '? STR$(pfCurrentDb)
                        SCALER = .9
                    CASE 95.1 TO 99.9
                        '? STR$(pfCurrentDb)
                        SCALER = .95
    
                    CASE 100
                        '? STR$(pfCurrentDb)
                        SCALER = 1
    
                    CASE ELSE
                        '? STR$(pfCurrentDb)
                        SCALER = pfCurrentDb
                END SELECT
    
            ELSE
                ? "There is no audio level object!"
            END IF
            '--------------------------------------
            ComponentName = "Microphone Boost"
            pIaudioVolumeLevel = NOTHING
            pfCurrentDb = 0
            CALL SelectCaptureDevice
    
            IF ISOBJECT(pIaudioVolumeLevel) THEN
                '? "made it here"
                pIaudioVolumeLevel.GetLevelRange(0, fMinDb, fMaxDb, fStepDb)
                '? "VMIN" & STR$(fMinDb)
                '? "VMAX" & STR$(fMaxDb)
                '? "VSTEP" & STR$(fStepDb)
    
                pIaudioVolumeLevel.GetLevel(0, pfCurrentDb)
                '? "VCUR" & STR$(pfCurrentDb)
                pIaudioVolumeLevel.SetLevel(0, pfCurrentDb, BYVAL %NULL)
                SCALER_MB = pfCurrentDb
            END IF
            pConnFrom = NOTHING
    
            '\\\\\\\\\\\\\\\
            'Get the endpoint device's IAudioMeterInformation interface.
            xhr = gpIMMDeviceMicrophone.Activate(IID_IAudioMeterInformation, %CLSCTX_ALL, BYVAL %NULL, gpMeterInfo)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get peak meter info.")
                ? "Unable to get peak meter info."
                FUNCTION = 0
                EXIT FUNCTION
            END IF
    
        END IF
    
    
        FUNCTION = 1
    END FUNCTION
    '_________________________________________________________________
    '
    ' FUNCTION  AfxGetDeviceName                                Jarvis
    '_________________________________________________________________
    
    ' ================================================================
    ' Helper function to retrieve the friendly name of the
    ' audio device.
    ' ================================================================
    FUNCTION AfxGetDeviceName (BYVAL pDev AS IMMDevice) AS WSTRING
    
        LOCAL hr AS LONG
    
        LOCAL pPropStore AS IPropertyStore
        hr = pDev.OpenPropertyStore(%STGM_READ, pPropStore)
        IF FAILED(hr) THEN EXIT FUNCTION
        LOCAL friendlyName AS PROPVARIANT
        PropVariantInit(friendlyName)
        hr = pPropStore.getValue(DEVPKEY_Device_FriendlyName, friendlyName)
        IF FAILED(hr) THEN EXIT FUNCTION
        LOCAL wszDevName AS WSTRINGZ * 128
        PropVariantToString(friendlyName, wszDevName, 128)
        FUNCTION = wszDevName
        PropVariantClear(friendlyName)
    
    END FUNCTION
    
    '_________________________________________________________________
    '
    ' SUB COPY_TO_CLIPBOARD                                 EjectDrive
    '_________________________________________________________________
    
    SUB COPY_TO_CLIPBOARD
      CopyToClipBoard 0, BYCOPY XFill
      XFill = ""
    END SUB
    '_________________________________________________________________
    '
    '   FUNCTION CopyToClipBoard                                  VER4
    '_________________________________________________________________
    
    FUNCTION CopyToClipBoard(HParent AS LONG, MyText AS STRING) AS LONG
        LOCAL HptrMemory AS DWORD
        LOCAL HptrAsciiz AS ASCIIZ PTR
    
        OpenClipboard HParent
        IF ISFALSE(EmptyClipboard) THEN
            CloseClipboard
            EXIT FUNCTION
        END IF
    
        HptrMemory  = GlobalAlloc(%GHND, ((LEN(MyText) + 1) + TALLY(MyText, ANY ($CRLF & $TAB))))
        HptrAsciiz = GlobalLock(HptrMemory)
        @HptrAsciiz = MyText
        GlobalUnlock HptrMemory
        FUNCTION = SetClipboardData(%CF_TEXT, HptrMemory)
        CloseClipboard
    END FUNCTION
    '_________________________________________________________________
    
    
    '_________________________________________________________________
    '
    ' Processes pending Windows messages.                         VER4
    ' Call this procedure if you are performing a tight
    ' FOR/NEXT or DO/LOOP and need to allow
    ' your application to be responsive to user input.
    ' Modified version of AfxPumpMessages
    '_________________________________________________________________
    
    SUB MyPumpMessages(BYVAL xTimes AS LONG) EXPORT
      '/////////////////////////////////////////////////////////////
      'Do not want to remove the WM_QUIT message from message que.
    '  IF GlobalVariableTest86 = 1 THEN EXIT SUB
      '/////////////////////////////////////////////////////////////
    
      LOCAL Ix AS LONG
    
      IF giPumpingMessages = 1 THEN EXIT SUB
      giPumpingMessages = 1
    
    '  IF GlobalVariableTestOn THEN
          IF xTimes <= 0 THEN xTimes = 1
          FOR Ix = 1 TO xTimes
              CALL PeekRemoveTranslateDispatchMessage
    '          IF GlobalVariableTest86 = 1 THEN GOTO NoMoreNeeded
          NEXT Ix
    '  END IF
    
      NoMoreNeeded:
      giPumpingMessages = 0
    END SUB
    '_________________________________________________________________
    '
    '   SUB  PeekRemoveTranslateDispatchMessage                   VER4
    '_________________________________________________________________
    
    SUB PeekRemoveTranslateDispatchMessage
      '/////////////////////////////////////////////////////////////
      'Do not want to remove the WM_QUIT message from message que.
    '  IF GlobalVariableTest86 = 1 THEN EXIT SUB
      '/////////////////////////////////////////////////////////////
      'Could use this to signal main message loop to close
      'PostThreadMessageA (GetCurrentThreadID, %WM_QUIT, 0, 0)   ' Signal Quit to Msg Loop
    
      IF giPRTDMessage = 1 THEN EXIT SUB
      giPRTDMessage = 1
    
      STATIC Msg AS tagMsg
      IF PeekMessageA(Msg, %NULL, %NULL, %NULL, %PM_REMOVE) THEN
          TranslateMessage Msg
          DispatchMessageA Msg
      END IF
    
      giPRTDMessage = 0
    END SUB
    
    
    '//-----------------------------------------------------------
    '// This function traverses the data path that extends from the
    '// endpoint device to the system bus (for example, PCI)
    '// or external bus (USB). If the function discovers a MUX
    '// (input selector) in the path, it selects the MUX input
    '// that connects to the stream from the endpoint device.
    '// In this case we are looking for the Microphone or Microphone
    '// Boost parts.
    '//-----------------------------------------------------------
    
         'Limits of Device Topology
         'Where (con) is a connector
         'microphone -> endpoint device B (con) physical external
         '
         '-> Input Multiplexer Device (Topology Filter)
         '   (con) physical external   IPart & IConnector
         '   subunit Mute ->           IPart & ISubUnit
         '   subunit Vol ->            IPart & ISubUnit
         '   subunit MUX ->            IPart & ISubUnit
         '   (Con) Software Fixed      IPart & IConnector
         '
         '-> Wave Capture Device (Wave Filter)
         '   (Con) Software Fixed      IPart & IConnector
         '   subunit ADC               IPart & ISubUnit
         '   (Con) Software IO         IPart & IConnector
         '
         '-> System Bus Wave-in Stream (DMA)
    
    
    FUNCTION SelectCaptureDevice() AS LONG
        LOCAL hr       AS LONG
    
        hr = %S_OK
        pIaudioVolumeLevel = NOTHING
        IF ISOBJECT(pConnFrom) THEN
            'continue
        ELSE
            GOTO ExitHere
        END IF
    
        '// Outer loop: Each iteration traverses the data path
        '// through a device topology starting at the input
        '// connector and ending at the output connector.
        WHILE %TRUE
            IF giProgStat = 0 THEN GOTO ExitHere
    
            hr = pConnFrom.IsConnected(bConnected)
            IF FAILED(hr) THEN GOTO ExitHere
    
            '// Does this connector connect to another device?
            IF bConnected = %FALSE THEN
    
                '// This is the end of the data path that
                '// stretches from the endpoint device to the
                '// system bus or external bus. Verify that
                '// the connection type is Software_IO.
                hr = pConnFrom.GetType(connType)
                IF FAILED(hr) THEN GOTO ExitHere
    
                IF connType = %ConnectorType_Software_IO THEN
                    '? "Finished looking for connections."
                    EXIT LOOP '// finished
                END IF
                IF FAILED(hr = %E_FAIL) THEN GOTO ExitHere
            END IF
    
            '// Get the connector in the next device topology,
            '// which lies on the other side of the connection.
            hr = pConnFrom.GetConnectedTo(pConnTo)
            IF FAILED(hr) THEN GOTO ExitHere
    
            '// Get the connector's IPart interface.
            hr = pConnTo.QueryInterface( _
                            IID_IPart, _
                            BYVAL VARPTR(pPartPrev))
            IF FAILED(hr) THEN GOTO ExitHere
            pConnTo = NOTHING
    
            '// Inner loop: Each iteration traverses one link in a
            '// device topology and looks for input multiplexers.
            WHILE %TRUE
    
                IF giProgStat = 0 THEN GOTO ExitHere
    
                MyPumpMessages(2000)
                '// Follow downstream link to next part.
                hr = pPartPrev.EnumPartsOutgoing(ppParts)
                IF FAILED(hr) THEN GOTO ExitHere
    
                hr = ppParts.GetPart(0, pPartNext)
                ppParts = NOTHING
                IF FAILED(hr) THEN GOTO ExitHere
    
                hr = pPartNext.GetPartType(pPartType)
                IF FAILED(hr) THEN GOTO ExitHere
    
                IF SUCCEEDED(pPartNext.GetName(pName)) THEN
                    '? @pName   '=Microphone or Microphone Boost
    
                    'Failure of the following call means only that
                    'the part is not a boost (microphone boost).
                    IF UCASE$(ComponentName) <> UCASE$(@pName) THEN
                        '? "Not a match."
                    ELSE
                        '? "The part name matched."
                        'get IAudioVolumeLevel to control volume
                        hr = pPartNext.Activate(%CLSCTX_ALL, IID_IAudioVolumeLevel, pIaudioVolumeLevel)
                        GOTO ExitHere
                    END IF
                    CoTaskMemFree(pName)
                END IF
    
                '? "Try again..."
    
                SELECT CASE pPartType
                    CASE %PartType_Connector  '=0
                        '? "Connector found."
                        '// We've reached the output connector that
                        '// lies at the end of this device topology.
                        hr = pPartNext.QueryInterface( _
                                          IID_IConnector, _
                                          BYVAL VARPTR(pConnFrom))
                        IF FAILED(hr) THEN GOTO ExitHere
    
                        pPartPrev = NOTHING
                        pPartNext = NOTHING
                        EXIT LOOP
    
                    CASE %PartType_Subunit '=1
                        '? "Subunit found."
    
                END SELECT
    
                '// Failure of the following call means only that
                '// the part is not a MUX (input selector).
                hr = pPartNext.Activate( _
                                  %CLSCTX_ALL, _
                                  IID_IAudioInputSelector, _
                                  pSelector)
                IF hr = %S_OK THEN
                    ? "The MUX was found."
                    '// We found a MUX (input selector), so select
                    '// the input from our endpoint device.
                    LOCAL localId AS DWORD
                    hr = pPartPrev.GetLocalId(localId)
                    IF FAILED(hr) THEN GOTO ExitHere
    
                    hr = pSelector.SetSelection(localId, BYVAL %NULL)
                    IF FAILED(hr) THEN GOTO ExitHere
    
                    pSelector = NOTHING
                END IF
    
                pPartPrev = NOTHING
                pPartPrev = pPartNext
                pPartNext = NOTHING
            WEND
        WEND
    
    ExitHere:
        ppParts   = NOTHING
        pConnTo   = NOTHING
        pPartPrev = NOTHING
        pPartNext = NOTHING
        pSelector = NOTHING
        FUNCTION = hr
    
    END FUNCTION

  • #2
    Version 2
    Code:
    'Volume and mute control for Vista, Seven, Eight, and Ten.
    'Callback functions will syncronize speaker volume and mute
    'microphone volume and mute and microphone boost with other mixer controls.
    'Based on work done by Pierre Bellisle.
    'Works with PBWIN10.4 and J.Roca includes WINAPI_III_107.
    'Released to the public on 2020 MAY 12.
    
    #COMPILE EXE "VolumeWithCallbackVistaPlus.exe"
    #DIM ALL
    #REGISTER NONE
    
    $CandCGrammar     = "JARVIS.xml"
    
    'unused functions
    'STEREOSPEAKERSMUTESELECT
    'MICROPHONEMUTESELECT
    'PUTQUESTIONMARKONMARQUE
    
    GLOBAL g_guid_1_Context                     AS GUID  'speakers volume/mute-unmute context
    GLOBAL g_guid_2_Context                     AS GUID  'microphone volume/mute-unmute context
    GLOBAL g_guid_3_Context                     AS GUID  'microphone boost volume context (currently not used)
    
    'SAPI
    GLOBAL InProcEvents                         AS ISpeechRecoContextEventsImplemented   'SAPI interface
    GLOBAL giRunningRecogProcess                AS LONG  'SAPI
    GLOBAL RecognitionStatus                    AS LONG  'SAPI
    GLOBAL giStartStopRecognition               AS LONG  'SAPI
    GLOBAL oRecoContext                         AS ISpeechRecoContext           'SAPI
    GLOBAL oRecognizer                          AS ISpeechRecognizer            'SAPI
    GLOBAL oMyGrammar                           AS ISpeechRecoGrammar           'SAPI
    GLOBAL oCategory                            AS ISpeechObjectTokenCategory   'SAPI
    GLOBAL oToken                               AS ISpeechObjectToken           'SAPI
    GLOBAL giCCGrammarLoaded                    AS LONG  'SAPI
    GLOBAL gsBannerDictation                    AS STRING  'SAPI
    GLOBAL gsBannerListen                       AS STRING  'SAPI
    GLOBAL giProcessingRecog                    AS LONG  'SAPI
    GLOBAL giRecogNotUnderstood                 AS LONG      'SAPI
    GLOBAL MyDataX                              AS STRING    'SAPI contains the recognized phrase
    'END SAPI
    
    GLOBAL hDlg                                 AS DWORD
    GLOBAL giProgStat                           AS LONG
    GLOBAL VistaPlus                            AS LONG 'Vista to 10
    GLOBAL sBuffer                              AS STRING
    
    GLOBAL gsMasterVolumeIsChangeable           AS STRING  '"YES" OR "NO"
    
    GLOBAL gpIMMDeviceEnumStereoSpeakers        AS IMMDeviceEnumerator
    GLOBAL gpIMMDeviceStereoSpeakers            AS IMMDevice
    GLOBAL gpIAudioEndpointVolumeStereoSpeakers AS IAudioEndpointVolume
    GLOBAL giStereoSpeakers                     AS LONG   ' default stereo speaker availablility  yes = 1, no = 0
    GLOBAL giStereoSpeakersHr                   AS LONG
    GLOBAL giMasterStereoSpeakersLevel          AS LONG
    GLOBAL gsTheStereoSpeakersName              AS STRING
    GLOBAL giMasterStereoSpeakerMuteLevel       AS LONG
    GLOBAL giSpeakersMute                       AS LONG
    
    GLOBAL gpIMMDeviceEnumMicrophone            AS IMMDeviceEnumerator
    GLOBAL gpIMMDeviceMicrophone                AS IMMDevice
    GLOBAL gpIAudioEndpointVolumeMicrophone     AS IAudioEndpointVolume
    GLOBAL giMicrophone                         AS LONG   ' default microphone availablility  yes = 1, no = 0
    GLOBAL giMicrophoneHr                       AS LONG
    GLOBAL giMasterMicrophoneLevel              AS LONG
    GLOBAL gsTheMicrophoneName                  AS STRING
    GLOBAL giMasterMicrophoneMuteLevel          AS LONG
    GLOBAL giMicrophoneMute                     AS LONG
    
    GLOBAL giMasterMicroBoostLevel              AS LONG
    
    GLOBAL gpIMMNotificationClientMicrophone    AS IMMNotificationClient
    GLOBAL gpMeterInfo                          AS IAudioMeterInformation
    
    GLOBAL pDeviceTopology                      AS IDeviceTopology
    GLOBAL pConnFrom                            AS IConnector
    GLOBAL pConnTo                              AS IConnector
    GLOBAL pPartPrev                            AS IPart
    GLOBAL pPartNext                            AS IPart
    GLOBAL ppParts                              AS IPartsList
    GLOBAL pIaudioVolumeLevel                   AS IAudioVolumeLevel
    GLOBAL pSelector                            AS IAudioInputSelector
    
    GLOBAL ppNotify_1                           AS IAudioEndpointVolumeCallback    'speakers
    GLOBAL ppNotify_2                           AS IAudioEndpointVolumeCallback    'microphone
    GLOBAL ppNotify_3                           AS IControlChangeNotify            'microphone boost
    
    GLOBAL PEAK                                 AS SINGLE     'for peak meter
    
    GLOBAL SCALER_MB                            AS SINGLE
    GLOBAL pDevId                               AS WSTRINGZ PTR
    GLOBAL devName                              AS WSTRING
    GLOBAL pName                                AS WSTRINGZ PTR  'LPWSTR pName
    
    GLOBAL giMicroMute                          AS LONG 'Tracks Microphone Mute status
    GLOBAL pflow                                AS LONG 'DataFlow pflow
    GLOBAL pPartType                            AS LONG 'PartType
    GLOBAL bConnected                           AS LONG 'BOOL bConnected
    GLOBAL connType                             AS LONG 'ConnectorType
    GLOBAL ComponentName                        AS WSTRINGZ * 255
    
    GLOBAL XFill                                AS STRING  'used to post text to clipboard
    
    GLOBAL giPRTDMessage                        AS LONG
    GLOBAL giPumpingMessages                    AS LONG
    
    GLOBAL CLSID_MMDeviceEnumerator             AS GUID
    GLOBAL IID_IMMDeviceEnumerator              AS GUID   'new
    GLOBAL IID_IPart                            AS GUID
    GLOBAL IID_IAudioVolumeLevel                AS GUID
    GLOBAL IID_IConnector                       AS GUID
    GLOBAL IID_IAudioInputSelector              AS GUID
    GLOBAL IID_IAudioEndpointVolume             AS GUID
    GLOBAL IID_IDeviceTopology                  AS GUID
    GLOBAL IID_IAudioMeterInformation           AS GUID
    GLOBAL IID_IAudioEndpointVolumeCallback     AS GUID   'new
    GLOBAL IID_IControlChangeNotify             AS GUID   'new
    
    $AppName   = "Audio with Callback"
    
    %LabelMinSpeaker              = 101   'MIN
    %LabelMaxSpeaker              = 102   'MAX
    %LabelVolSpeaker              = 103
    %LabelInfoSpeaker             = 104
    %LabelOnOffSpeaker            = 105
    %TrackbarVolSpeaker           = 201
    %CheckboxVolOnOffSpeaker      = 301
    %ButtonSndVolExeSpeaker       = 401
    
    %LabelMinMicro                = 1101  'MIN
    %LabelMaxMicro                = 1102  'MAX
    %LabelVolMicro                = 1103
    %LabelInfoMicro               = 1104
    %LabelOnOffMicro              = 1105
    %TrackbarVolMicro             = 1201
    %CheckboxVolOnOffMicro        = 1301
    '%ButtonSndVolExeMicro         = 1401 'not used
    
    %LabelMinMicroBoost           = 2101  'MIN
    %LabelMaxMicroBoost           = 2102  'MAX
    %LabelVolMicroBoost           = 2103
    %LabelInfoMicroBoost          = 2104
    '%LabelOnOffMicroBoost         = 2105 'not used
    %TrackbarVolMicroBoost        = 2201
    '%CheckboxVolOnOffMicroBoost   = 2301 'not used
    '%ButtonSndVolExeMicroBoost    = 2401 'not used
    
    %ID_VOLUME                    = 1002
    %IDC_PEAK_METER               = 1003
    %IDC_Timer0                   = 1004
    
    %TrackbarMax                  = 100   'for speaker and microphone trackbar
    %TrackbarMaxBoost             = 3     'for microphone boost trackbar
    %ID_RefreshBanner             = 5000  'SAPI
    
    '#INCLUDE ONCE "MB_API_A.inc"        'WHEN USED {6,735 total lines} COMPILE TIME 0.1 seconds
    'OR THESE
    #INCLUDE ONCE "Win32Api.inc"       'WHEN USED {289,533 total lines} COMPILE TIME 1.5 seconds
    #INCLUDE ONCE "CommCtrl.inc"       'Needed if manifest and WinXP
    #INCLUDE ONCE "mmdeviceapi.inc"    'used for WASAPI
    #INCLUDE ONCE "devpkey.inc"        'used for WASAPI
    #INCLUDE ONCE "propvarutil.inc"    'used for WASAPI
    #INCLUDE ONCE "endpointvolume.inc" 'used for WASAPI
    #INCLUDE ONCE "propsys.inc"        'used for WASAPI
    #INCLUDE ONCE "sapi.inc"           'used for SAPI
    #INCLUDE ONCE "WinNls.inc"         'User language
    
    ' ########################################################################################
    ' Class CISpeechRecoContextEvents                          SAPI
    ' Interface name = _ISpeechRecoContextEvents
    ' IID = {7B8FCB42-0E9D-4F00-A048-7B04D6179D3D}
    ' Attributes = 4096 [&H1000] [Dispatchable]
    ' ########################################################################################
    
    CLASS CISpeechRecoContextEventsImplemented GUID$("{5B344ADB-C0C7-4B5F-8046-7D2DB91A1D75}") AS EVENT
    
    INTERFACE ISpeechRecoContextEventsImplemented GUID$("{7B8FCB42-0E9D-4F00-A048-7B04D6179D3D}") AS EVENT
    
      INHERIT IDISPATCH
    
       ' =====================================================================================
       METHOD Recognition <7> ( _
         BYVAL StreamNumber AS LONG _                       ' __in long StreamNumber
       , BYVAL StreamPosition AS VARIANT _                  ' __in VARIANT StreamPosition
       , BYVAL RecognitionType AS LONG _                    ' __in SpeechRecognitionType RecognitionType
       , BYVAL Result AS ISpeechRecoResult _                ' __in ISpeechRecoResult *Result
       )                                                    ' void
    
          LOCAL pDisp AS IDISPATCH
          LOCAL bstrText AS WSTRING
          IF ISNOTHING(Result) THEN EXIT METHOD
          pDisp = Result
          OBJECT CALL pDisp.PhraseInfo.GetText TO bstrText
          IF OBJRESULT THEN
             ? "GetText error: " & OBJRESULT$
          ELSE
              giRecogNotUnderstood = 0
              MyDataX = TRIM$(bstrText)
          END IF
    
       END METHOD
       ' =====================================================================================
       ' =====================================================================================
       METHOD FalseRecognition <11> ( _
         BYVAL StreamNumber AS LONG _                       ' __in long StreamNumber
       , BYVAL StreamPosition AS VARIANT _                  ' __in VARIANT StreamPosition
       , BYVAL Result AS ISpeechRecoResult _                ' __in ISpeechRecoResult *Result
       )                                                    ' void
    
          LOCAL pDisp AS IDISPATCH
          LOCAL bstrText AS WSTRING
          IF ISNOTHING(Result) THEN
              EXIT METHOD
          END IF
          pDisp = Result
          OBJECT CALL pDisp.PhraseInfo.GetText TO bstrText
          IF OBJRESULT THEN
             ? "GetText error: " & OBJRESULT$
          ELSE
              giRecogNotUnderstood = 1
              MyDataX = "What was that?"
          END IF
       END METHOD
    
    
    END INTERFACE
    
    END CLASS
    
    
    ' ########################################################################################
    ' 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
    
    
    ' ########################################################################################
    ' Class CIAudioEndpointVolumeCallback                   CURRENTLY NOT USED
    ' Interface name = _IAudioEndpointVolumeCallback
    ' IID = {657804FA-D6AD-4496-8A60-352752AF4F89}
    ' Inherited interface = IUnknown
    ' ########################################################################################
    
    CLASS CIAudioEndpointVolumeCallback_3 AS COM
    
     INTERFACE IAudioEndpointVolumeCallback $IID_IAudioEndpointVolumeCallback
    
      INHERIT IUNKNOWN
    
       ' =====================================================================================
       METHOD OnNotify ( _                                      ' VTable offset = 12
         BYREF pNotify_3 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_3) = %NULL THEN
           METHOD = %E_INVALIDARG
         ELSE
           IF (hDlg <> %NULL) THEN
               IF (pNotify_3.guidEventContext <> g_guid_3_Context) THEN
                 'Post notification to main dialog
                 'PostMessageA (hDlg, %WM_APP, %TrackbarVolMicroBoost, pNotify_3.fMasterVolume * %TrackbarMaxBoost)
               END IF
           END IF
    
           METHOD = %S_OK
         END IF
       END METHOD
       ' =====================================================================================
    
     END INTERFACE
    
    END CLASS
    
    ' ########################################################################################
    ' 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
    
    '_____________________________________________________________________________
    '
    SUB IconSet_1(iconOn AS LONG)
      DIM   hIcon(0 TO 3) AS STATIC DWORD
      LOCAL Looper        AS LONG
    
      SELECT CASE iconOn
    
          CASE 0, 1 'Set "disable icon" or "enable icon" on dialog ans static
              SetClassLongA (hDlg, %GCL_HICONSM, hIcon(iconOn))
              SendMessageA (hDlg, %WM_SETICON, %ICON_SMALL, hIcon(iconOn))
              SendDlgItemMessageA (hDlg, %LabelOnOffSpeaker, %STM_SETIMAGE, %IMAGE_ICON, hIcon(iconOn + 02))
    
          CASE -2 'Get "disable icon" and "enable icon" from appropriate files
              IF VistaPlus THEN 'Vista to 10
                  ExtractIconExA ("SndVol.exe", 1, BYVAL VARPTR(hIcon(3)), BYVAL VARPTR(hIcon(1)), 1) 'Vista to 10, Enable-Big-Small
                  ExtractIconExA ("SndVol.exe", 2, BYVAL VARPTR(hIcon(2)), BYVAL VARPTR(hIcon(0)), 1) 'Vista to 10, Disable-Big-Small
              ELSE '95 to XP
                  ? "Sorry, this app does not support this OS."
              END IF
    
          CASE -1 'Release memory clean-up
              FOR Looper = 0 TO 3
                  DestroyIcon (hIcon(Looper))
              NEXT
    
      END SELECT
    
    END SUB
    
    ' ========================================================================================
    ' Helper function to retrieve the friendly name of the audio device.
    ' ========================================================================================
    FUNCTION AfxGetDeviceName (BYVAL pDev AS IMMDevice) AS WSTRING
    
        LOCAL hr AS LONG
    
        LOCAL pPropStore AS IPropertyStore
        hr = pDev.OpenPropertyStore(%STGM_READ, pPropStore)
        IF FAILED(hr) THEN EXIT FUNCTION
        LOCAL friendlyName AS PROPVARIANT
        PropVariantInit(friendlyName)
        hr = pPropStore.getValue(DEVPKEY_Device_FriendlyName, friendlyName)
        IF FAILED(hr) THEN EXIT FUNCTION
        LOCAL wszDevName AS WSTRINGZ * 128
        PropVariantToString(friendlyName, wszDevName, 128)
        FUNCTION = wszDevName
        PropVariantClear(friendlyName)
    
    END FUNCTION
    
    ' ========================================================================================
    
    FUNCTION StereoSpeakersMuteSelect AS LONG
    
        IF giStereoSpeakers = 1 THEN
    
            IF giSpeakersMute = 0 THEN
                MuteStereoSpeakers
            ELSE
                UnMuteStereoSpeakers
            END IF
        END IF
    
        FUNCTION = 1
    END FUNCTION
    
    ' ========================================================================================
    
    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_1 = CLASS "CIAudioEndpointVolumeCallback_1"
    
            xhr = gpIAudioEndpointVolumeStereoSpeakers.RegisterControlChangeNotify(BYVAL ppNotify_1) 'IAudioEndpointVolumeCallback
    
            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
    
    'When notifications are no longer needed, the client can call the
    'IAudioEndpointVolume.UnregisterControlChangeNotify method to terminate the notifications.
    
    ' ========================================================================================
    
    FUNCTION GetMuteStereoSpeakers AS LONG
    
        LOCAL xhr   AS LONG
    
        LOCAL fMute AS LONG
    
        IF ISOBJECT(gpIAudioEndpointVolumeStereoSpeakers) THEN
    
            xhr = gpIAudioEndpointVolumeStereoSpeakers.GetMute(fMute)
            IF FAILED(xhr) THEN
                ? "Unable to get stereo speaker mute level."
                FUNCTION = 0
                EXIT FUNCTION
            ELSE
                '? "Default stereo speaker mute level is " + STR$(fMute)
            END IF
    
        END IF
        giMasterStereoSpeakerMuteLevel = fMute
        SELECT CASE fMute
            CASE 1
                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeaker), %BM_SETCHECK, %BST_CHECKED, 0)
                IconSet_1(0)
    
            CASE ELSE
                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeaker), %BM_SETCHECK, %BST_UNCHECKED, 0)
                IconSet_1(1)
        END SELECT
        FUNCTION = 1
    END FUNCTION
    
    ' ========================================================================================
    
    FUNCTION MuteStereoSpeakers AS LONG
    
        LOCAL xhr   AS LONG
    
        IF gsMasterVolumeIsChangeable = "YES" THEN
            IF ISOBJECT(gpIAudioEndpointVolumeStereoSpeakers) THEN
    
                xhr = gpIAudioEndpointVolumeStereoSpeakers.SetMute(1, g_guid_1_Context) 'BYVAL %NULL)  '%TRUE  BYREF pguidEventContext AS GUID
                IF FAILED(xhr) THEN
                    ? "Unable to mute default stereo speakers."
                ELSE
                    '? "Default StereoSpeakers muted successfully!"
                END IF
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    ' ========================================================================================
    
    FUNCTION UnMuteStereoSpeakers AS LONG
    
        LOCAL xhr   AS LONG
    
        IF gsMasterVolumeIsChangeable = "YES" THEN
            IF ISOBJECT(gpIAudioEndpointVolumeStereoSpeakers) THEN
    
                xhr = gpIAudioEndpointVolumeStereoSpeakers.SetMute(0, g_guid_1_Context) 'BYVAL %NULL)  '%FALSE  BYREF pguidEventContext AS GUID
                IF FAILED(xhr) THEN
                    ? "Unable to unmute default stereo speakers."
                ELSE
                    '? "Default StereoSpeakers unmuted successfully!"
                END IF
    
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    ' ========================================================================================
    
    FUNCTION GetVolumeStereoSpeakers AS LONG
    
        LOCAL xhr   AS LONG
        LOCAL fVolume AS SINGLE
    
        IF gsMasterVolumeIsChangeable = "YES" THEN
            IF ISOBJECT(gpIAudioEndpointVolumeStereoSpeakers) THEN
    
                xhr = gpIAudioEndpointVolumeStereoSpeakers.GetMasterVolumeLevelScalar(fVolume)
                IF FAILED(xhr) THEN
                    ? "Unable to get scalar on default stereo speakers."
                    FUNCTION = 0
                    EXIT FUNCTION
                ELSE
                    '? "Default Stereo Speakers volume scalar is " + STR$(fVolume)
                END IF
    
            END IF
            '? "Speaker Volume=" & STR$(fVolume)
            giMasterStereoSpeakersLevel = fVolume * 100
            '? STR$(giMasterStereoSpeakersLevel)
            SendDlgItemMessageA (hDlg, %TrackbarVolSpeaker, %TBM_SETPOS, %TRUE, %TrackbarMax - fVolume * 100)
            SetDlgItemTextA (hDlg, %LabelVolSpeaker, FORMAT$(fVolume * 100, "##0"))
        END IF
        FUNCTION = 1
    END FUNCTION
    
    ' ========================================================================================
    
    FUNCTION SetVolumeStereoSpeakers(BYVAL SpeakerScalar AS SINGLE) AS LONG
    
        LOCAL xhr   AS LONG
    
        IF gsMasterVolumeIsChangeable = "YES" THEN
            IF ISOBJECT(gpIAudioEndpointVolumeStereoSpeakers) THEN
    
                xhr = gpIAudioEndpointVolumeStereoSpeakers.SetMasterVolumeLevelScalar(SpeakerScalar, g_guid_1_Context) 'BYVAL %NULL) 'BYREF pguidEventContext AS GUID
    
                IF FAILED(xhr) THEN
                    ? "Unable to set scalar on default stereo speakers."
                    FUNCTION = 0
                    EXIT FUNCTION
                ELSE
                    '? "Default Stereo Speakers volume set successfully!"
                END IF
    
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  StereoSpeakersCheck
    '_________________________________________________________________
    
    FUNCTION StereoSpeakersCheck AS LONG
        LOCAL xhr     AS LONG
        LOCAL pDevId  AS WSTRINGZ PTR
        LOCAL devName AS WSTRING
    
        gpIMMDeviceEnumStereoSpeakers = NEWCOM CLSID CLSID_MMDeviceEnumerator
        IF ISNOTHING(gpIMMDeviceEnumStereoSpeakers) THEN EXIT FUNCTION
    
        ' // Enumerate the audio endpoints of interest (in this case audio capture endpoints)
        ' // The flags that can be used are %EDataFlow_eRender (for audio output endpoints),
        ' // %EDataFlow_eCapture (for audio capture endpoints) or %EDataFlow_eAll (for all audio endpoints)
        ' // %EDataFlow_eCapture (for audio capture endpoints),
        ' // %EDataFlow_eAll (for all audio endpoints)
    
        ' // %ERole_eConsole = Games, system notification sounds, and voice commands.
        ' // %ERole_eMultimedia = Music, movies, narration, and live music recording.
        ' // %ERole_eCommunications = Voice communications (talking to another person).
    
        giStereoSpeakersHr = gpIMMDeviceEnumStereoSpeakers.GetDefaultAudioEndpoint( _
                                   %EDataFlow_eRender,   _
                                   %ERole_eMultimedia,   _
                                   gpIMMDeviceStereoSpeakers)  'IMMDevice
        IF FAILED(giStereoSpeakersHr) THEN
            'CALL SHOW_NOTES("Unable to find default speakers.")
            ? "Unable to find the default Stereo Speakers"
            FUNCTION = 0
            EXIT FUNCTION
        END IF
    
        xhr = gpIMMDeviceStereoSpeakers.Activate( _
                         IID_IAudioEndpointVolume, _
                         %CLSCTX_INPROC_SERVER,    _
                         BYVAL %NULL,              _
                         gpIAudioEndpointVolumeStereoSpeakers)   'IAudioEndpointVolume
        IF FAILED(xhr) THEN
            'CALL SHOW_NOTES("Unable to activate default speakers.")
            ? "Unable to activate default Stereo Speakers"
            FUNCTION = 0
            EXIT FUNCTION
        ELSE
            ' "Default Stereo Speakers activated successfully!"
    
            ' // Get the device identifier
            xhr = gpIMMDeviceStereoSpeakers.GetId(pDevId)
            IF pDevId THEN
                ' "Device id = " & @pDevId
                CoTaskMemFree(pDevId)
            END IF
            ' // Get the device name
            gsTheStereoSpeakersName =  AfxGetDeviceName (gpIMMDeviceStereoSpeakers)  'IMMDevice
    
        END IF
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  MicrophoneCheck         new 2020 MAY 06 new
    '_________________________________________________________________
    
    FUNCTION MicrophoneCheck AS LONG
        LOCAL xhr         AS LONG
        LOCAL fMinDb      AS SINGLE 'float fMinDb
        LOCAL fMaxDb      AS SINGLE 'float fMaxDb
        LOCAL fStepDb     AS SINGLE 'float fStepDb
        LOCAL pfCurrentDb AS SINGLE 'float pfCurrentDb
    
        ' Get enumerator for audio endpoint devices.
        gpIMMDeviceEnumMicrophone = NEWCOM CLSID CLSID_MMDeviceEnumerator
        IF ISNOTHING(gpIMMDeviceEnumMicrophone) THEN
            ? "Unable to create audio device enumerator."
            FUNCTION = 0
            EXIT FUNCTION
        END IF
    
        ' // Enumerate the audio endpoints of interest (in this case audio capture endpoints)
        ' // The flags that can be used are:
        ' // %EDataFlow_eRender (for audio output endpoints),
        ' // %EDataFlow_eCapture (for audio capture endpoints),
        ' // %EDataFlow_eAll (for all audio endpoints)
    
        ' // %ERole_eConsole = Games, system notification sounds, and voice commands.
        ' // %ERole_eMultimedia = Music, movies, narration, and live music recording.
        ' // %ERole_eCommunications = Voice communications (talking to another person).
                                                                                                '%DEVICE_STATE_ACTIVE
        giMicrophoneHr = gpIMMDeviceEnumMicrophone.GetDefaultAudioEndpoint(%EDataFlow_eCapture, %ERole_eConsole, gpIMMDeviceMicrophone)
        IF FAILED(giMicrophoneHr) THEN
            ? "Unable to find default microphone device."
            FUNCTION = 0
            EXIT FUNCTION
        END IF
    
        xhr = gpIMMDeviceMicrophone.Activate( _
                        IID_IAudioEndpointVolume, _
                        %CLSCTX_INPROC_SERVER,    _
                        BYVAL %NULL,             _
                        gpIAudioEndpointVolumeMicrophone)
        IF FAILED(xhr) THEN
            'CALL SHOW_NOTES_NO_WAIT("Unable to activate a microphone.")
            ? "Unable to activate default microphone"
            FUNCTION = 0
            EXIT FUNCTION
        ELSE
            ' "Default microphone activated successfully!"
    
            ' // Get the device identifier
            xhr = gpIMMDeviceMicrophone.GetId(pDevId)
            IF pDevId THEN
                 XFill = "Device id = " & @pDevId  'Device id = {0.0.1.00000000}.{be4cea6b-aba4-4420-8e08-dbfb7a6be10c}
                 'call COPY_TO_CLIPBOARD
                CoTaskMemFree(pDevId)
            END IF
            ' // Get the device name
            gsTheMicrophoneName = AfxGetDeviceName(gpIMMDeviceMicrophone)
            '? gsTheMicrophoneName
    
            'Get the endpoint device's IDeviceTopology interface.
            xhr = gpIMMDeviceMicrophone.Activate( _
                          IID_IDeviceTopology,    _
                          %CLSCTX_ALL,            _
                          BYVAL %NULL,            _
                          pDeviceTopology)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get microphone topology.")
                ? "Unable to get microphone topology."
                FUNCTION = 0
                EXIT FUNCTION
            END IF
    
            'The device topology for an endpoint device always
            'contains just one connector (connector number 0).
            xhr = pDeviceTopology.GetConnector(0, pConnFrom)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get the connector info.")
                ? "Unable to get the connector info."
                FUNCTION = 0
                EXIT FUNCTION
            END IF
            pDeviceTopology = NOTHING
    
            'Make sure that this is a capture device.
            xhr = pConnFrom.GetDataFlow(pflow)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get connector flow data.")
                ? "Unable to get connector flow data."
                FUNCTION = 0
                EXIT FUNCTION
            ELSE
                'For example, a typical rendering device on an adapter has a connector
                'with data-flow direction "In" through which the Windows audio engine
                'streams PCM data into the device. The same device has a connector with
                'data-flow direction "Out" through which the device transmits an audio
                'signal to speakers or headphones.
    
                '%DataFlow_In  = 0  Input stream. The audio stream flows into the device through the connector.
                '%DataFlow_Out = 1  Output stream. The audio stream flows out of the device through the connector.
                SELECT CASE pFlow
                    CASE 0  'In
                        'Error -- this is a rendering device.
                        'EXIT_ON_ERROR(xhr = %AUDCLNT_E_WRONG_ENDPOINT_TYPE)
                        'CALL SHOW_NOTES_NO_WAIT("Error! The connector flow data was wrong.")
                        ? "Error! The connector flow data was wrong."
                        FUNCTION = 0
                        EXIT FUNCTION
    
                    CASE 1  'Out
                        '? "DataFlow is out stream."
                END SELECT
            END IF
    
            '--------------------------------------
            'Computer\HKEY_CURRENT_USER\Control Panel\International\User Profile\en-US (if exists)0x00000001 (1)
            'Name 0409:00000409
            'Type: REG_DWORD
            'Data: 0x00000001 (1)
    
            LOCAL dwReserved AS DWORD
            LOCAL MyVal      AS DWORD
            LOCAL zLocale    AS ASCIIZ * %MAX_PATH
    
            IF EnumUILanguagesA (CODEPTR(EnumUILanguagesCB()), dwReserved, MyVal) THEN
              'If the function succeeds, the return value is TRUE.
              'let pass
            ELSE
              ? "Error determining language to support"
            END IF
    
            REPLACE "English 0409" WITH "en-US" IN sBuffer   'windows 10
            REPLACE "English 0009" WITH "en"    IN sBuffer   'windows 7
            REPLACE "French 040C"  WITH "fr-FR" IN sBuffer   'windows 10
            REPLACE "French 000C"  WITH "fr"    IN sBuffer   'windows 7
    
            IF INSTR(sBuffer, "fr") THEN
                ComponentName = "Ampli Microphone"     'french
            ELSE
                ComponentName = "Microphone Boost"     'english
            END IF
    
            '--------------------------------------
            CALL SelectCaptureDevice
    
            pConnFrom = NOTHING
    
            '\\\\\\\\\\\\\\\
            'Get the endpoint device's IAudioMeterInformation interface.
            xhr = gpIMMDeviceMicrophone.Activate( _
                            IID_IAudioMeterInformation, _
                            %CLSCTX_ALL,                _
                            BYVAL %NULL,                _
                            gpMeterInfo)
            IF FAILED(xhr) THEN
                'CALL SHOW_NOTES_NO_WAIT("Unable to get peak meter info.")
                ? "Unable to get peak meter info."
                FUNCTION = 0
                EXIT FUNCTION
            END IF
    
        END IF
    
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  SetVolumeMicrophone
    '_________________________________________________________________
    
    FUNCTION SetVolumeMicrophone(BYVAL MicroScalar AS SINGLE) AS LONG
    
        LOCAL xhr   AS LONG
    
        IF giMicrophone = 1 THEN
            IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
                xhr = gpIAudioEndpointVolumeMicrophone.SetMasterVolumeLevelScalar(MicroScalar, g_guid_2_Context) 'BYVAL %NULL)  'BYREF pguidEventContext AS GUID
    
                IF FAILED(xhr) THEN
                    'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on default microphone.")
                    ? "Unable to set volume state on default microphone"
                    FUNCTION = 0
                    EXIT FUNCTION
                ELSE
                    ' "Default microphone volume set successfully!"
                END IF
    
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  RefreshVolumeTrackbarPos
    '_________________________________________________________________
    
    FUNCTION RefreshVolumeTrackbarPos(BYVAL MicroScalar AS SINGLE) AS LONG
        giMasterMicrophoneLevel = MicroScalar * 100
        '? STR$(giMasterMicrophoneLevel)
        SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETPOS, %TRUE, %TrackbarMax - MicroScalar * 100)
        SetDlgItemTextA (hDlg, %LabelVolMicro, FORMAT$(MicroScalar * 100, "##0"))
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  GetVolumeMicrophone
    '_________________________________________________________________
    
    FUNCTION GetVolumeMicrophone() AS LONG
    
        LOCAL xhr     AS LONG
        LOCAL fVolume AS SINGLE
        IF giMicrophone = 1 THEN
            IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
                xhr = gpIAudioEndpointVolumeMicrophone.GetMasterVolumeLevelScalar(fVolume)
    
                IF FAILED(xhr) THEN
                    'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on default microphone.")
                    ? "Unable to get volume state on default microphone"
                    FUNCTION = 0
                    EXIT FUNCTION
                ELSE
                    ' "Default microphone volume get successfully!"
                END IF
                '? "Microphone Volume=" & STR$(fVolume)
                giMasterMicrophoneLevel = fVolume * 100
                '? STR$(giMasterMicrophoneLevel)
                SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETPOS, %TRUE, %TrackbarMax - fVolume * 100)
                SetDlgItemTextA (hDlg, %LabelVolMicro, FORMAT$(fVolume * 100, "##0"))
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  GetMuteMicrophone
    '_________________________________________________________________
    
    FUNCTION GetMuteMicrophone AS LONG
    
        LOCAL xhr   AS LONG
        LOCAL fMute AS LONG
    
        IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
            xhr = gpIAudioEndpointVolumeMicrophone.GetMute(fMute)
            IF FAILED(xhr) THEN
                ? "Unable to get microphone mute level."
                FUNCTION = 0
                EXIT FUNCTION
            ELSE
                '? "Default microphone mute level is " + STR$(fMute)
            END IF
    
        END IF
        giMasterMicrophoneMuteLevel = fMute
        SELECT CASE fMute
            CASE 1
                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                'IconSet_2(0)
    
            CASE ELSE
                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                'IconSet_2(1)
        END SELECT
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  MuteMicrophone
    '_________________________________________________________________
    
    FUNCTION MuteMicrophone AS LONG
    
        LOCAL xhr   AS LONG
    
        IF gsMasterVolumeIsChangeable = "YES" THEN
            IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
                xhr = gpIAudioEndpointVolumeMicrophone.SetMute(1, g_guid_2_Context) '%TRUE,  BYREF pguidEventContext AS GUID
                IF FAILED(xhr) THEN
                    ? "Unable to mute default microphone."
                    '? "Unable to set mute state on default microphone"
                ELSE
                    '? "Default microphone muted successfully!"
                END IF
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  UnMuteMicrophone
    '_________________________________________________________________
    
    FUNCTION UnMuteMicrophone AS LONG
    
        LOCAL xhr   AS LONG
    
        IF gsMasterVolumeIsChangeable = "YES" THEN
            IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
    
                xhr = gpIAudioEndpointVolumeMicrophone.SetMute(0, g_guid_2_Context) '%FALSE,  BYREF pguidEventContext AS GUID
                IF FAILED(xhr) THEN
                    ? "Unable to unmute default microphone."
                    '? "Unable to set unmute state on default microphone"
                ELSE
                    '? "Default microphone unmuted successfully!"
                END IF
    
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    '_________________________________________________________________
    '
    '   FUNCTION  MicrophoneMuteSelect
    '_________________________________________________________________
    
    FUNCTION MicrophoneMuteSelect AS LONG
    
        IF giMicrophone = 1 THEN
    
            IF giMicrophoneMute = 0 THEN
                MuteMicrophone
            ELSE
                UnMuteMicrophone
            END IF
        END IF
    
        FUNCTION = 1
    END FUNCTION
    
    ' ========================================================================================
    
    FUNCTION SetMicrophoneCallback 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(gpIAudioEndpointVolumeMicrophone) THEN
    
            xhr = CoCreateGuid (g_guid_2_Context)
    
            LET ppNotify_2 = CLASS "CIAudioEndpointVolumeCallback_2"
    
            xhr = gpIAudioEndpointVolumeMicrophone.RegisterControlChangeNotify(BYVAL ppNotify_2) 'IAudioEndpointVolumeCallback
    
            IF FAILED(xhr) THEN
                ? "Unable to set microphone callback."
                FUNCTION = 0
                EXIT FUNCTION
            ELSE
                '? "Default microphone callback."
            END IF
    
        END IF
        FUNCTION = 1
    END FUNCTION
    
    'MICROPHONE BOOST
    '_________________________________________________________________
    '
    '   FUNCTION  GetVolumeMicrophoneBoost
    '_________________________________________________________________
    
    FUNCTION GetVolumeMicrophoneBoost() AS LONG
    
        LOCAL xhr     AS LONG
        LOCAL fVolume AS SINGLE
        IF giMicrophone = 1 THEN
            IF ISOBJECT(pIaudioVolumeLevel) THEN
    
                xhr = pIaudioVolumeLevel.GetLevel(0, fVolume)
    
                IF FAILED(xhr) THEN
                    'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on microphone boost.")
                    ? "Unable to get volume state on microphone boost"
                    FUNCTION = 0
                    EXIT FUNCTION
                ELSE
                    ' "Default microphone boost volume get successful!"
                END IF
                giMasterMicroBoostLevel = fVolume
                '? "Microphone Boost Volume=" & STR$(fVolume)
                '? str$(fVolume/10)
                SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, %TrackbarMaxBoost - fVolume/10)
                SetDlgItemTextA (hDlg, %LabelVolMicroBoost, FORMAT$(fVolume, "##0") & " dB")
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   FUNCTION  SetVolumeMicrophoneBoost
    '_________________________________________________________________
    
    FUNCTION SetVolumeMicrophoneBoost(BYVAL MicroScalar AS SINGLE) AS LONG
    
        LOCAL xhr   AS LONG
    
        IF giMicrophone = 1 THEN
            IF ISOBJECT(pIaudioVolumeLevel) THEN
    
                xhr = pIaudioVolumeLevel.SetLevel(0, MicroScalar, g_guid_3_Context) 'BYVAL %NULL)  'BYREF pguidEventContext AS GUID
    
                IF FAILED(xhr) THEN
                    'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on microphone boost.")
                    ? "Unable to set volume state on microphone boost"
                    FUNCTION = 0
                    EXIT FUNCTION
                ELSE
                    ' "Default microphone boost volume set successful!"
                END IF
    
            END IF
        END IF
        FUNCTION = 1
    END FUNCTION
    
    ' ========================================================================================
    
    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
    
    
    '_________________________________________________________________
    '
    '   FUNCTION  GlobalVariableTest86                            VER4
    '_________________________________________________________________
    
    FUNCTION GlobalVariableTest86() AS LONG
        IF giProgStat = 0 THEN
            FUNCTION = 1
        END IF
    END FUNCTION
    '_________________________________________________________________
    '
    '   FUNCTION  GlobalVariableTestOn                            VER4
    '_________________________________________________________________
    
    FUNCTION GlobalVariableTestOn() AS LONG
        IF giProgStat = 1 THEN
            FUNCTION = 1
        END IF
    END FUNCTION
    
    '_________________________________________________________________
    '
    ' Processes pending Windows messages.                         VER4
    ' Call this procedure if you are performing a tight
    ' FOR/NEXT or DO/LOOP and need to allow
    ' your application to be responsive to user input.
    ' Modified version of AfxPumpMessages
    '_________________________________________________________________
    
    SUB MyPumpMessages(BYVAL xTimes AS LONG) EXPORT
      '/////////////////////////////////////////////////////////////
      'Do not want to remove the WM_QUIT message from message que.
      IF GlobalVariableTest86 = 1 THEN EXIT SUB
      '/////////////////////////////////////////////////////////////
    
      LOCAL Ix AS LONG
    
      IF giPumpingMessages = 1 THEN EXIT SUB
      giPumpingMessages = 1
    
      IF GlobalVariableTestOn THEN
          IF xTimes <= 0 THEN xTimes = 1
          FOR Ix = 1 TO xTimes
              CALL PeekRemoveTranslateDispatchMessage
              IF GlobalVariableTest86 = 1 THEN GOTO NoMoreNeeded
          NEXT Ix
      END IF
    
      NoMoreNeeded:
      giPumpingMessages = 0
    END SUB
    
    '_________________________________________________________________
    '
    ' GetEvents dialog procedure (replacement for Sleep 0)
    ' Sleep 0 releases time slices back to the OS but does
    ' not necessarily give your program the slices back.
    '
    '   SUB  GetEvents
    '_________________________________________________________________
    
    SUB GetEvents(SleepTime AS LONG) EXPORT
    
        IF SleepTime <= 0 THEN SleepTime = 1
    
        CALL PeekRemoveTranslateDispatchMessage
    
        SLEEP SleepTime/4
    
        CALL PeekRemoveTranslateDispatchMessage
    
        SLEEP SleepTime/4
    
        CALL PeekRemoveTranslateDispatchMessage
    
        SLEEP SleepTime/4
    
        CALL PeekRemoveTranslateDispatchMessage
    
        SLEEP SleepTime/4
    
    END SUB
    
    '_________________________________________________________________
    '
    '   SUB  PeekRemoveTranslateDispatchMessage                   VER4
    '_________________________________________________________________
    
    SUB PeekRemoveTranslateDispatchMessage
      '/////////////////////////////////////////////////////////////
      'Do not want to remove the WM_QUIT message from message que.
      IF GlobalVariableTest86 = 1 THEN EXIT SUB
      '/////////////////////////////////////////////////////////////
      'Could use this to signal main message loop to close
      'PostThreadMessageA (GetCurrentThreadID, %WM_QUIT, 0, 0)   ' Signal Quit to Msg Loop
    
      IF giPRTDMessage = 1 THEN EXIT SUB
      giPRTDMessage = 1
    
      STATIC Msg AS tagMsg
      IF PeekMessageA(Msg, %NULL, %NULL, %NULL, %PM_REMOVE) THEN
          TranslateMessage Msg
          DispatchMessageA Msg
      END IF
    
      giPRTDMessage = 0
    END SUB
    
    
    '//-----------------------------------------------------------
    '// This function traverses the data path that extends from the
    '// endpoint device to the system bus (for example, PCI)
    '// or external bus (USB). If the function discovers a MUX
    '// (input selector) in the path, it selects the MUX input
    '// that connects to the stream from the endpoint device.
    '// In this case we are looking for the Microphone or Microphone
    '// Boost parts.
    '//-----------------------------------------------------------
    
         'Limits of Device Topology
         'Where (con) is a connector
         'microphone -> endpoint device B (con) physical external
         '
         '-> Input Multiplexer Device (Topology Filter)
         '   (con) physical external   IPart & IConnector
         '   subunit Mute ->           IPart & ISubUnit
         '   subunit Vol ->            IPart & ISubUnit
         '   subunit MUX ->            IPart & ISubUnit
         '   (Con) Software Fixed      IPart & IConnector
         '
         '-> Wave Capture Device (Wave Filter)
         '   (Con) Software Fixed      IPart & IConnector
         '   subunit ADC               IPart & ISubUnit
         '   (Con) Software IO         IPart & IConnector
         '
         '-> System Bus Wave-in Stream (DMA)
    
    
    FUNCTION SelectCaptureDevice() AS LONG
        LOCAL hr       AS LONG
        LOCAL ppwstrGlobalId AS WSTRINGZ PTR
        LOCAL ppBoostTopology AS IDeviceTopology
    
        hr = %S_OK
        pIaudioVolumeLevel = NOTHING
    
        IF ISOBJECT(pConnFrom) THEN
            'continue
        ELSE
            GOTO ExitHere
        END IF
    
        '// Outer loop: Each iteration traverses the data path
        '// through a device topology starting at the input
        '// connector and ending at the output connector.
        WHILE %TRUE
            IF giProgStat = 0 THEN GOTO ExitHere
    
            hr = pConnFrom.IsConnected(bConnected)
            IF FAILED(hr) THEN GOTO ExitHere
    
            '// Does this connector connect to another device?
            IF bConnected = %FALSE THEN
    
                '// This is the end of the data path that
                '// stretches from the endpoint device to the
                '// system bus or external bus. Verify that
                '// the connection type is Software_IO.
                hr = pConnFrom.GetType(connType)
                IF FAILED(hr) THEN GOTO ExitHere
    
                IF connType = %ConnectorType_Software_IO THEN
                    '? "Finished looking for connections."
                    EXIT LOOP '// finished
                END IF
                IF FAILED(hr = %E_FAIL) THEN GOTO ExitHere
            END IF
    
            '// Get the connector in the next device topology,
            '// which lies on the other side of the connection.
            hr = pConnFrom.GetConnectedTo(pConnTo)
            IF FAILED(hr) THEN GOTO ExitHere
    
            '// Get the connector's IPart interface.
            hr = pConnTo.QueryInterface( _
                            IID_IPart, _
                            BYVAL VARPTR(pPartPrev))
            IF FAILED(hr) THEN GOTO ExitHere
            pConnTo = NOTHING
    
            '// Inner loop: Each iteration traverses one link in a
            '// device topology and looks for input multiplexers.
            WHILE %TRUE
    
                IF giProgStat = 0 THEN GOTO ExitHere
    
                MyPumpMessages(2000)
                '// Follow downstream link to next part.
                hr = pPartPrev.EnumPartsOutgoing(ppParts)
                IF FAILED(hr) THEN GOTO ExitHere
    
                hr = ppParts.GetPart(0, pPartNext)
                ppParts = NOTHING
                IF FAILED(hr) THEN GOTO ExitHere
    
                hr = pPartNext.GetPartType(pPartType)
                IF FAILED(hr) THEN GOTO ExitHere
    
                IF SUCCEEDED(pPartNext.GetName(pName)) THEN
                    '? @pName   'Microphone Boost
    
                    'Failure of the following call means only that
                    'the part is not a boost (microphone boost).
                    IF UCASE$(ComponentName) <> UCASE$(@pName) THEN
                        '? "Not a match."
                    ELSE
                        '? "The part name matched."
                        'get IAudioVolumeLevel to control volume
                        hr = pPartNext.Activate( _
                                    %CLSCTX_ALL, _
                                    IID_IAudioVolumeLevel, _
                                    pIaudioVolumeLevel)
    
                        CALL SetMicrophoneBoostCallback
    
                        GOTO ExitHere
                    END IF
                    CoTaskMemFree(pName)
                END IF
    
                '? "Try again..."
    
                SELECT CASE pPartType
                    CASE %PartType_Connector  '=0
                        '? "Connector found."
                        '// We've reached the output connector that
                        '// lies at the end of this device topology.
                        hr = pPartNext.QueryInterface( _
                                          IID_IConnector, _
                                          BYVAL VARPTR(pConnFrom))
                        IF FAILED(hr) THEN GOTO ExitHere
    
                        pPartPrev = NOTHING
                        pPartNext = NOTHING
                        EXIT LOOP
    
                    CASE %PartType_Subunit '=1
                        '? "Subunit found."
    
                END SELECT
    
                '// Failure of the following call means only that
                '// the part is not a MUX (input selector).
                hr = pPartNext.Activate( _
                                  %CLSCTX_ALL, _
                                  IID_IAudioInputSelector, _
                                  pSelector)
                IF hr = %S_OK THEN
                    ? "The MUX was found."
                    '// We found a MUX (input selector), so select
                    '// the input from our endpoint device.
                    LOCAL localId AS DWORD
                    hr = pPartPrev.GetLocalId(localId)
                    IF FAILED(hr) THEN GOTO ExitHere
    
                    hr = pSelector.SetSelection(localId, BYVAL %NULL)
                    IF FAILED(hr) THEN GOTO ExitHere
    
                    pSelector = NOTHING
                END IF
    
                pPartPrev = NOTHING
                pPartPrev = pPartNext
                pPartNext = NOTHING
            WEND
        WEND
    
    ExitHere:
        ppParts   = NOTHING
        pConnTo   = NOTHING
        pPartPrev = NOTHING
        pPartNext = NOTHING
        pSelector = NOTHING
        FUNCTION = hr
    
    END FUNCTION
    '_____________________________________________________________________________
    
    CALLBACK FUNCTION DlgProc
        LOCAL  pNMHDR                    AS NMHDR POINTER
        LOCAL  rcThumb_1                 AS RECT
        LOCAL  rcThumb_2                 AS RECT
        LOCAL  rcThumb_3                 AS RECT
        LOCAL  xhr                       AS LONG
        STATIC Mute                      AS LONG
        STATIC TrackBarPos_1             AS LONG
        STATIC TrackBarPos_2             AS LONG
        STATIC TrackBarPos_3             AS LONG
        LOCAL pid                        AS DWORD
    
        '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        'Create a timer event every 1/8 Sec = 125
        '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        SetTimer hDlg, %IDC_Timer0, 125, BYVAL %NULL
    
        SELECT CASE CBMSG
    
            CASE %WM_INITDIALOG
                giProgStat = 1
                gsMasterVolumeIsChangeable = "YES"
                giStartStopRecognition = 1     'start recognition
    
                IF VistaPlus THEN 'Vista to 10
                    giStereoSpeakers = StereoSpeakersCheck
                    '? str$(giStereoSpeakers)
                    CALL GetMuteStereoSpeakers
                    CALL GetVolumeStereoSpeakers
                    CALL SetVolumeCallback
    
                    gpMeterInfo = NOTHING
                    giMicrophone     = MicrophoneCheck
                    CALL GetMuteMicrophone
                    CALL GetVolumeMicrophone
                    CALL SetMicrophoneCallback
    
                    CALL GetVolumeMicrophoneBoost
    
                  '  SetVolumeMicrophone(SCALER_MB)
                  '  '? str$(giMicrophone)
                    CALL CREATE_THREAD_SERVICES
    
                ELSE '95 to XP
                    ? "Sorry, this app does not support this OS."
                END IF
    
            CASE %WM_APP
                SELECT CASE CB.WPARAM
                    CASE %CheckboxVolOnOffSpeaker
                        'NOTIFY MASTER MUTE
                        SELECT CASE CB.LPARAM
                            CASE 1
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeaker), %BM_SETCHECK, %BST_CHECKED, 0)
                                IconSet_1(0)
    
                            CASE ELSE
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeaker), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                IconSet_1(1)
                        END SELECT
    
                    CASE %TrackbarVolSpeaker
                        'NOTIFY MASTER VOLUME
                        PostMessageA (GetDlgItem(hDlg, %TrackbarVolSpeaker), %TBM_SETPOS, %TRUE, %TrackbarMax - CB.LPARAM)
                        SetDlgItemTextA (hDlg, %LabelVolSpeaker, FORMAT$(CB.LPARAM, "##0"))
    
                    CASE %CheckboxVolOnOffMicro
                        'NOTIFY MICROPHONE MUTE
                        SELECT CASE CB.LPARAM
                            CASE 1
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                                'IconSet_2(0)
    
                            CASE ELSE
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                'IconSet_2(1)
                        END SELECT
    
                    CASE %TrackbarVolMicro
                        'NOTIFY MICROPHONE VOLUME
                        PostMessageA (GetDlgItem(hDlg, %TrackbarVolMicro), %TBM_SETPOS, %TRUE, %TrackbarMax - CB.LPARAM)
                        SetDlgItemTextA (hDlg, %LabelVolMicro, FORMAT$(CB.LPARAM, "##0"))
    
                    CASE %TrackbarVolMicroBoost
                        'NOTIFY MICROPHONE BOOST
                        SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, %TrackbarMaxBoost - CB.LPARAM/10)
                        SetDlgItemTextA (hDlg, %LabelVolMicroBoost, FORMAT$(CB.LPARAM, "##0") & " dB")
    
                END SELECT
    
    
            CASE %WM_TIMER
               SELECT CASE CB.WPARAM
                   CASE %IDC_Timer0
                       '// Update the peak meter in the dialog box.
                       IF ISOBJECT(gpMeterInfo) THEN
                           gpMeterInfo.GetPeakValue(PEAK)
                           CONTROL SET TEXT hDlg, %IDC_PEAK_METER, USING$("##.#", PEAK * 100)
                           CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETPOS, (PEAK * 100), 0
                       END IF
    
               END SELECT
               'An application should return zero if it processes this message.
               FUNCTION = 0
               EXIT FUNCTION
    
            CASE %WM_COMMAND
                SELECT CASE CB.CTL
                    'SPEAKERS
                    CASE %LabelOnOffSpeaker
                        IF (CBCTLMSG = %STN_CLICKED) OR (CBCTLMSG = 1) THEN
                            IF IsDlgButtonChecked (hDlg, %CheckboxVolOnOffSpeaker) THEN
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeaker), %BM_SETCHECK, %BST_UNCHECKED, 0)
                            ELSE
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeaker), %BM_SETCHECK, %BST_CHECKED, 0)
                            END IF
                            PostMessageA (hDlg, %WM_COMMAND, MAKDWD(%CheckboxVolOnOffSpeaker, %BN_CLICKED), GetDlgItem(hDlg, %CheckboxVolOnOffSpeaker))
                        END IF
    
                    CASE %CheckboxVolOnOffSpeaker
                        IF (CBCTLMSG = %BN_CLICKED) OR (CBCTLMSG = 1) THEN
                            IF IsDlgButtonChecked(hDlg, %CheckboxVolOnOffSpeaker) THEN
                                Mute = %TRUE
                                IconSet_1(0)
                            ELSE
                                Mute = %FALSE 'Mute XOR %TRUE
                                IconSet_1(1)
                            END IF
                            IF VistaPlus THEN 'Vista to 10   !!!!!! mute change
                                SELECT CASE Mute
                                    CASE %TRUE
                                        CALL MuteStereoSpeakers
                                    CASE %FALSE
                                        CALL UnMuteStereoSpeakers
                                END SELECT
                            ELSE '95 to XP
                                ? "Sorry, this app does not support this OS."
                            END IF
                        END IF
    
                    CASE %ButtonSndVolExeSpeaker
                        IF (CBCTLMSG = %BN_CLICKED) OR (CBCTLMSG = 1) THEN
                            IF VistaPlus THEN 'Vista to 10
                                'SndVol.exe, use the -f flag to show the master volume slider only: SndVol.exe -f
                                'SndVol.exe allows you to specify window location by adding coordinates after -f switch:
                                pid = SHELL("SndVol.exe") '"C:\Windows\System32\SndVol.exe"
                            ELSE '95 to XP
                                ? "Sorry, this app does not support this OS."
                            END IF
                        END IF
    
                    'MICROPHONE
                    CASE %LabelOnOffMicro
                        IF (CBCTLMSG = %STN_CLICKED) OR (CBCTLMSG = 1) THEN
                            IF IsDlgButtonChecked (hDlg, %CheckboxVolOnOffMicro) THEN
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                            ELSE
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                            END IF
                            PostMessageA (hDlg, %WM_COMMAND, MAKDWD(%CheckboxVolOnOffMicro, %BN_CLICKED), GetDlgItem(hDlg, %CheckboxVolOnOffMicro))
                        END IF
    
                    CASE %CheckboxVolOnOffMicro
                        IF (CBCTLMSG = %BN_CLICKED) OR (CBCTLMSG = 1) THEN
                            IF IsDlgButtonChecked(hDlg, %CheckboxVolOnOffMicro) THEN
                                Mute = %TRUE
                                'IconSet_2(0)
                            ELSE
                                Mute = %FALSE 'Mute XOR %TRUE
                                'IconSet_2(1)
                            END IF
                            IF VistaPlus THEN 'Vista to 10   !!!!!! mute change
                                SELECT CASE Mute
                                    CASE %TRUE
                                        CALL MuteMicrophone
                                    CASE %FALSE
                                        CALL UnMuteMicrophone
                                END SELECT
                            ELSE '95 to XP
                                ? "Sorry, this app does not support this OS."
                            END IF
                        END IF
    
                END SELECT
    
            CASE %WM_NOTIFY
                pNMHDR = CBLPARAM
                'SPEAKERS
                IF @pNMHDR.hwndFrom = GetDlgItem(hDlg, %TrackbarVolSpeaker) THEN
                    IF @pNMHDR.code = %NM_RELEASEDCAPTURE THEN
                        BEEP
                    END IF
                    SendMessageA (@pNMHDR.hwndFrom, %TBM_GETTHUMBRECT, 0, VARPTR(rcThumb_1))
                    MapWindowPoints (@pNMHDR.hwndFrom, hDlg, BYVAL VARPTR(rcThumb_1), 2)
                    SetWindowPos (GetDlgItem(hDlg, %LabelVolSpeaker), 0, rcThumb_1.nRight + 5, _
                                            rcThumb_1.nTop - 2, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                END IF
                'MICROPHONE
                IF @pNMHDR.hwndFrom = GetDlgItem(hDlg, %TrackbarVolMicro) THEN
                    IF @pNMHDR.code = %NM_RELEASEDCAPTURE THEN
                        'BEEP
                    END IF
                    SendMessageA (@pNMHDR.hwndFrom, %TBM_GETTHUMBRECT, 0, VARPTR(rcThumb_2))
                    MapWindowPoints (@pNMHDR.hwndFrom, hDlg, BYVAL VARPTR(rcThumb_2), 2)
                    SetWindowPos (GetDlgItem(hDlg, %LabelVolMicro), 0, rcThumb_2.nRight + 5, _
                                            rcThumb_2.nTop - 2, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                END IF
                'MICROPHONE BOOST
                IF @pNMHDR.hwndFrom = GetDlgItem(hDlg, %TrackbarVolMicroBoost) THEN
                    IF @pNMHDR.code = %NM_RELEASEDCAPTURE THEN
                        'BEEP
                    END IF
                    'SendMessageA (@pNMHDR.hwndFrom, %TBM_GETTHUMBRECT, 0, VARPTR(rcThumb_3))
                    'MapWindowPoints (@pNMHDR.hwndFrom, hDlg, BYVAL VARPTR(rcThumb_3), 2)
                    'SetWindowPos (GetDlgItem(hDlg, %LabelVolMicro), 0, rcThumb_3.nRight + 5, _
                    '                        rcThumb_3.nTop - 2, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                END IF
    
            CASE %WM_VSCROLL
                'SPEAKERS
                IF CBLPARAM = GetDlgItem(hDlg, %TrackbarVolSpeaker) THEN
                    SELECT CASE LOWRD(CBWPARAM)
                        CASE %TB_LINEDOWN, %TB_LINEUP, %TB_THUMBPOSITION, %TB_THUMBTRACK, _
                             %TB_PAGEDOWN, %TB_PAGEUP, %TB_TOP, %TB_BOTTOM', %TB_ENDSCROLL
                            TrackBarPos_1 = %TrackbarMax - SendMessageA (CBLPARAM, %TBM_GETPOS, 0, 0)
                            SetDlgItemTextA (hDlg, %LabelVolSpeaker, FORMAT$(TrackBarPos_1, "##0"))
                            'Set speaker volume
                            IF VistaPlus THEN 'Vista to 10  !!!!! volume change
                                CALL SetVolumeStereoSpeakers(TrackBarPos_1 / 100) 'range 0 to 100
                            ELSE '95 to XP
                              ? "Sorry, this app does not support this OS."
                            END IF
                    END SELECT
                END IF
                'MICROPHONE
                IF CBLPARAM = GetDlgItem(hDlg, %TrackbarVolMicro) THEN
                    SELECT CASE LOWRD(CBWPARAM)
                        CASE %TB_LINEDOWN, %TB_LINEUP, %TB_THUMBPOSITION, %TB_THUMBTRACK, _
                             %TB_PAGEDOWN, %TB_PAGEUP, %TB_TOP, %TB_BOTTOM', %TB_ENDSCROLL
                            TrackBarPos_2 = %TrackbarMax - SendMessageA (CBLPARAM, %TBM_GETPOS, 0, 0)
                            SetDlgItemTextA (hDlg, %LabelVolMicro, FORMAT$(TrackBarPos_2, "##0"))
                            'Set microphone volume
                            IF VistaPlus THEN 'Vista to 10  !!!!! volume change
                                CALL SetVolumeMicrophone(TrackBarPos_2 / 100)  'range 0 to 100
                            ELSE '95 to XP
                              ? "Sorry, this app does not support this OS."
                            END IF
                    END SELECT
                END IF
                'MICROPHONE BOOST
                IF CBLPARAM = GetDlgItem(hDlg, %TrackbarVolMicroBoost) THEN
                    SELECT CASE LOWRD(CBWPARAM)
                        CASE %TB_LINEDOWN, %TB_LINEUP, %TB_THUMBPOSITION, %TB_THUMBTRACK, _
                             %TB_PAGEDOWN, %TB_PAGEUP, %TB_TOP, %TB_BOTTOM', %TB_ENDSCROLL
                            TrackBarPos_3 = %TrackbarMaxBoost - SendMessageA (CBLPARAM, %TBM_GETPOS, 0, 0)
                            SetDlgItemTextA (hDlg, %LabelVolMicroBoost, FORMAT$(TrackBarPos_3 * 10, "##0") & " dB")
    
                            'Set microphone volume
                            IF VistaPlus THEN 'Vista to 10  !!!!! volume change
                                CALL SetVolumeMicrophoneBoost(TrackBarPos_3 * 10) 'range 0 to 3
                            ELSE '95 to XP
                              ? "Sorry, this app does not support this OS."
                            END IF
                    END SELECT
                END IF
    
    
            CASE %WM_CLOSE
                giProgStat = 0
                'DestroyWindow hWnd
    
            CASE %WM_DESTROY         'window is being destroyed
                                     'after windows is removed from screen (children still exist)
                KillTimer (hDlg, %IDC_Timer0)
    
                pSelector                            = NOTHING
                gpIMMDeviceEnumMicrophone            = NOTHING
                gpIMMDeviceMicrophone                = NOTHING
                gpIAudioEndpointVolumeMicrophone     = NOTHING
                gpMeterInfo                          = NOTHING
                pDeviceTopology                      = NOTHING
                pConnFrom                            = NOTHING
                pConnTo                              = NOTHING
                pPartPrev                            = NOTHING
                pPartNext                            = NOTHING
                ppParts                              = NOTHING
                pIaudioVolumeLevel                   = NOTHING
    
                PostQuitMessage 0
                'If an application processes this message, it should return zero.
                FUNCTION = 0
                EXIT FUNCTION
    
            'CASE %WM_DESTROY
            '    IF VistaPlus THEN 'Vista to 10    !!!!! destroy created objects
            '        IF pIMMDeviceEnumerator THEN
            '            'PP = @pAudioEndpointVolume + 16 'UnRegisterControlChangeNotify
            '            'CALL DWORD @PP USING F2(BYVAL pAudioEndpointVolume, BYVAL pAudioEndpointVolumeCallBack) TO xhr
            '            'CALL DWORD @@pIMMDeviceEnumerator[2] USING F1(@pIMMDeviceEnumerator) 'Release object
            '            'CALL DWORD @@pIMMDevice[2] USING F1(@pIMMDevice)                     'Release object
            '            'CALL DWORD @@pAudioEndpointVolume[2] USING F1(@pAudioEndpointVolume) 'Release object
            '        END IF
            '        CoUninitialize
            '
            '    ELSE '95 to XP
            '        ? "Sorry, this app does not support this OS."
            '    END IF
            '    giProgStat = 0
    
        END SELECT
    
    END FUNCTION
    
    '______________________________________________________________________________
    
    FUNCTION PBMAIN()
        LOCAL OS AS OSVERSIONINFOEXA
    
        OS.dwOSVersionInfoSize = SIZEOF(OSVERSIONINFOEXA)
        GetVersionExA (BYVAL VARPTR(OS))
        IF (OS.dwPlatformId = %VER_PLATFORM_WIN32_NT) AND (OS.dwMajorVersion >= 6) THEN
          VistaPlus = %TRUE 'Vista to 10
        END IF
    
        CLSID_MMDeviceEnumerator         = GUID$("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
        IID_IMMDeviceEnumerator          = GUID$("{A95664D2-9614-4F35-A746-DE8DB63617E6}")
        IID_IAudioMeterInformation       = GUID$("{C02216F6-8C67-4B5B-9D00-D008E73E0064}")
        IID_IDeviceTopology              = GUID$("{2A07407E-6497-4A18-9787-32F79BD0D98F}")
        IID_IAudioEndpointVolume         = GUID$("{5CDF2C82-841E-4546-9722-0CF74078229A}")
        IID_IPart                        = GUID$("{AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9}")
        IID_IAudioVolumeLevel            = GUID$("{7FB7B48F-531D-44A2-BCB3-5AD5A134B3DC}")
        IID_IConnector                   = GUID$("{9C2C4058-23F5-41DE-877A-DF3AF236A09E}")
        IID_IAudioInputSelector          = GUID$("{4F03DC02-5E6E-4653-8F72-A030C123D598}")
        IID_IAudioEndpointVolumeCallback = GUID$("{657804FA-D6AD-4496-8A60-352752AF4F89}")
        IID_IControlChangeNotify         = GUID$("{A09513ED-C709-4D21-BD7B-5F34C47F3947}")
    
        DIALOG FONT "Segoe UI", 9
        DIALOG NEW PIXELS, %HWND_DESKTOP, $AppNAme, , , 280, 400, _
        %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, 0 TO hDlg
    
        'SPEAKERS
        IconSet_1(-2) 'Init
        IconSet_1(1)  'Set to icon on
    
        CONTROL ADD LABEL, hDlg, %LabelInfoSpeaker, "Speakers:", _
                                             15, 10, 80, 20, %WS_CHILD OR %WS_VISIBLE OR %SS_NOPREFIX OR %SS_CENTER, %WS_EX_CLIENTEDGE
        CONTROL ADD LABEL, hDlg, %LabelInfoMicro, "Microphone:", _
                                             100, 10, 80, 20, %WS_CHILD OR %WS_VISIBLE OR %SS_NOPREFIX OR %SS_CENTER, %WS_EX_CLIENTEDGE
        CONTROL ADD LABEL, hDlg, %LabelInfoMicroBoost, "MicroBoost:", _
                                             185, 10, 80, 20, %WS_CHILD OR %WS_VISIBLE OR %SS_NOPREFIX OR %SS_CENTER, %WS_EX_CLIENTEDGE
    
        CONTROL ADD LABEL, hDlg, %LabelMaxSpeaker, "Max", 35, 40, 30, 12
        CONTROL ADD LABEL, hDlg, %LabelMinSpeaker, "Min", 35, 195, 30, 12
        CONTROL ADD LABEL, hDlg, %LabelVolSpeaker, "---", 60, 120, 30, 12
    
        CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarVolSpeaker, "msctls_trackbar321", 30, 55, 25, 140, _
        %WS_VISIBLE OR %WS_CHILD OR %TBS_BOTH OR %TBS_NOTICKS OR %WS_TABSTOP OR %TBS_VERT OR %TBS_REVERSED
        SendDlgItemMessageA (hDlg, %TrackbarVolSpeaker, %TBM_SETPOS, %TRUE, 50)
        SendDlgItemMessageA (hDlg, %TrackbarVolSpeaker, %TBM_SETPAGESIZE,0, 10)
        SendDlgItemMessageA (hDlg, %TrackbarVolSpeaker, %TBM_SETRANGEMAX, %TRUE, %TrackbarMax)
        SendDlgItemMessageA (hDlg, %TrackbarVolSpeaker, %TBM_SETRANGEMIN, %TRUE, 0)
    
    
        CONTROL ADD LABEL, hDlg, %LabelMaxMicro, "Max", 125, 40, 30, 12
        CONTROL ADD LABEL, hDlg, %LabelMinMicro, "Min", 125, 195, 30, 12
        CONTROL ADD LABEL, hDlg, %LabelVolMicro, "---", 150, 120, 30, 12
    
        CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarVolMicro, "msctls_trackbar321", 120, 55, 25, 140, _
        %WS_VISIBLE OR %WS_CHILD OR %TBS_BOTH OR %TBS_NOTICKS OR %WS_TABSTOP OR %TBS_VERT OR %TBS_REVERSED
        SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETPOS, %TRUE, 50)
        SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETPAGESIZE,0, 10)
        SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETRANGEMAX, %TRUE, %TrackbarMax)
        SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETRANGEMIN, %TRUE, 0)
    
    
        CONTROL ADD LABEL, hDlg, %LabelMaxMicroBoost, "Max", 90 + 125, 40, 30, 12
        CONTROL ADD LABEL, hDlg, %LabelMinMicroBoost, "Min", 90 + 125, 195, 30, 12
        CONTROL ADD LABEL, hDlg, %LabelVolMicroBoost, "---", 90 + 150, 120, 30, 12
    
        CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarVolMicroBoost, "msctls_trackbar321", 90 + 120, 55, 25, 140, _
        %WS_VISIBLE OR %WS_CHILD OR %TBS_BOTH OR %TBS_NOTICKS OR %WS_TABSTOP OR %TBS_VERT OR %TBS_REVERSED
        'SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, 10)
        SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPAGESIZE,0, 10)
        SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETRANGEMAX, %TRUE, 3)
        SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETRANGEMIN, %TRUE, 0)
    
    
        CONTROL ADD CHECKBOX, hDlg, %CheckboxVolOnOffMicro, "Microphone Mute", 50, 20 + 210, 140, 12, _
        %BS_AUTOCHECKBOX OR %WS_TABSTOP
    
        CONTROL ADD CHECKBOX, hDlg, %CheckboxVolOnOffSpeaker, "Speaker Mute", 50, 20 + 230, 140, 12, _
        %BS_AUTOCHECKBOX OR %WS_TABSTOP
    
        CONTROL ADD LABEL, hDlg, %LabelOnOffSpeaker, "", 190, 240, _
        GetSystemMetrics (%SM_CXICON), GetSystemMetrics(%SM_CYICON), %SS_ICON OR %SS_NOTIFY
    
        IconSet_1(1)
    
        CONTROL ADD BUTTON, hDlg, %ButtonSndVolExeSpeaker, "Volume Mixer", 50, 280, 180, 22
    
    
        CONTROL ADD PROGRESSBAR, hDlg, %ID_VOLUME, "", 15, 320, 250, 20
        CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETRANGE, 0, MAKLNG(0, 100)
        CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETSTEP, -1, 0
        CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETBKCOLOR, 0, RGB(50,50,50)
        CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETBARCOLOR, 0, RGB(55,224,230)
    
        CONTROL ADD LABEL, hDlg, %IDC_PEAK_METER, "", 15, 345, 250, 20
    
    
    
        DIALOG SHOW MODAL hDlg CALL DlgProc
    
        IconSet_1(-1) 'Release
    
    END FUNCTION
    '______________________________________________________________________________
    
    '_________________________________________________________________
    '
    '   SUB CREATE_THREAD_SERVICES
    '                    full time
    '_________________________________________________________________
    
    SUB CREATE_THREAD_SERVICES
      CALL CreateRecognitionProcess
    
      LOCAL idThreadPeakMeter         AS LONG
    
      THREAD CREATE PeakMeterThread(1) TO idThreadPeakMeter
      THREAD CLOSE idThreadPeakMeter TO idThreadPeakMeter
    END SUB
    '_________________________________________________________________
    '
    '   THREAD FUNCTION  PeakMeterThread                          VER4
    '                    full time
    '_________________________________________________________________
    
    THREAD FUNCTION PeakMeterThread(BYVAL yyy AS LONG) AS LONG
    
        DO WHILE GlobalVariableTestOn
    
            '// Update the peak meter in the dialog box.
            IF ISOBJECT(gpMeterInfo) THEN
                gpMeterInfo.GetPeakValue(PEAK)
                CONTROL SEND hDlg, %IDC_PEAK_METER, %PBM_SETPOS, PEAK * 1000, 0
            END IF
    
            SLEEP 10
            IF GlobalVariableTest86 = 1 THEN GOTO LoopEnd
            SLEEP 20
        LOOP
        LoopEnd:
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   SUB  CreateRecognitionProcess                           Jarvis
    '_________________________________________________________________
    
    SUB CreateRecognitionProcess
      LOCAL idThreadRecognition        AS LONG
    
      THREAD CREATE RecogProcessThread(1) TO idThreadRecognition
      THREAD CLOSE idThreadRecognition TO idThreadRecognition
    END SUB
    '_________________________________________________________________
    '
    '   THREAD FUNCTION  RecogProcessThread                     Jarvis
    '                    full time
    '_________________________________________________________________
    
    THREAD FUNCTION RecogProcessThread(BYVAL yyy AS LONG) AS LONG
        LOCAL MyPhraseData AS STRING
    
        SLEEP 1000 '75     'small delay
    
        giRunningRecogProcess = 1
    
        DO WHILE GlobalVariableTestOn = 1
    
            IF TRIM$(MyDataX) <> "" THEN
                MyPhraseData = MyDataX
                MyDataX = ""
                CALL PutColonOnMarque
                CALL PROCESS_RECOGNITION(MyPhraseData)
                CALL ResetRecognition
            END IF
    
            SLEEP 20
            CALL MyPumpMessages(100)
            IF GlobalVariableTest86 = 1 THEN
                EXIT LOOP
            END IF
    
            SELECT CASE RecognitionStatus
                CASE 0
                    IF giStartStopRecognition = 1 THEN
                        giStartStopRecognition = 0  'reset signal
    
                        IF ISFALSE ISOBJECT(oRecoContext) THEN
                            CALL CREATE_RECOGNITION_CONTEXT
                        END IF
    
                        CALL RECOGNITION_ENABLE
                        RecognitionStatus = 1
    
                        'Emmulate recognition to test the interface.
                        oRecoContext.Recognizer.EmulateRecognition("Recognition has started")
    
                    END IF
    
                CASE 1
                    IF giStartStopRecognition = 2 THEN
                        giStartStopRecognition = 0  'reset signal
                        CALL RECOGNITION_OFF
                    END IF
    
            END SELECT
    
            SLEEP 20
            CALL MyPumpMessages(100)
            IF GlobalVariableTest86 = 1 THEN
                EXIT LOOP
            END IF
    
        LOOP
    
        giRunningRecogProcess = 0
        'winbeep(5000, 75)
    END FUNCTION
    '_________________________________________________________________
    '
    ' SUB  CREATE_RECOGNITION_CONTEXT                           Jarvis
    '_________________________________________________________________
    
    SUB CREATE_RECOGNITION_CONTEXT
      LOCAL vInput     AS STRING
    
      SLEEP 10
    
      'Create an instance of the ISpeechRecoContext Interface
      oRecoContext = NEWCOM "SAPI.SpInProcRecoContext"
    
      '** VB WithEvents work around. **
      'Link the events of oRecoContext to InProcEvents to
      'process a recognition event.
      InProcEvents = CLASS "CISpeechRecoContextEventsImplemented"
      EVENTS FROM oRecoContext CALL InProcEvents
    
      'Create the InProc Speech Recognizer.
      oRecognizer = oRecoContext.Recognizer
    
      'Create the InProc Speech Grammar.
      oMyGrammar = oRecoContext.CreateGrammar(1)
    
      'Disable Grammar while loading it.
      oMyGrammar.State = %SGSDisabled
    
      'Load the default Dictation Grammar.
      vInput = ""
      oMyGrammar.DictationLoad(vInput, %SLOstatic)
      oMyGrammar.DictationSetState(%SGDSInactive)
    
      'Load the command and control grammar
      IF FileExists(EXE.PATH$ + $CandCGrammar) THEN      '"jarvis.xml"
          oMyGrammar.CmdLoadFromFile(EXE.PATH$ + $CandCGrammar, %SLODynamic) 'SLOStatic)
          'dim gData As Variant
          'gData = gsSOLXML
          'oMyGrammar.CmdLoadFromMemory gData, %SLODynamic         'Load from memory
    
          oMyGrammar.CmdSetRuleIdState(0, %SGDSInactive)
          giCCGrammarLoaded = 1
          gsBannerDictation = "*"
      ELSE
          'CALL FLASH_NOTE_FOR_SECONDS("Unable to load C&C grammar.", 1.5)
          '? "Unable to load C&C grammar."
          giCCGrammarLoaded = 0
          gsBannerDictation = ">"
      END IF
    
      CALL RECOGNITION_ENABLE
    
      'Enable Grammar after loading it.
      oMyGrammar.State = %SGSEnabled
    
      CALL RECOGNITION_DISABLE
    
      'Create the Audio Token Category.
      oCategory = NEWCOM "SAPI.SpObjectTokenCategory"
    
      'Set the Audio Token category ID.
      oCategory.SetId("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\AudioInput")
    
      'Create the Audio Token.
      oToken = NEWCOM "SAPI.SpObjectToken"
    
      'Set the Token Category ID.
      oToken.SetId(oCategory.Default())
    
      'Give the Recognizer the Token.
      oRecognizer.PutRef_AudioInput() = oToken
    END SUB
    
    '_________________________________________________________________
    '
    ' 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 drop 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
    
    '_________________________________________________________________
    '
    ' SUB RECOGNITION_DISABLE
    '_________________________________________________________________
    
    SUB RECOGNITION_DISABLE
      'Stop the recognition by deactivating both of the grammars
      IF ISTRUE ISOBJECT(oRecoContext) THEN
          CALL PutTildeOnMarque
          oMyGrammar.DictationSetState(%SGDSInactive)         'Turn Dictation off.
    
          IF giCCGrammarLoaded = 1 THEN
              oMyGrammar.CmdSetRuleIdState(0, %SGDSInactive)  'turn command / contol off
          END IF
      ELSE
          CALL PutEqualSignOnMarque
      END IF
    END SUB
    
    '_________________________________________________________________
    '
    ' SUB RECOGNITION_OFF                                 VISTA PLUS
    '_________________________________________________________________
    
    SUB RECOGNITION_OFF
      CALL RECOGNITION_DISABLE
    
      RecognitionStatus = 0
    END SUB
    
    
    '_________________________________________________________________
    '
    ' SUB PROCESS_RECOGNITION
    '_________________________________________________________________
    
    SUB PROCESS_RECOGNITION(TheWordPhrase AS STRING)
      LOCAL lsMyData    AS STRING
      LOCAL lsMessage   AS STRING
    
      IF giProcessingRecog = 1 THEN CALL ResetRecognition: EXIT SUB
      giProcessingRecog = 1
    
      lsMyData = LCASE$(TRIM$(TheWordPhrase))
      'IF lsMyData = "recognition has started" then ? "Recognition has started."   'used to validate recognition
    
      TheWordPhrase = ""
      CALL ResetRecognition
    
    END SUB
    
    '_________________________________________________________________
    '
    ' SUB ResetRecognition                                      Jarvis
    '_________________________________________________________________
    
    SUB ResetRecognition
    
      GetEvents(80)
    
      IF RecognitionStatus = 1 THEN
          'Pause Recognition
          oRecoContext.Pause()
    
          GetEvents(8)
    
          'Resume Recognition
          oRecoContext.Resume()
      END IF
    
    END SUB
    
    SUB F698  'REFRESH_MARQUEE_BANNER
      PostMessageA hDlg, %WM_COMMAND, %ID_RefreshBanner, 0
    END SUB
    
    '_________________________________________________________________
    '
    '   SUB  PutTildeOnMarque   the recognition context is available but not activated
    '_________________________________________________________________
    
    SUB PutTildeOnMarque
      gsBannerListen = "~"
      CALL F698
    END SUB
    
    '_________________________________________________________________
    '
    '   SUB  PutColonOnMarque   means a recognition has happened and is being processed   are you ok
    '_________________________________________________________________
    
    SUB PutColonOnMarque
      IF RecognitionStatus = 1 THEN
          IF giMicroMute = 0 THEN
              gsBannerListen = ":"
              CALL F698
          END IF
      END IF
    END SUB
    
    '_________________________________________________________________
    '
    '   SUB  PutQuestionMarkOnMarque   the recognition context is available and active
    '_________________________________________________________________
    
    SUB PutQuestionMarkOnMarque
    
      IF RecognitionStatus = 1 THEN
          IF giMicroMute = 0 THEN
              'microphone is not muted
              gsBannerListen = "?"
              CALL F698
          END IF
      END IF
    END SUB
    
    '_________________________________________________________________
    '
    '   SUB  PutEqualSignOnMarque      no recognition context available
    '_________________________________________________________________
    
    SUB PutEqualSignOnMarque
      IF RecognitionStatus = 0 THEN
          gsBannerListen = "="
          CALL F698
      END IF
    END SUB
    
    
    '_________________________________________________________________
    '
    ' FileExists - make sure a file or folder exists
    '_________________________________________________________________
    
    FUNCTION FileExists(sFileName AS ASCIIZ) AS LONG
        LOCAL sFN AS STRING
        sFN = REMOVE$(sFileName, $CRLF)
        FUNCTION = PathFileExistsA(BYVAL STRPTR(sFN)) 'Return non-zero if file or folder exist.
    
    END FUNCTION
    
    '_________________________________________________________________
    '
    '   SUB  EnumUILanguagesCB
    '_________________________________________________________________
    
    FUNCTION EnumUILanguagesCB(lpUILanguageString AS ASCIIZ, lParam AS LONG) AS LONG
     LOCAL zLocale AS ASCIIZ * %MAX_PATH
    
     GetLocaleInfoA (VAL("&h" & lpUILanguageString), %LOCALE_SENGLANGUAGE, zLocale, %MAX_PATH)
     sBuffer = zLocale & $SPC & lpUILanguageString & $CRLF
    
     FUNCTION = %TRUE 'To continue enumeration, the callback function should return TRUE.
    
    END FUNCTION
    Sorry, no image...
    This site says:
    Upload failed due to your usergroups's upload quota. This file will
    require 20.3 KB but you only have 13.8 KB of 30.00 MB
    remaining.

    Comment


    • #3
      Click image for larger version  Name:	SpkMikVol.png Views:	58 Size:	17.8 KB ID:	793757
      View of Jims's work v2.
      Related...
      Last edited by Pierre Bellisle; 17 May 2020, 10:27 AM.

      Comment


      • #4
        Merci Pierre!

        Comment


        • #5
          Changes:

          Code:
              'SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, 10)
              SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPAGESIZE,0, 10)
          Was changed to this

          Code:
              'SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, 1)
              SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPAGESIZE,0, 1)
          So that the microphone boost trackbar moves correctly when clicked.

          Also added cleanup for callback methods which should be used during app shutdown
          so that the OS mixer control doesn't experience a memory leak:


          Code:
          '_________________________________________________________________
          '
          '   SUB  UnRegisterSpeakerCallback
          '_________________________________________________________________
          
          SUB UnRegisterSpeakerCallback
            LOCAL xhr AS LONG
            xhr = gpIAudioEndpointVolumeStereoSpeakers.UnRegisterControlChangeNotify( _
                     BYVAL ppNotify_1) 'IAudioEndpointVolumeCallback
          END SUB
          
          '_________________________________________________________________
          '
          '   SUB  UnRegisterMicrophoneCallback
          '_________________________________________________________________
          
          SUB UnRegisterMicrophoneCallback
            LOCAL xhr AS LONG
            xhr = gpIAudioEndpointVolumeMicrophone.UnRegisterControlChangeNotify( _
                     BYVAL ppNotify_2) 'IAudioEndpointVolumeCallback
          END SUB
          
          '_________________________________________________________________
          '
          '   SUB  UnRegisterMicrophoneBoostCallback
          '_________________________________________________________________
          
          SUB UnRegisterMicrophoneBoostCallback
            LOCAL xhr AS LONG
            xhr = pPartNext.UnRegisterControlChangeCallback( _
                     BYVAL ppNotify_3) 'IControlChangeNotify
          END SUB
          Demonstrated use:

          Code:
                  CASE %WM_DESTROY         'window is being destroyed
                                           'after windows is removed from screen (children still exist)
                      KillTimer (hDlg, %IDC_Timer0)
                      CALL UnRegisterSpeakerCallback
                      CALL UnRegisterMicrophoneCallback
                      CALL UnRegisterMicrophoneBoostCallback
                      pSelector                            = NOTHING
                      gpIMMDeviceEnumMicrophone            = NOTHING
                      gpIMMDeviceMicrophone                = NOTHING
                      gpIAudioEndpointVolumeMicrophone     = NOTHING
                      gpMeterInfo                          = NOTHING
                      pDeviceTopology                      = NOTHING
                      pConnFrom                            = NOTHING
                      pConnTo                              = NOTHING
                      pPartPrev                            = NOTHING
                      pPartNext                            = NOTHING
                      ppParts                              = NOTHING
                      pIaudioVolumeLevel                   = NOTHING
          
          
                      PostQuitMessage 0
                      'If an application processes this message, it should return zero.
                      FUNCTION = 0
                      EXIT FUNCTION

          Comment


          • #6
            ExitHere: should me modified to this...

            Code:
            ExitHere:
                CoTaskMemFree(pName)
                ppParts   = NOTHING
                pConnTo   = NOTHING
                pPartPrev = NOTHING
                pSelector = NOTHING
                FUNCTION = hr
            pPartNext must be preserved for later use in UnRegisterMicrophoneBoostCallback

            Another issue:
            The higher the microphone boost is set the more likely the program will not close correctly. This is because the speech recognition routine goes into saturation thinking the amplified background noise is something it should recognize so it expends more and more system resources to do just that.

            The work around for this problem is this...
            Drop the microphone boost level to 10 dB and
            Stop the recognition.
            Code:
                    CASE %WM_CLOSE
                        CALL SetVolumeMicrophoneBoost(1 * 10) '10 dB
                        giStartStopRecognition = 2
                        giProgStat = 0
            Obviously when you start the program the next time the level will be 10 dB.
            Last edited by Jim Fritts; 16 May 2020, 05:20 PM.

            Comment


            • #7
              Another update...
              PeakMeterThread new version
              Now the timer can be deleted.

              Code:
              '_________________________________________________________________
              '
              '   THREAD FUNCTION  PeakMeterThread
              '                    full time
              '_________________________________________________________________
              
              THREAD FUNCTION PeakMeterThread(BYVAL yyy AS LONG) AS LONG
              
                  DO WHILE GlobalVariableTestOn
              
                      '// Update the peak meter in the dialog box.
                      IF ISOBJECT(gpMeterInfo) THEN
                          gpMeterInfo.GetPeakValue(PEAK)
                          CONTROL SET TEXT hDlg, %IDC_PEAK_METER, USING$("##.#", PEAK * 100)
                          CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETPOS, (PEAK * 100), 0
                      END IF
              
                      SLEEP 10
                      IF GlobalVariableTest86 = 1 THEN GOTO LoopEnd
                      SLEEP 20
                  LOOP
                  LoopEnd:
              END FUNCTION

              Comment


              • #8
                Version 3 with Jarvis C&CG speech recognition enabled.
                Code:
                'Volume and mute control for Vista, Seven, Eight, and Ten.
                'Callback functions will syncronize speaker volume and mute
                'microphone volume and mute and microphone boost with other mixer controls.
                'Based on work done by Pierre Bellisle.
                'Works with PBWIN10.4 and J.Roca includes WINAPI_III_107.
                'Released to the public on 2020 MAY 12.
                
                'Version 3
                'Speech Recognition implemented
                'Note:
                'Once the microphone is muted there is no recognition.
                
                #COMPILE EXE "VolumeWithCallbackVistaPlus.exe"
                #DIM ALL
                #REGISTER NONE
                
                $CandCGrammar     = "JARVIS.xml"
                
                'unused functions
                
                '--------------------------------------------------------------------------------
                '   ** Globals **
                '--------------------------------------------------------------------------------
                
                GLOBAL g_guid_1_Context                     AS GUID  'speakers volume/mute-unmute context
                GLOBAL g_guid_2_Context                     AS GUID  'microphone volume/mute-unmute context
                GLOBAL g_guid_3_Context                     AS GUID  'microphone boost volume context (currently not used)
                
                'SAPI
                GLOBAL InProcEvents                         AS ISpeechRecoContextEventsImplemented   'SAPI interface
                GLOBAL giRunningRecogProcess                AS LONG  'SAPI
                GLOBAL RecognitionStatus                    AS LONG  'SAPI
                GLOBAL giStartStopRecognition               AS LONG  'SAPI
                GLOBAL oRecoContext                         AS ISpeechRecoContext           'SAPI
                GLOBAL oRecognizer                          AS ISpeechRecognizer            'SAPI
                GLOBAL oMyGrammar                           AS ISpeechRecoGrammar           'SAPI
                GLOBAL oCategory                            AS ISpeechObjectTokenCategory   'SAPI
                GLOBAL oToken                               AS ISpeechObjectToken           'SAPI
                GLOBAL giCCGrammarLoaded                    AS LONG    'SAPI
                GLOBAL gsBannerDictation                    AS STRING  'SAPI
                GLOBAL gsBannerListen                       AS STRING  'SAPI
                GLOBAL giProcessingRecog                    AS LONG    'SAPI
                GLOBAL giRecogNotUnderstood                 AS LONG    'SAPI
                GLOBAL MyDataX                              AS STRING  'SAPI contains the recognized phrase
                'END SAPI
                
                GLOBAL hDlg                                 AS DWORD
                GLOBAL giProgStat                           AS LONG
                GLOBAL VistaPlus                            AS LONG 'Vista to 10
                GLOBAL sBuffer                              AS STRING
                
                GLOBAL gsMasterVolumeIsChangeable           AS STRING  '"YES" OR "NO"
                
                GLOBAL gpIMMDeviceEnumSpeakers              AS IMMDeviceEnumerator
                GLOBAL gpIMMDeviceSpeakers                  AS IMMDevice
                GLOBAL gpIAudioEndpointVolumeSpeakers       AS IAudioEndpointVolume
                GLOBAL giSpeakers                           AS LONG   ' default stereo speaker availablility  yes = 1, no = 0
                GLOBAL giSpeakersHr                         AS LONG
                GLOBAL giMasterSpeakersLevel                AS LONG
                GLOBAL gsTheSpeakersName                    AS STRING
                GLOBAL giMasterSpeakersMuteLevel            AS LONG
                
                GLOBAL gpIMMDeviceEnumMicrophone            AS IMMDeviceEnumerator
                GLOBAL gpIMMDeviceMicrophone                AS IMMDevice
                GLOBAL gpIAudioEndpointVolumeMicrophone     AS IAudioEndpointVolume
                GLOBAL giMicrophone                         AS LONG   ' default microphone availablility  yes = 1, no = 0
                GLOBAL giMicrophoneHr                       AS LONG
                GLOBAL giMasterMicrophoneLevel              AS LONG
                GLOBAL gsTheMicrophoneName                  AS STRING
                GLOBAL giMasterMicrophoneMuteLevel          AS LONG
                
                GLOBAL giMasterMicroBoostLevel              AS LONG   'value 0, 10, 20, 30
                
                GLOBAL gpIMMNotificationClientMicrophone    AS IMMNotificationClient
                
                GLOBAL pDeviceTopology                      AS IDeviceTopology
                GLOBAL pConnFrom                            AS IConnector
                GLOBAL pConnTo                              AS IConnector
                GLOBAL pPartPrev                            AS IPart
                GLOBAL pPartNext                            AS IPart
                GLOBAL ppParts                              AS IPartsList
                GLOBAL pIaudioVolumeLevel                   AS IAudioVolumeLevel
                GLOBAL pSelector                            AS IAudioInputSelector
                
                GLOBAL ppNotify_1                           AS IAudioEndpointVolumeCallback    'speakers
                GLOBAL ppNotify_2                           AS IAudioEndpointVolumeCallback    'microphone
                GLOBAL ppNotify_3                           AS IControlChangeNotify            'microphone boost
                
                GLOBAL PEAK                                 AS SINGLE     'for peak meter
                GLOBAL gpMeterInfo                          AS IAudioMeterInformation
                
                GLOBAL pDevId                               AS WSTRINGZ PTR
                GLOBAL devName                              AS WSTRING
                GLOBAL pName                                AS WSTRINGZ PTR  'LPWSTR pName
                
                GLOBAL giMicroMute                          AS LONG 'Tracks Microphone Mute status
                GLOBAL pflow                                AS LONG 'DataFlow pflow
                GLOBAL pPartType                            AS LONG 'PartType
                GLOBAL bConnected                           AS LONG 'BOOL bConnected
                GLOBAL connType                             AS LONG 'ConnectorType
                GLOBAL ComponentName                        AS WSTRINGZ * 255
                
                GLOBAL XFill                                AS STRING  'used to post text to clipboard
                
                GLOBAL giPRTDMessage                        AS LONG
                GLOBAL giPumpingMessages                    AS LONG
                
                GLOBAL CLSID_MMDeviceEnumerator             AS GUID
                GLOBAL IID_IMMDeviceEnumerator              AS GUID
                GLOBAL IID_IPart                            AS GUID
                GLOBAL IID_IAudioVolumeLevel                AS GUID
                GLOBAL IID_IConnector                       AS GUID
                GLOBAL IID_IAudioInputSelector              AS GUID
                GLOBAL IID_IAudioEndpointVolume             AS GUID
                GLOBAL IID_IDeviceTopology                  AS GUID
                GLOBAL IID_IAudioMeterInformation           AS GUID
                GLOBAL IID_IAudioEndpointVolumeCallback     AS GUID
                GLOBAL IID_IControlChangeNotify             AS GUID
                
                GLOBAL LastValue                            AS SINGLE
                
                $AppName   = "Audio with Callback"
                
                '--------------------------------------------------------------------------------
                '   ** Constants **
                '--------------------------------------------------------------------------------
                
                ENUM Equates SINGULAR
                  ID_RefreshBanner       = %WM_USER + 5001  'SAPI
                  LabelMinSpeakers                 'MIN
                  LabelMaxSpeakers                 'MAX
                  LabelVolSpeakers
                  LabelInfoSpeakers
                  LabelOnOffSpeakers
                  TrackbarVolSpeakers
                  CheckboxVolOnOffSpeakers
                  ButtonSndVolExeSpeakers
                
                  LabelMinMicro                   'MIN
                  LabelMaxMicro                   'MAX
                  LabelVolMicro
                  LabelInfoMicro
                  LabelOnOffMicro
                  TrackbarVolMicro
                  CheckboxVolOnOffMicro
                  ButtonSndVolExeMicro
                
                  LabelMinMicroBoost
                  LabelMaxMicroBoost
                  LabelVolMicroBoost
                  LabelInfoMicroBoost
                  'LabelOnOffMicroBoost           'not used
                  TrackbarVolMicroBoost
                  'CheckboxVolOnOffMicroBoost     'not used
                  'ButtonSndVolExeMicroBoost      'not used
                
                  ID_VOLUME
                  IDC_PEAK_METER
                
                  SpeakersUp
                  SpeakersDn
                  MicrophoneUp
                  MicrophoneDn
                  MicrophoneBoostUp
                  MicrophoneBoostDn
                  SpeakersMuteUnmute
                  MicrophoneMuteUnmute
                
                END ENUM
                
                %TrackbarMax                  = 100   'for speaker and microphone trackbar
                %TrackbarMaxBoost             = 3     'for microphone boost trackbar
                
                '--------------------------------------------------------------------------------
                '   ** Includes **
                '--------------------------------------------------------------------------------
                '#INCLUDE ONCE "MB_API_A.inc"        'WHEN USED {6,996 total lines} COMPILE TIME 0.1 seconds
                'OR THESE
                #INCLUDE ONCE "Win32Api.inc"       'WHEN USED {289,783 total lines} COMPILE TIME 1.9 seconds
                #INCLUDE ONCE "CommCtrl.inc"       'Needed if manifest and WinXP
                #INCLUDE ONCE "mmdeviceapi.inc"    'used for WASAPI
                #INCLUDE ONCE "devpkey.inc"        'used for WASAPI
                #INCLUDE ONCE "propvarutil.inc"    'used for WASAPI
                #INCLUDE ONCE "endpointvolume.inc" 'used for WASAPI
                #INCLUDE ONCE "propsys.inc"        'used for WASAPI
                #INCLUDE ONCE "sapi.inc"           'used for SAPI
                #INCLUDE ONCE "WinNls.inc"         'User language
                
                ' ########################################################################################
                ' Class CISpeechRecoContextEvents                          SAPI
                ' Interface name = _ISpeechRecoContextEvents
                ' IID = {7B8FCB42-0E9D-4F00-A048-7B04D6179D3D}
                ' Attributes = 4096 [&H1000] [Dispatchable]
                ' ########################################################################################
                
                CLASS CISpeechRecoContextEventsImplemented GUID$("{5B344ADB-C0C7-4B5F-8046-7D2DB91A1D75}") AS EVENT
                
                INTERFACE ISpeechRecoContextEventsImplemented GUID$("{7B8FCB42-0E9D-4F00-A048-7B04D6179D3D}") AS EVENT
                
                  INHERIT IDISPATCH
                
                   ' =====================================================================================
                   METHOD Recognition <7> ( _
                     BYVAL StreamNumber AS LONG _                       ' __in long StreamNumber
                   , BYVAL StreamPosition AS VARIANT _                  ' __in VARIANT StreamPosition
                   , BYVAL RecognitionType AS LONG _                    ' __in SpeechRecognitionType RecognitionType
                   , BYVAL Result AS ISpeechRecoResult _                ' __in ISpeechRecoResult *Result
                   )                                                    ' void
                
                      LOCAL pDisp AS IDISPATCH
                      LOCAL bstrText AS WSTRING
                      IF ISNOTHING(Result) THEN EXIT METHOD
                      pDisp = Result
                      OBJECT CALL pDisp.PhraseInfo.GetText TO bstrText
                      IF OBJRESULT THEN
                         ? "GetText error: " & OBJRESULT$
                      ELSE
                          giRecogNotUnderstood = 0
                          MyDataX = TRIM$(bstrText)
                          '? "]" & MyDataX & "["
                      END IF
                
                   END METHOD
                   ' =====================================================================================
                   ' =====================================================================================
                   METHOD FalseRecognition <11> ( _
                     BYVAL StreamNumber AS LONG _                       ' __in long StreamNumber
                   , BYVAL StreamPosition AS VARIANT _                  ' __in VARIANT StreamPosition
                   , BYVAL Result AS ISpeechRecoResult _                ' __in ISpeechRecoResult *Result
                   )                                                    ' void
                
                      LOCAL pDisp AS IDISPATCH
                      LOCAL bstrText AS WSTRING
                      IF ISNOTHING(Result) THEN
                          EXIT METHOD
                      END IF
                      pDisp = Result
                      OBJECT CALL pDisp.PhraseInfo.GetText TO bstrText
                      IF OBJRESULT THEN
                         ? "GetText error: " & OBJRESULT$
                      ELSE
                          giRecogNotUnderstood = 1
                          MyDataX = "What was that?"
                          '? "]" & MyDataX & "["
                      END IF
                   END METHOD
                
                END INTERFACE
                
                END CLASS
                
                
                ' ########################################################################################
                ' 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
                             'Post notification to main dialog
                             SendMessageA (hDlg, %WM_APP, %CheckboxVolOnOffSpeakers, pNotify_1.bMuted)
                             SendMessageA (hDlg, %WM_APP, %TrackbarVolSpeakers, 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
                             SendMessageA (hDlg, %WM_APP, %CheckboxVolOnOffMicro, pNotify_2.bMuted)
                             SendMessageA (hDlg, %WM_APP, %TrackbarVolMicro, pNotify_2.fMasterVolume * %TrackbarMax)
                           END IF
                       END IF
                
                       METHOD = %S_OK
                     END IF
                   END METHOD
                   ' =====================================================================================
                
                 END INTERFACE
                
                END CLASS
                
                
                ' ########################################################################################
                ' 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
                
                '_____________________________________________________________________________
                '
                SUB IconSet_1(iconOn AS LONG)
                  DIM   hIcon(0 TO 3) AS STATIC DWORD
                  LOCAL Looper        AS LONG
                
                  SELECT CASE iconOn
                
                      CASE 0, 1 'Set "disable icon" or "enable icon" on dialog ans static
                          SetClassLongA (hDlg, %GCL_HICONSM, hIcon(iconOn))
                          SendMessageA (hDlg, %WM_SETICON, %ICON_SMALL, hIcon(iconOn))
                          SendDlgItemMessageA (hDlg, %LabelOnOffSpeakers, %STM_SETIMAGE, %IMAGE_ICON, hIcon(iconOn + 02))
                
                      CASE -2 'Get "disable icon" and "enable icon" from appropriate files
                          IF VistaPlus THEN 'Vista to 10
                              ExtractIconExA ("SndVol.exe", 1, BYVAL VARPTR(hIcon(3)), BYVAL VARPTR(hIcon(1)), 1) 'Vista to 10, Enable-Big-Small
                              ExtractIconExA ("SndVol.exe", 2, BYVAL VARPTR(hIcon(2)), BYVAL VARPTR(hIcon(0)), 1) 'Vista to 10, Disable-Big-Small
                          ELSE '95 to XP
                              ? "Sorry, this app does not support this OS."
                          END IF
                
                      CASE -1 'Release memory clean-up
                          FOR Looper = 0 TO 3
                              DestroyIcon (hIcon(Looper))
                          NEXT
                
                  END SELECT
                
                END SUB
                
                ' ========================================================================================
                ' Helper function to retrieve the friendly name of the audio device.
                ' ========================================================================================
                FUNCTION AfxGetDeviceName (BYVAL pDev AS IMMDevice) AS WSTRING
                
                    LOCAL hr AS LONG
                
                    LOCAL pPropStore AS IPropertyStore
                    hr = pDev.OpenPropertyStore(%STGM_READ, pPropStore)
                    IF FAILED(hr) THEN EXIT FUNCTION
                    LOCAL friendlyName AS PROPVARIANT
                    PropVariantInit(friendlyName)
                    hr = pPropStore.getValue(DEVPKEY_Device_FriendlyName, friendlyName)
                    IF FAILED(hr) THEN EXIT FUNCTION
                    LOCAL wszDevName AS WSTRINGZ * 128
                    PropVariantToString(friendlyName, wszDevName, 128)
                    FUNCTION = wszDevName
                    PropVariantClear(friendlyName)
                
                END FUNCTION
                
                
                '_________________________________________________________________
                '
                '   SUB  UnRegisterSpeakerCallback
                '_________________________________________________________________
                
                SUB UnRegisterSpeakerCallback
                  LOCAL xhr AS LONG
                  xhr = gpIAudioEndpointVolumeSpeakers.UnRegisterControlChangeNotify( _
                           BYVAL ppNotify_1) 'IAudioEndpointVolumeCallback
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  UnRegisterMicrophoneCallback
                '_________________________________________________________________
                
                SUB UnRegisterMicrophoneCallback
                  LOCAL xhr AS LONG
                  xhr = gpIAudioEndpointVolumeMicrophone.UnRegisterControlChangeNotify( _
                           BYVAL ppNotify_2) 'IAudioEndpointVolumeCallback
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  UnRegisterMicrophoneBoostCallback
                '_________________________________________________________________
                
                SUB UnRegisterMicrophoneBoostCallback
                  LOCAL xhr AS LONG
                  xhr = pPartNext.UnRegisterControlChangeCallback( _
                           BYVAL ppNotify_3) 'IControlChangeNotify
                END SUB
                
                
                '_________________________________________________________________
                '
                '   FUNCTION  SetSpeakersCallback
                '_________________________________________________________________
                
                FUNCTION SetSpeakersCallback 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(gpIAudioEndpointVolumeSpeakers) THEN
                
                        xhr = CoCreateGuid (g_guid_1_Context)
                
                        LET ppNotify_1 = CLASS "CIAudioEndpointVolumeCallback_1"
                
                        xhr = gpIAudioEndpointVolumeSpeakers.RegisterControlChangeNotify(BYVAL ppNotify_1) 'IAudioEndpointVolumeCallback
                
                        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
                
                '_________________________________________________________________
                '
                '   FUNCTION  SetMicrophoneCallback
                '_________________________________________________________________
                
                FUNCTION SetMicrophoneCallback 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(gpIAudioEndpointVolumeMicrophone) THEN
                
                        xhr = CoCreateGuid (g_guid_2_Context)
                
                        LET ppNotify_2 = CLASS "CIAudioEndpointVolumeCallback_2"
                
                        xhr = gpIAudioEndpointVolumeMicrophone.RegisterControlChangeNotify(BYVAL ppNotify_2) 'IAudioEndpointVolumeCallback
                
                        IF FAILED(xhr) THEN
                            ? "Unable to set microphone callback."
                            FUNCTION = 0
                            EXIT FUNCTION
                        ELSE
                            '? "Default microphone callback."
                        END IF
                
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  SetMicrophoneBoostCallback
                '_________________________________________________________________
                
                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
                
                
                
                
                ' ========================================================================================
                
                FUNCTION GetMuteSpeakers AS LONG
                
                    LOCAL xhr   AS LONG
                
                    LOCAL fMute AS LONG
                
                    IF ISOBJECT(gpIAudioEndpointVolumeSpeakers) THEN
                
                        xhr = gpIAudioEndpointVolumeSpeakers.GetMute(fMute)
                        IF FAILED(xhr) THEN
                            ? "Unable to get stereo speakers mute level."
                            FUNCTION = 0
                            EXIT FUNCTION
                        ELSE
                            '? "Default stereo speakers mute level is " + STR$(fMute)
                        END IF
                
                    END IF
                
                    giMasterSpeakersMuteLevel = fMute
                    SELECT CASE fMute
                        CASE 1
                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_CHECKED, 0)
                            IconSet_1(0)
                
                        CASE ELSE
                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_UNCHECKED, 0)
                            IconSet_1(1)
                    END SELECT
                    FUNCTION = 1
                END FUNCTION
                
                ' ========================================================================================
                
                FUNCTION MuteSpeakers AS LONG
                
                    LOCAL xhr   AS LONG
                
                    IF gsMasterVolumeIsChangeable = "YES" THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeSpeakers) THEN
                
                            xhr = gpIAudioEndpointVolumeSpeakers.SetMute(1, g_guid_1_Context) 'BYVAL %NULL)  '%TRUE  BYREF pguidEventContext AS GUID
                            IF FAILED(xhr) THEN
                                ? "Unable to mute default stereo speakers."
                            ELSE
                                '? "Default Speakers muted successfully!"
                            END IF
                        END IF
                
                        giMasterSpeakersMuteLevel = 1
                        SELECT CASE giMasterSpeakersMuteLevel
                            CASE 1
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_CHECKED, 0)
                                IconSet_1(0)
                
                            CASE ELSE
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                IconSet_1(1)
                        END SELECT
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                ' ========================================================================================
                
                FUNCTION UnMuteSpeakers AS LONG
                
                    LOCAL xhr   AS LONG
                
                    IF gsMasterVolumeIsChangeable = "YES" THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeSpeakers) THEN
                
                            xhr = gpIAudioEndpointVolumeSpeakers.SetMute(0, g_guid_1_Context) 'BYVAL %NULL)  '%FALSE  BYREF pguidEventContext AS GUID
                            IF FAILED(xhr) THEN
                                ? "Unable to unmute default stereo speakers."
                            ELSE
                                '? "Default Speakers unmuted successfully!"
                            END IF
                
                        END IF
                
                        giMasterSpeakersMuteLevel = 0
                        SELECT CASE giMasterSpeakersMuteLevel
                            CASE 1
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_CHECKED, 0)
                                IconSet_1(0)
                
                            CASE ELSE
                                PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                IconSet_1(1)
                        END SELECT
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                ' ========================================================================================
                
                ' ========================================================================================
                
                '_________________________________________________________________
                '
                '   FUNCTION  SpeakersCheck
                '_________________________________________________________________
                
                FUNCTION SpeakersCheck AS LONG
                    LOCAL xhr     AS LONG
                    LOCAL pDevId  AS WSTRINGZ PTR
                    LOCAL devName AS WSTRING
                
                    gpIMMDeviceEnumSpeakers = NEWCOM CLSID CLSID_MMDeviceEnumerator
                    IF ISNOTHING(gpIMMDeviceEnumSpeakers) THEN EXIT FUNCTION
                
                    ' // Enumerate the audio endpoints of interest (in this case audio capture endpoints)
                    ' // The flags that can be used are %EDataFlow_eRender (for audio output endpoints),
                    ' // %EDataFlow_eCapture (for audio capture endpoints) or %EDataFlow_eAll (for all audio endpoints)
                    ' // %EDataFlow_eCapture (for audio capture endpoints),
                    ' // %EDataFlow_eAll (for all audio endpoints)
                
                    ' // %ERole_eConsole = Games, system notification sounds, and voice commands.
                    ' // %ERole_eMultimedia = Music, movies, narration, and live music recording.
                    ' // %ERole_eCommunications = Voice communications (talking to another person).
                
                    giSpeakersHr = gpIMMDeviceEnumSpeakers.GetDefaultAudioEndpoint( _
                                               %EDataFlow_eRender,   _
                                               %ERole_eMultimedia,   _
                                               gpIMMDeviceSpeakers)  'IMMDevice
                    IF FAILED(giSpeakersHr) THEN
                        'CALL SHOW_NOTES("Unable to find default speakers.")
                        ? "Unable to find the default Stereo Speakers"
                        FUNCTION = 0
                        EXIT FUNCTION
                    END IF
                
                    xhr = gpIMMDeviceSpeakers.Activate( _
                                     IID_IAudioEndpointVolume, _
                                     %CLSCTX_INPROC_SERVER,    _
                                     BYVAL %NULL,              _
                                     gpIAudioEndpointVolumeSpeakers)   'IAudioEndpointVolume
                    IF FAILED(xhr) THEN
                        'CALL SHOW_NOTES("Unable to activate default speakers.")
                        ? "Unable to activate default Stereo Speakers"
                        FUNCTION = 0
                        EXIT FUNCTION
                    ELSE
                        ' "Default Stereo Speakers activated successfully!"
                
                        ' // Get the device identifier
                        xhr = gpIMMDeviceSpeakers.GetId(pDevId)
                        IF pDevId THEN
                            ' "Device id = " & @pDevId
                            CoTaskMemFree(pDevId)
                        END IF
                        ' // Get the device name
                        gsTheSpeakersName =  AfxGetDeviceName (gpIMMDeviceSpeakers)  'IMMDevice
                
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  MicrophoneCheck         new 2020 MAY 06 new
                '_________________________________________________________________
                
                FUNCTION MicrophoneCheck AS LONG
                    LOCAL xhr         AS LONG
                    LOCAL fMinDb      AS SINGLE 'float fMinDb
                    LOCAL fMaxDb      AS SINGLE 'float fMaxDb
                    LOCAL fStepDb     AS SINGLE 'float fStepDb
                    LOCAL pfCurrentDb AS SINGLE 'float pfCurrentDb
                
                    ' Get enumerator for audio endpoint devices.
                    gpIMMDeviceEnumMicrophone = NEWCOM CLSID CLSID_MMDeviceEnumerator
                    IF ISNOTHING(gpIMMDeviceEnumMicrophone) THEN
                        ? "Unable to create audio device enumerator."
                        FUNCTION = 0
                        EXIT FUNCTION
                    END IF
                
                    ' // Enumerate the audio endpoints of interest (in this case audio capture endpoints)
                    ' // The flags that can be used are:
                    ' // %EDataFlow_eRender (for audio output endpoints),
                    ' // %EDataFlow_eCapture (for audio capture endpoints),
                    ' // %EDataFlow_eAll (for all audio endpoints)
                
                    ' // %ERole_eConsole = Games, system notification sounds, and voice commands.
                    ' // %ERole_eMultimedia = Music, movies, narration, and live music recording.
                    ' // %ERole_eCommunications = Voice communications (talking to another person).
                                                                                                            '%DEVICE_STATE_ACTIVE
                    giMicrophoneHr = gpIMMDeviceEnumMicrophone.GetDefaultAudioEndpoint(%EDataFlow_eCapture, %ERole_eConsole, gpIMMDeviceMicrophone)
                    IF FAILED(giMicrophoneHr) THEN
                        ? "Unable to find default microphone device."
                        FUNCTION = 0
                        EXIT FUNCTION
                    END IF
                
                    xhr = gpIMMDeviceMicrophone.Activate( _
                                    IID_IAudioEndpointVolume, _
                                    %CLSCTX_INPROC_SERVER,    _
                                    BYVAL %NULL,             _
                                    gpIAudioEndpointVolumeMicrophone)
                    IF FAILED(xhr) THEN
                        'CALL SHOW_NOTES_NO_WAIT("Unable to activate a microphone.")
                        ? "Unable to activate default microphone"
                        FUNCTION = 0
                        EXIT FUNCTION
                    ELSE
                        ' "Default microphone activated successfully!"
                
                        ' // Get the device identifier
                        xhr = gpIMMDeviceMicrophone.GetId(pDevId)
                        IF pDevId THEN
                             XFill = "Device id = " & @pDevId  'Device id = {0.0.1.00000000}.{be4cea6b-aba4-4420-8e08-dbfb7a6be10c}
                             'call COPY_TO_CLIPBOARD
                            CoTaskMemFree(pDevId)
                        END IF
                        ' // Get the device name
                        gsTheMicrophoneName = AfxGetDeviceName(gpIMMDeviceMicrophone)
                        '? gsTheMicrophoneName
                
                        'Get the endpoint device's IDeviceTopology interface.
                        xhr = gpIMMDeviceMicrophone.Activate( _
                                      IID_IDeviceTopology,    _
                                      %CLSCTX_ALL,            _
                                      BYVAL %NULL,            _
                                      pDeviceTopology)
                        IF FAILED(xhr) THEN
                            'CALL SHOW_NOTES_NO_WAIT("Unable to get microphone topology.")
                            ? "Unable to get microphone topology."
                            FUNCTION = 0
                            EXIT FUNCTION
                        END IF
                
                        'The device topology for an endpoint device always
                        'contains just one connector (connector number 0).
                        xhr = pDeviceTopology.GetConnector(0, pConnFrom)
                        IF FAILED(xhr) THEN
                            'CALL SHOW_NOTES_NO_WAIT("Unable to get the connector info.")
                            ? "Unable to get the connector info."
                            FUNCTION = 0
                            EXIT FUNCTION
                        END IF
                        pDeviceTopology = NOTHING
                
                        'Make sure that this is a capture device.
                        xhr = pConnFrom.GetDataFlow(pflow)
                        IF FAILED(xhr) THEN
                            'CALL SHOW_NOTES_NO_WAIT("Unable to get connector flow data.")
                            ? "Unable to get connector flow data."
                            FUNCTION = 0
                            EXIT FUNCTION
                        ELSE
                            'For example, a typical rendering device on an adapter has a connector
                            'with data-flow direction "In" through which the Windows audio engine
                            'streams PCM data into the device. The same device has a connector with
                            'data-flow direction "Out" through which the device transmits an audio
                            'signal to speakers or headphones.
                
                            '%DataFlow_In  = 0  Input stream. The audio stream flows into the device through the connector.
                            '%DataFlow_Out = 1  Output stream. The audio stream flows out of the device through the connector.
                            SELECT CASE pFlow
                                CASE 0  'In
                                    'Error -- this is a rendering device.
                                    'EXIT_ON_ERROR(xhr = %AUDCLNT_E_WRONG_ENDPOINT_TYPE)
                                    'CALL SHOW_NOTES_NO_WAIT("Error! The connector flow data was wrong.")
                                    ? "Error! The connector flow data was wrong."
                                    FUNCTION = 0
                                    EXIT FUNCTION
                
                                CASE 1  'Out
                                    '? "DataFlow is out stream."
                            END SELECT
                        END IF
                
                        '--------------------------------------
                        'Computer\HKEY_CURRENT_USER\Control Panel\International\User Profile\en-US (if exists)0x00000001 (1)
                        'Name 0409:00000409
                        'Type: REG_DWORD
                        'Data: 0x00000001 (1)
                
                        LOCAL dwReserved AS DWORD
                        LOCAL MyVal      AS DWORD
                        LOCAL zLocale    AS ASCIIZ * %MAX_PATH
                
                        IF EnumUILanguagesA (CODEPTR(EnumUILanguagesCB()), dwReserved, MyVal) THEN
                          'If the function succeeds, the return value is TRUE.
                          'let pass
                        ELSE
                          ? "Error determining language to support"
                        END IF
                
                        REPLACE "English 0409" WITH "en-US" IN sBuffer   'windows 10
                        REPLACE "English 0009" WITH "en"    IN sBuffer   'windows 7
                        REPLACE "French 040C"  WITH "fr-FR" IN sBuffer   'windows 10
                        REPLACE "French 000C"  WITH "fr"    IN sBuffer   'windows 7
                
                        IF INSTR(sBuffer, "fr") THEN
                            ComponentName = "Ampli Microphone"     'french
                        ELSE
                            ComponentName = "Microphone Boost"     'english
                        END IF
                
                        '--------------------------------------
                        CALL SelectCaptureDevice
                
                        pConnFrom = NOTHING
                
                        '\\\\\\\\\\\\\\\
                        'Get the endpoint device's IAudioMeterInformation interface.
                        xhr = gpIMMDeviceMicrophone.Activate( _
                                        IID_IAudioMeterInformation, _
                                        %CLSCTX_ALL,                _
                                        BYVAL %NULL,                _
                                        gpMeterInfo)
                        IF FAILED(xhr) THEN
                            'CALL SHOW_NOTES_NO_WAIT("Unable to get peak meter info.")
                            ? "Unable to get peak meter info."
                            FUNCTION = 0
                            EXIT FUNCTION
                        END IF
                
                    END IF
                
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  GetMuteMicrophone
                '_________________________________________________________________
                
                FUNCTION GetMuteMicrophone AS LONG
                
                    LOCAL xhr   AS LONG
                    LOCAL fMute AS LONG
                
                    IF giMicrophone = 1 THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
                
                            xhr = gpIAudioEndpointVolumeMicrophone.GetMute(fMute)
                            IF FAILED(xhr) THEN
                                ? "Unable to get microphone mute level."
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                '? "Default microphone mute level is " + STR$(fMute)
                            END IF
                
                            giMasterMicrophoneMuteLevel = fMute
                            SELECT CASE fMute
                                CASE 1
                                    PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                                    'IconSet_2(0)
                
                                CASE ELSE
                                    PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                    'IconSet_2(1)
                            END SELECT
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  MuteMicrophone
                '_________________________________________________________________
                
                FUNCTION MuteMicrophone AS LONG
                
                    LOCAL xhr   AS LONG
                
                    IF giMicrophone = 1 THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
                
                            xhr = gpIAudioEndpointVolumeMicrophone.SetMute(1, g_guid_2_Context) '%TRUE,  BYREF pguidEventContext AS GUID
                            IF FAILED(xhr) THEN
                                ? "Unable to mute default microphone."
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                '? "Default microphone muted successfully!"
                            END IF
                
                            giMasterMicrophoneMuteLevel = 1
                            SELECT CASE giMasterMicrophoneMuteLevel
                                CASE 1
                                    PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                                    'IconSet_2(0)
                
                                CASE ELSE
                                    PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                    'IconSet_2(1)
                            END SELECT
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  UnMuteMicrophone
                '_________________________________________________________________
                
                FUNCTION UnMuteMicrophone AS LONG
                
                    LOCAL xhr   AS LONG
                
                    IF giMicrophone = 1 THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
                
                            xhr = gpIAudioEndpointVolumeMicrophone.SetMute(0, g_guid_2_Context) '%FALSE,  BYREF pguidEventContext AS GUID
                            IF FAILED(xhr) THEN
                                ? "Unable to unmute default microphone."
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                '? "Default microphone unmuted successfully!"
                            END IF
                
                            giMasterMicrophoneMuteLevel = 0
                            SELECT CASE giMasterMicrophoneMuteLevel
                                CASE 1
                                    PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                                    'IconSet_2(0)
                
                                CASE ELSE
                                    PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                    'IconSet_2(1)
                            END SELECT
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  GlobalVariableTest86                            VER4
                '_________________________________________________________________
                
                FUNCTION GlobalVariableTest86() AS LONG
                    IF giProgStat = 0 THEN
                        FUNCTION = 1
                    END IF
                END FUNCTION
                '_________________________________________________________________
                '
                '   FUNCTION  GlobalVariableTestOn                            VER4
                '_________________________________________________________________
                
                FUNCTION GlobalVariableTestOn() AS LONG
                    IF giProgStat = 1 THEN
                        FUNCTION = 1
                    END IF
                END FUNCTION
                
                '_________________________________________________________________
                '
                ' Processes pending Windows messages.                         VER4
                ' Call this procedure if you are performing a tight
                ' FOR/NEXT or DO/LOOP and need to allow
                ' your application to be responsive to user input.
                ' Modified version of AfxPumpMessages
                '_________________________________________________________________
                
                SUB MyPumpMessages(BYVAL xTimes AS LONG) EXPORT
                  '/////////////////////////////////////////////////////////////
                  'Do not want to remove the WM_QUIT message from message que.
                  IF GlobalVariableTest86 = 1 THEN EXIT SUB
                  '/////////////////////////////////////////////////////////////
                
                  LOCAL Ix AS LONG
                
                  IF giPumpingMessages = 1 THEN EXIT SUB
                  giPumpingMessages = 1
                
                  IF GlobalVariableTestOn THEN
                      IF xTimes <= 0 THEN xTimes = 1
                      FOR Ix = 1 TO xTimes
                          CALL PeekRemoveTranslateDispatchMessage
                          IF GlobalVariableTest86 = 1 THEN GOTO NoMoreNeeded
                      NEXT Ix
                  END IF
                
                  NoMoreNeeded:
                  giPumpingMessages = 0
                END SUB
                
                '_________________________________________________________________
                '
                ' GetEvents dialog procedure (replacement for Sleep 0)
                ' Sleep 0 releases time slices back to the OS but does
                ' not necessarily give your program the slices back.
                '
                '   SUB  GetEvents
                '_________________________________________________________________
                
                SUB GetEvents(SleepTime AS LONG) EXPORT
                
                    IF SleepTime <= 0 THEN SleepTime = 1
                
                    CALL PeekRemoveTranslateDispatchMessage
                
                    SLEEP SleepTime/4
                
                    CALL PeekRemoveTranslateDispatchMessage
                
                    SLEEP SleepTime/4
                
                    CALL PeekRemoveTranslateDispatchMessage
                
                    SLEEP SleepTime/4
                
                    CALL PeekRemoveTranslateDispatchMessage
                
                    SLEEP SleepTime/4
                
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  PeekRemoveTranslateDispatchMessage                   VER4
                '_________________________________________________________________
                
                SUB PeekRemoveTranslateDispatchMessage
                  '/////////////////////////////////////////////////////////////
                  'Do not want to remove the WM_QUIT message from message que.
                  IF GlobalVariableTest86 = 1 THEN EXIT SUB
                  '/////////////////////////////////////////////////////////////
                  'Could use this to signal main message loop to close
                  'PostThreadMessageA (GetCurrentThreadID, %WM_QUIT, 0, 0)   ' Signal Quit to Msg Loop
                
                  IF giPRTDMessage = 1 THEN EXIT SUB
                  giPRTDMessage = 1
                
                  STATIC Msg AS tagMsg
                  IF PeekMessageA(Msg, %NULL, %NULL, %NULL, %PM_REMOVE) THEN
                      TranslateMessage Msg
                      DispatchMessageA Msg
                  END IF
                
                  giPRTDMessage = 0
                END SUB
                
                
                '//-----------------------------------------------------------
                '// This function traverses the data path that extends from the
                '// endpoint device to the system bus (for example, PCI)
                '// or external bus (USB). If the function discovers a MUX
                '// (input selector) in the path, it selects the MUX input
                '// that connects to the stream from the endpoint device.
                '// In this case we are looking for the Microphone Boost
                '// parts.
                '//-----------------------------------------------------------
                
                     'Limits of Device Topology
                     'Where (con) is a connector
                     'microphone -> endpoint device B (con) physical external
                     '
                     '-> Input Multiplexer Device (Topology Filter)
                     '   (con) physical external   IPart & IConnector
                     '   subunit Mute ->           IPart & ISubUnit
                     '   subunit Vol ->            IPart & ISubUnit
                     '   subunit MUX ->            IPart & ISubUnit
                     '   (Con) Software Fixed      IPart & IConnector
                     '
                     '-> Wave Capture Device (Wave Filter)
                     '   (Con) Software Fixed      IPart & IConnector
                     '   subunit ADC               IPart & ISubUnit
                     '   (Con) Software IO         IPart & IConnector
                     '
                     '-> System Bus Wave-in Stream (DMA)
                
                
                FUNCTION SelectCaptureDevice() AS LONG
                    LOCAL hr       AS LONG
                    LOCAL ppwstrGlobalId AS WSTRINGZ PTR
                    LOCAL ppBoostTopology AS IDeviceTopology
                
                    hr = %S_OK
                    pIaudioVolumeLevel = NOTHING
                
                    IF ISOBJECT(pConnFrom) THEN
                        'continue
                    ELSE
                        GOTO ExitHere
                    END IF
                
                    '// Outer loop: Each iteration traverses the data path
                    '// through a device topology starting at the input
                    '// connector and ending at the output connector.
                    WHILE %TRUE
                        IF giProgStat = 0 THEN GOTO ExitHere
                
                        hr = pConnFrom.IsConnected(bConnected)
                        IF FAILED(hr) THEN GOTO ExitHere
                
                        '// Does this connector connect to another device?
                        IF bConnected = %FALSE THEN
                
                            '// This is the end of the data path that
                            '// stretches from the endpoint device to the
                            '// system bus or external bus. Verify that
                            '// the connection type is Software_IO.
                            hr = pConnFrom.GetType(connType)
                            IF FAILED(hr) THEN GOTO ExitHere
                
                            IF connType = %ConnectorType_Software_IO THEN
                                '? "Finished looking for connections."
                                EXIT LOOP '// finished
                            END IF
                            IF FAILED(hr = %E_FAIL) THEN GOTO ExitHere
                        END IF
                
                        '// Get the connector in the next device topology,
                        '// which lies on the other side of the connection.
                        hr = pConnFrom.GetConnectedTo(pConnTo)
                        IF FAILED(hr) THEN GOTO ExitHere
                
                        '// Get the connector's IPart interface.
                        hr = pConnTo.QueryInterface( _
                                        IID_IPart, _
                                        BYVAL VARPTR(pPartPrev))
                        IF FAILED(hr) THEN GOTO ExitHere
                        pConnTo = NOTHING
                
                        '// Inner loop: Each iteration traverses one link in a
                        '// device topology and looks for input multiplexers.
                        WHILE %TRUE
                
                            IF giProgStat = 0 THEN GOTO ExitHere
                
                            MyPumpMessages(2000)
                            '// Follow downstream link to next part.
                            hr = pPartPrev.EnumPartsOutgoing(ppParts)
                            IF FAILED(hr) THEN GOTO ExitHere
                
                            hr = ppParts.GetPart(0, pPartNext)
                            ppParts = NOTHING
                            IF FAILED(hr) THEN GOTO ExitHere
                
                            hr = pPartNext.GetPartType(pPartType)
                            IF FAILED(hr) THEN GOTO ExitHere
                
                            IF SUCCEEDED(pPartNext.GetName(pName)) THEN
                                '? @pName   'Microphone Boost
                
                                'Failure of the following call means only that
                                'the part is not a boost (microphone boost).
                                IF UCASE$(ComponentName) <> UCASE$(@pName) THEN
                                    '? "Not a match."
                                ELSE
                                    '? "The part name matched."
                                    'get IAudioVolumeLevel to control volume
                                    hr = pPartNext.Activate( _
                                                %CLSCTX_ALL, _
                                                IID_IAudioVolumeLevel, _
                                                pIaudioVolumeLevel)
                
                                    CALL SetMicrophoneBoostCallback
                
                                    GOTO ExitHere
                                END IF
                                CoTaskMemFree(pName)
                            END IF
                
                            '? "Try again..."
                
                            SELECT CASE pPartType
                                CASE %PartType_Connector  '=0
                                    '? "Connector found."
                                    '// We've reached the output connector that
                                    '// lies at the end of this device topology.
                                    hr = pPartNext.QueryInterface( _
                                                      IID_IConnector, _
                                                      BYVAL VARPTR(pConnFrom))
                                    IF FAILED(hr) THEN GOTO ExitHere
                
                                    pPartPrev = NOTHING
                                    pPartNext = NOTHING
                                    EXIT LOOP
                
                                CASE %PartType_Subunit '=1
                                    '? "Subunit found."
                
                            END SELECT
                
                            '// Failure of the following call means only that
                            '// the part is not a MUX (input selector).
                            hr = pPartNext.Activate( _
                                              %CLSCTX_ALL, _
                                              IID_IAudioInputSelector, _
                                              pSelector)
                            IF hr = %S_OK THEN
                                ? "The MUX was found."
                                '// We found a MUX (input selector), so select
                                '// the input from our endpoint device.
                                LOCAL localId AS DWORD
                                hr = pPartPrev.GetLocalId(localId)
                                IF FAILED(hr) THEN GOTO ExitHere
                
                                hr = pSelector.SetSelection(localId, BYVAL %NULL)
                                IF FAILED(hr) THEN GOTO ExitHere
                
                                pSelector = NOTHING
                            END IF
                
                            pPartPrev = NOTHING
                            pPartPrev = pPartNext
                            pPartNext = NOTHING
                        WEND
                    WEND
                
                ExitHere:
                    CoTaskMemFree(pName)
                    ppParts   = NOTHING
                    pConnTo   = NOTHING
                    pPartPrev = NOTHING
                    pSelector = NOTHING
                    FUNCTION = hr
                
                END FUNCTION
                '_____________________________________________________________________________
                
                CALLBACK FUNCTION DlgProc
                    LOCAL  pNMHDR                    AS NMHDR POINTER
                    LOCAL  rcThumb_1                 AS RECT
                    LOCAL  rcThumb_2                 AS RECT
                    LOCAL  rcThumb_3                 AS RECT
                    LOCAL  xhr                       AS LONG
                    STATIC Mute                      AS LONG
                    STATIC TrackBarPos_1             AS LONG
                    STATIC TrackBarPos_2             AS LONG
                    STATIC TrackBarPos_3             AS LONG
                    LOCAL pid                        AS DWORD
                
                    SELECT CASE CBMSG
                
                        CASE %WM_INITDIALOG
                            giProgStat = 1
                            gsMasterVolumeIsChangeable = "YES"
                            giStartStopRecognition = 1     'start recognition
                
                            IF VistaPlus THEN 'Vista to 10
                                giSpeakers = SpeakersCheck
                                CALL GetMuteSpeakers
                                CALL GetVolumeSpeakers
                                CALL SetSpeakersCallback
                
                                gpMeterInfo = NOTHING
                                giMicrophone     = MicrophoneCheck
                                CALL GetMuteMicrophone
                                CALL GetVolumeMicrophone
                                CALL SetMicrophoneCallback
                
                                CALL GetVolumeMicrophoneBoost
                
                                CALL CREATE_THREAD_SERVICES
                
                            ELSE '95 to XP
                                ? "Sorry, this app does not support this OS."
                            END IF
                
                        CASE %WM_APP
                            SELECT CASE CB.WPARAM
                                CASE %CheckboxVolOnOffSpeakers
                                    'NOTIFY MASTER MUTE
                                    SELECT CASE CB.LPARAM
                                        CASE 1
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_CHECKED, 0)
                                            IconSet_1(0)
                
                                        CASE ELSE
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                            IconSet_1(1)
                                    END SELECT
                
                                CASE %CheckboxVolOnOffMicro
                                    'NOTIFY MICROPHONE MUTE
                                    SELECT CASE CB.LPARAM
                                        CASE 1
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                                            'IconSet_2(0)
                
                                        CASE ELSE
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                            'IconSet_2(1)
                                    END SELECT
                
                                CASE %TrackbarVolSpeakers
                                    'NOTIFY MASTER VOLUME
                                    PostMessageA (GetDlgItem(hDlg, %TrackbarVolSpeakers), %TBM_SETPOS, %TRUE, %TrackbarMax - CB.LPARAM)
                                    SetDlgItemTextA (hDlg, %LabelVolSpeakers, FORMAT$(CB.LPARAM, "##0"))
                
                                CASE %TrackbarVolMicro
                                    'NOTIFY MICROPHONE VOLUME
                                    PostMessageA (GetDlgItem(hDlg, %TrackbarVolMicro), %TBM_SETPOS, %TRUE, %TrackbarMax - CB.LPARAM)
                                    SetDlgItemTextA (hDlg, %LabelVolMicro, FORMAT$(CB.LPARAM, "##0"))
                
                                CASE %TrackbarVolMicroBoost
                                    'NOTIFY MICROPHONE BOOST
                                    SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, %TrackbarMaxBoost - CB.LPARAM/10)
                                    SetDlgItemTextA (hDlg, %LabelVolMicroBoost, FORMAT$(CB.LPARAM, "##0") & " dB")
                
                            END SELECT
                
                
                        CASE %WM_COMMAND
                            SELECT CASE CB.CTL
                                CASE %SpeakersUp
                                    CALL SPEAKERS_UP
                                CASE %SpeakersDn
                                    CALL SPEAKERS_DN
                                CASE %MicrophoneUp
                                    CALL MICROPHONE_UP
                                CASE %MicrophoneDn
                                    CALL MICROPHONE_DN
                                CASE %MicrophoneBoostUp
                                    CALL MICROPHONE_BOOST_UP
                                CASE %MicrophoneBoostDn
                                    CALL MICROPHONE_BOOST_DN
                                CASE %SpeakersMuteUnmute
                                    CALL SpeakersMuteSelect
                                CASE %MicrophoneMuteUnmute
                                    CALL MicrophoneMuteSelect
                
                                'SPEAKERS
                                CASE %LabelOnOffSpeakers
                                    'IF (CBCTLMSG = %STN_CLICKED) OR (CBCTLMSG = 1) THEN
                                        IF IsDlgButtonChecked (hDlg, %CheckboxVolOnOffSpeakers) THEN
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                        ELSE
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers), %BM_SETCHECK, %BST_CHECKED, 0)
                                        END IF
                                        PostMessageA (hDlg, %WM_COMMAND, MAKDWD(%CheckboxVolOnOffSpeakers, %BN_CLICKED), GetDlgItem(hDlg, %CheckboxVolOnOffSpeakers))
                                    'END IF
                
                                CASE %CheckboxVolOnOffSpeakers
                                    'IF (CBCTLMSG = %BN_CLICKED) OR (CBCTLMSG = 1) THEN
                                        IF IsDlgButtonChecked(hDlg, %CheckboxVolOnOffSpeakers) THEN
                                            Mute = %TRUE
                                            IconSet_1(0)
                                        ELSE
                                            Mute = %FALSE 'Mute XOR %TRUE
                                            IconSet_1(1)
                                        END IF
                                        IF VistaPlus THEN 'Vista to 10   !!!!!! mute change
                                            SELECT CASE Mute
                                                CASE %TRUE
                                                    CALL MuteSpeakers
                                                CASE %FALSE
                                                    CALL UnMuteSpeakers
                                            END SELECT
                                        ELSE '95 to XP
                                            ? "Sorry, this app does not support this OS."
                                        END IF
                                    'END IF
                
                                CASE %ButtonSndVolExeSpeakers
                                    'IF (CBCTLMSG = %BN_CLICKED) OR (CBCTLMSG = 1) THEN
                                        IF VistaPlus THEN 'Vista to 10
                                            'SndVol.exe, use the -f flag to show the master volume slider only: SndVol.exe -f
                                            'SndVol.exe allows you to specify window location by adding coordinates after -f switch:
                                            pid = SHELL("SndVol.exe") '"C:\Windows\System32\SndVol.exe"
                                        ELSE '95 to XP
                                            ? "Sorry, this app does not support this OS."
                                        END IF
                                    'END IF
                
                                CASE %ButtonSndVolExeMicro
                                    'IF (CBCTLMSG = %BN_CLICKED) OR (CBCTLMSG = 1) THEN
                                        IF VistaPlus THEN 'Vista to 10
                                            pid = SHELL("rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,1")
                                        ELSE '95 to XP
                                            ? "Sorry, this app does not support this OS."
                                        END IF
                                    'END IF
                
                                'MICROPHONE
                                CASE %LabelOnOffMicro
                                    'IF (CBCTLMSG = %STN_CLICKED) OR (CBCTLMSG = 1) THEN
                                        IF IsDlgButtonChecked (hDlg, %CheckboxVolOnOffMicro) THEN
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_UNCHECKED, 0)
                                        ELSE
                                            PostMessageA (GetDlgItem(hDlg, %CheckboxVolOnOffMicro), %BM_SETCHECK, %BST_CHECKED, 0)
                                        END IF
                                        PostMessageA (hDlg, %WM_COMMAND, MAKDWD(%CheckboxVolOnOffMicro, %BN_CLICKED), GetDlgItem(hDlg, %CheckboxVolOnOffMicro))
                                    'END IF
                
                                CASE %CheckboxVolOnOffMicro
                                    'IF (CBCTLMSG = %BN_CLICKED) OR (CBCTLMSG = 1) THEN
                                        IF IsDlgButtonChecked(hDlg, %CheckboxVolOnOffMicro) THEN
                                            Mute = %TRUE
                                            'IconSet_2(0)
                                        ELSE
                                            Mute = %FALSE 'Mute XOR %TRUE
                                            'IconSet_2(1)
                                        END IF
                                        IF VistaPlus THEN 'Vista to 10   !!!!!! mute change
                                            SELECT CASE Mute
                                                CASE %TRUE
                                                    CALL MuteMicrophone
                                                CASE %FALSE
                                                    CALL UnMuteMicrophone
                                            END SELECT
                                        ELSE '95 to XP
                                            ? "Sorry, this app does not support this OS."
                                        END IF
                                    'END IF
                
                            END SELECT
                
                        CASE %WM_NOTIFY
                            pNMHDR = CBLPARAM
                            'SPEAKERS
                            IF @pNMHDR.hwndFrom = GetDlgItem(hDlg, %TrackbarVolSpeakers) THEN
                                IF @pNMHDR.code = %NM_RELEASEDCAPTURE THEN
                                    BEEP
                                END IF
                                SendMessageA (@pNMHDR.hwndFrom, %TBM_GETTHUMBRECT, 0, VARPTR(rcThumb_1))
                                MapWindowPoints (@pNMHDR.hwndFrom, hDlg, BYVAL VARPTR(rcThumb_1), 2)
                                SetWindowPos (GetDlgItem(hDlg, %LabelVolSpeakers), 0, rcThumb_1.nRight + 5, _
                                                        rcThumb_1.nTop - 2, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                            END IF
                            'MICROPHONE
                            IF @pNMHDR.hwndFrom = GetDlgItem(hDlg, %TrackbarVolMicro) THEN
                                IF @pNMHDR.code = %NM_RELEASEDCAPTURE THEN
                                    'BEEP
                                END IF
                                SendMessageA (@pNMHDR.hwndFrom, %TBM_GETTHUMBRECT, 0, VARPTR(rcThumb_2))
                                MapWindowPoints (@pNMHDR.hwndFrom, hDlg, BYVAL VARPTR(rcThumb_2), 2)
                                SetWindowPos (GetDlgItem(hDlg, %LabelVolMicro), 0, rcThumb_2.nRight + 5, _
                                                        rcThumb_2.nTop - 2, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                            END IF
                            'MICROPHONE BOOST
                            IF @pNMHDR.hwndFrom = GetDlgItem(hDlg, %TrackbarVolMicroBoost) THEN
                                IF @pNMHDR.code = %NM_RELEASEDCAPTURE THEN
                                    'BEEP
                                END IF
                                'SendMessageA (@pNMHDR.hwndFrom, %TBM_GETTHUMBRECT, 0, VARPTR(rcThumb_3))
                                'MapWindowPoints (@pNMHDR.hwndFrom, hDlg, BYVAL VARPTR(rcThumb_3), 2)
                                'SetWindowPos (GetDlgItem(hDlg, %LabelVolMicro), 0, rcThumb_3.nRight + 5, _
                                '                        rcThumb_3.nTop - 2, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                            END IF
                
                        CASE %WM_VSCROLL
                            'SPEAKERS
                            IF CBLPARAM = GetDlgItem(hDlg, %TrackbarVolSpeakers) THEN
                                SELECT CASE LOWRD(CBWPARAM)
                                    CASE %TB_LINEDOWN, %TB_LINEUP, %TB_THUMBPOSITION, %TB_THUMBTRACK, _
                                         %TB_PAGEDOWN, %TB_PAGEUP, %TB_TOP, %TB_BOTTOM', %TB_ENDSCROLL
                                        TrackBarPos_1 = %TrackbarMax - SendMessageA (CBLPARAM, %TBM_GETPOS, 0, 0)
                                        SetDlgItemTextA (hDlg, %LabelVolSpeakers, FORMAT$(TrackBarPos_1, "##0"))
                                        'Set speaker volume
                                        IF VistaPlus THEN 'Vista to 10  !!!!! volume change
                                            CALL SetVolumeSpeakers(TrackBarPos_1 / 100) 'range 0 to 100
                                        ELSE '95 to XP
                                          ? "Sorry, this app does not support this OS."
                                        END IF
                                END SELECT
                            END IF
                            'MICROPHONE
                            IF CBLPARAM = GetDlgItem(hDlg, %TrackbarVolMicro) THEN
                                SELECT CASE LOWRD(CBWPARAM)
                                    CASE %TB_LINEDOWN, %TB_LINEUP, %TB_THUMBPOSITION, %TB_THUMBTRACK, _
                                         %TB_PAGEDOWN, %TB_PAGEUP, %TB_TOP, %TB_BOTTOM', %TB_ENDSCROLL
                                        TrackBarPos_2 = %TrackbarMax - SendMessageA (CBLPARAM, %TBM_GETPOS, 0, 0)
                                        SetDlgItemTextA (hDlg, %LabelVolMicro, FORMAT$(TrackBarPos_2, "##0"))
                                        'Set microphone volume
                                        IF VistaPlus THEN 'Vista to 10  !!!!! volume change
                                            CALL SetVolumeMicrophone(TrackBarPos_2 / 100)  'range 0 to 100
                                        ELSE '95 to XP
                                          ? "Sorry, this app does not support this OS."
                                        END IF
                                END SELECT
                            END IF
                            'MICROPHONE BOOST
                            IF CBLPARAM = GetDlgItem(hDlg, %TrackbarVolMicroBoost) THEN
                                SELECT CASE LOWRD(CBWPARAM)
                                    CASE %TB_LINEDOWN, %TB_LINEUP, %TB_THUMBPOSITION, %TB_THUMBTRACK, _
                                         %TB_PAGEDOWN, %TB_PAGEUP, %TB_TOP, %TB_BOTTOM', %TB_ENDSCROLL
                                        TrackBarPos_3 = %TrackbarMaxBoost - SendMessageA (CBLPARAM, %TBM_GETPOS, 0, 0)
                                        SetDlgItemTextA (hDlg, %LabelVolMicroBoost, FORMAT$(TrackBarPos_3 * 10, "##0") & " dB")
                
                                        'Set microphone volume
                                        IF VistaPlus THEN 'Vista to 10  !!!!! volume change
                                            CALL SetVolumeMicrophoneBoost(TrackBarPos_3 * 10) 'range 0 to 3
                                        ELSE '95 to XP
                                          ? "Sorry, this app does not support this OS."
                                        END IF
                                END SELECT
                            END IF
                
                
                        CASE %WM_CLOSE
                            CALL SetVolumeMicrophoneBoost(1 * 10) 'setting to 1 on close (range 0 to 3)
                            giStartStopRecognition = 2
                            giProgStat = 0
                            'DestroyWindow hWnd
                
                        CASE %WM_DESTROY         'window is being destroyed
                                                 'after windows is removed from screen (children still exist)
                            CALL UnRegisterSpeakerCallback
                            CALL UnRegisterMicrophoneCallback
                            CALL UnRegisterMicrophoneBoostCallback
                
                            pSelector                            = NOTHING
                            gpIMMDeviceEnumMicrophone            = NOTHING
                            gpIMMDeviceMicrophone                = NOTHING
                            gpIAudioEndpointVolumeMicrophone     = NOTHING
                            gpMeterInfo                          = NOTHING
                            pDeviceTopology                      = NOTHING
                            pConnFrom                            = NOTHING
                            pConnTo                              = NOTHING
                            pPartPrev                            = NOTHING
                            pPartNext                            = NOTHING
                            ppParts                              = NOTHING
                            pIaudioVolumeLevel                   = NOTHING
                
                            'PostQuitMessage 0    'only needed if dialog is modeless
                            'If an application processes this message, it should return zero.
                            FUNCTION = 0
                            EXIT FUNCTION
                
                    END SELECT
                
                END FUNCTION
                
                '______________________________________________________________________________
                
                FUNCTION PBMAIN()
                    LOCAL OS AS OSVERSIONINFOEXA
                
                    OS.dwOSVersionInfoSize = SIZEOF(OSVERSIONINFOEXA)
                    GetVersionExA (BYVAL VARPTR(OS))
                    IF (OS.dwPlatformId = %VER_PLATFORM_WIN32_NT) AND (OS.dwMajorVersion >= 6) THEN
                      VistaPlus = %TRUE 'Vista to 10
                    END IF
                
                    CLSID_MMDeviceEnumerator         = GUID$("{BCDE0395-E52F-467C-8E3D-C4579291692E}")
                    IID_IMMDeviceEnumerator          = GUID$("{A95664D2-9614-4F35-A746-DE8DB63617E6}")
                    IID_IAudioMeterInformation       = GUID$("{C02216F6-8C67-4B5B-9D00-D008E73E0064}")
                    IID_IDeviceTopology              = GUID$("{2A07407E-6497-4A18-9787-32F79BD0D98F}")
                    IID_IAudioEndpointVolume         = GUID$("{5CDF2C82-841E-4546-9722-0CF74078229A}")
                    IID_IPart                        = GUID$("{AE2DE0E4-5BCA-4F2D-AA46-5D13F8FDB3A9}")
                    IID_IAudioVolumeLevel            = GUID$("{7FB7B48F-531D-44A2-BCB3-5AD5A134B3DC}")
                    IID_IConnector                   = GUID$("{9C2C4058-23F5-41DE-877A-DF3AF236A09E}")
                    IID_IAudioInputSelector          = GUID$("{4F03DC02-5E6E-4653-8F72-A030C123D598}")
                    IID_IAudioEndpointVolumeCallback = GUID$("{657804FA-D6AD-4496-8A60-352752AF4F89}")
                    IID_IControlChangeNotify         = GUID$("{A09513ED-C709-4D21-BD7B-5F34C47F3947}")
                
                    DIALOG FONT "Segoe UI", 9
                    DIALOG NEW PIXELS, %HWND_DESKTOP, $AppNAme, , , 280, 400, _
                    %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU, 0 TO hDlg
                
                    'SPEAKERS
                    IconSet_1(-2) 'Init
                    IconSet_1(1)  'Set to icon on
                
                    CONTROL ADD LABEL, hDlg, %LabelInfoSpeakers, "Speakers:", _
                                                         15, 10, 80, 20, %WS_CHILD OR %WS_VISIBLE OR %SS_NOPREFIX OR %SS_CENTER, %WS_EX_CLIENTEDGE
                    CONTROL ADD LABEL, hDlg, %LabelInfoMicro, "Microphone:", _
                                                         100, 10, 80, 20, %WS_CHILD OR %WS_VISIBLE OR %SS_NOPREFIX OR %SS_CENTER, %WS_EX_CLIENTEDGE
                    CONTROL ADD LABEL, hDlg, %LabelInfoMicroBoost, "Boost:", _
                                                         185, 10, 80, 20, %WS_CHILD OR %WS_VISIBLE OR %SS_NOPREFIX OR %SS_CENTER, %WS_EX_CLIENTEDGE
                
                    CONTROL ADD LABEL, hDlg, %LabelMaxSpeakers, "Max", 35, 40, 30, 12
                    CONTROL ADD LABEL, hDlg, %LabelMinSpeakers, "Min", 35, 195, 30, 12
                    CONTROL ADD LABEL, hDlg, %LabelVolSpeakers, "---", 60, 120, 30, 12
                
                    CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarVolSpeakers, "msctls_trackbar321", 30, 55, 25, 140, _
                    %WS_VISIBLE OR %WS_CHILD OR %TBS_BOTH OR %TBS_NOTICKS OR %WS_TABSTOP OR %TBS_VERT OR %TBS_REVERSED
                    SendDlgItemMessageA (hDlg, %TrackbarVolSpeakers, %TBM_SETPOS, %TRUE, 50)
                    SendDlgItemMessageA (hDlg, %TrackbarVolSpeakers, %TBM_SETPAGESIZE,0, 10)
                    SendDlgItemMessageA (hDlg, %TrackbarVolSpeakers, %TBM_SETRANGEMAX, %TRUE, %TrackbarMax)
                    SendDlgItemMessageA (hDlg, %TrackbarVolSpeakers, %TBM_SETRANGEMIN, %TRUE, 0)
                
                
                    CONTROL ADD LABEL, hDlg, %LabelMaxMicro, "Max", 125, 40, 30, 12
                    CONTROL ADD LABEL, hDlg, %LabelMinMicro, "Min", 125, 195, 30, 12
                    CONTROL ADD LABEL, hDlg, %LabelVolMicro, "---", 150, 120, 30, 12
                
                    CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarVolMicro, "msctls_trackbar321", 120, 55, 25, 140, _
                    %WS_VISIBLE OR %WS_CHILD OR %TBS_BOTH OR %TBS_NOTICKS OR %WS_TABSTOP OR %TBS_VERT OR %TBS_REVERSED
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETPOS, %TRUE, 50)
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETPAGESIZE,0, 10)
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETRANGEMAX, %TRUE, %TrackbarMax)
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETRANGEMIN, %TRUE, 0)
                
                
                    CONTROL ADD LABEL, hDlg, %LabelMaxMicroBoost, "Max", 90 + 125, 40, 30, 12
                    CONTROL ADD LABEL, hDlg, %LabelMinMicroBoost, "Min", 90 + 125, 195, 30, 12
                    CONTROL ADD LABEL, hDlg, %LabelVolMicroBoost, "---", 90 + 150, 120, 30, 12
                
                    CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarVolMicroBoost, "msctls_trackbar321", 90 + 120, 55, 25, 140, _
                    %WS_VISIBLE OR %WS_CHILD OR %TBS_BOTH OR %TBS_NOTICKS OR %WS_TABSTOP OR %TBS_VERT OR %TBS_REVERSED
                    'SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, 1)
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPAGESIZE,0, 1)
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETRANGEMAX, %TRUE, 3)
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETRANGEMIN, %TRUE, 0)
                
                
                    CONTROL ADD CHECKBOX, hDlg, %CheckboxVolOnOffMicro, "Microphone Mute", 50, 20 + 210, 140, 12, _
                    %BS_AUTOCHECKBOX OR %WS_TABSTOP
                
                    CONTROL ADD CHECKBOX, hDlg, %CheckboxVolOnOffSpeakers, "Speakers Mute", 50, 20 + 230, 140, 12, _
                    %BS_AUTOCHECKBOX OR %WS_TABSTOP
                
                    CONTROL ADD LABEL, hDlg, %LabelOnOffSpeakers, "", 190, 240, _
                    GetSystemMetrics (%SM_CXICON), GetSystemMetrics(%SM_CYICON), %SS_ICON OR %SS_NOTIFY
                
                    IconSet_1(1)
                
                    CONTROL ADD BUTTON, hDlg, %ButtonSndVolExeSpeakers, "Volume Mixer", 50, 280, 180, 22
                
                    CONTROL ADD BUTTON, hDlg, %ButtonSndVolExeMicro, "Recording Mixer", 50, 312, 180, 22
                
                    CONTROL ADD PROGRESSBAR, hDlg, %ID_VOLUME, "", 15, 352, 250, 20
                    CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETRANGE, 0, MAKLNG(0, 100)
                    CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETSTEP, -1, 0
                    CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETBKCOLOR, 0, RGB(50,50,50)
                    CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETBARCOLOR, 0, RGB(55,224,230)
                
                    CONTROL ADD LABEL, hDlg, %IDC_PEAK_METER, "", 15, 377, 250, 20
                
                
                
                    DIALOG SHOW MODAL hDlg CALL DlgProc
                
                    IconSet_1(-1) 'Release
                
                END FUNCTION
                '______________________________________________________________________________
                
                '_________________________________________________________________
                '
                '   SUB CREATE_THREAD_SERVICES
                '                    full time
                '_________________________________________________________________
                
                SUB CREATE_THREAD_SERVICES
                  CALL CreateRecognitionProcess
                
                  LOCAL idThreadPeakMeter         AS LONG
                
                  THREAD CREATE PeakMeterThread(1) TO idThreadPeakMeter
                  THREAD CLOSE idThreadPeakMeter TO idThreadPeakMeter
                END SUB
                '_________________________________________________________________
                '
                '   THREAD FUNCTION  PeakMeterThread           NEW 2020 MAY 18 NEW
                '                    full time
                '_________________________________________________________________
                
                THREAD FUNCTION PeakMeterThread(BYVAL yyy AS LONG) AS LONG
                
                    DO WHILE GlobalVariableTestOn
                
                        '// Update the peak meter in the dialog box.
                        IF ISOBJECT(gpMeterInfo) THEN
                            gpMeterInfo.GetPeakValue(PEAK)
                            IF giMasterMicrophoneMuteLevel = 0 THEN
                                CONTROL SET TEXT hDlg, %IDC_PEAK_METER, USING$("##.#", PEAK * 100)
                                CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETPOS, (PEAK * 100), 0
                            ELSE
                                PEAK = 0 'Need to do this to make sure the progress bar and
                                         'peak indicator show 0.0 during computer speech.
                                CONTROL SET TEXT hDlg, %IDC_PEAK_METER, USING$("##.#", PEAK * 100)
                                CONTROL SEND hDlg, %ID_VOLUME, %PBM_SETPOS, (PEAK * 100), 0
                            END IF
                        END IF
                
                        SLEEP 10
                        IF GlobalVariableTest86 = 1 THEN GOTO LoopEnd
                        SLEEP 20
                    LOOP
                    LoopEnd:
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   SUB  CreateRecognitionProcess                           Jarvis
                '_________________________________________________________________
                
                SUB CreateRecognitionProcess
                  LOCAL idThreadRecognition        AS LONG
                
                  THREAD CREATE RecogProcessThread(1) TO idThreadRecognition
                  THREAD CLOSE idThreadRecognition TO idThreadRecognition
                END SUB
                '_________________________________________________________________
                '
                '   THREAD FUNCTION  RecogProcessThread                     Jarvis
                '                    full time
                '
                '   new 2020 MAY 18 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.
                '
                ' NOTE: oMyGrammar.DictationSetState(%SGDSActive)           'Turn Dictation on.
                ' This causes the microphone volume to drop 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.
                
                '_________________________________________________________________
                
                THREAD FUNCTION RecogProcessThread(BYVAL yyy AS LONG) AS LONG
                    LOCAL MyPhraseData AS STRING
                
                    SLEEP 1000 'small delay
                    giRunningRecogProcess = 1
                
                    DO WHILE GlobalVariableTestOn = 1
                
                        MyPhraseData = TRIM$(MyDataX)
                
                        IF MyPhraseData <> "" THEN
                            MyDataX = ""
                            CALL PutColonOnMarque
                            CALL PROCESS_RECOGNITION(MyPhraseData)
                            CALL ResetRecognition
                        END IF
                
                        SLEEP 20
                        CALL MyPumpMessages(100)
                        IF GlobalVariableTest86 = 1 THEN
                            EXIT LOOP
                        END IF
                
                        SELECT CASE RecognitionStatus
                            CASE 0
                                IF giStartStopRecognition = 1 THEN
                                    giStartStopRecognition = 0  'reset signal
                
                                    IF ISFALSE ISOBJECT(oRecoContext) THEN
                                        CALL CREATE_RECOGNITION_CONTEXT   '1st time %SGDSActive is called
                                    END IF
                
                                    IF ISTRUE ISOBJECT(oRecoContext) THEN
                                        CALL GetVolumeMicrophone                             'workaround
                                        LastValue = giMasterMicrophoneLevel/100              'workaround
                
                                        CALL RECOGNITION_ENABLE           '2nd time %SGDSActive is called
                                        RecognitionStatus = 1
                                        CALL PutQuestionMarkOnMarque
                
                                        'Emmulate recognition to test the interface.
                                        oRecoContext.Recognizer.EmulateRecognition("recognition has started")
                
                                        CALL SetVolumeMicrophone(LastValue)                  'workaround
                                        CALL RefreshVolumeMicrophoneTrackbarPos(LastValue)   'workaround
                                    ELSE
                                        ? "The recognizer is not available."
                                    END IF
                                ELSE
                                END IF
                
                            CASE 1
                                IF giStartStopRecognition = 2 THEN
                                    giStartStopRecognition = 0  'reset signal
                                    CALL RECOGNITION_OFF
                                END IF
                
                        END SELECT
                
                        SLEEP 20
                        CALL MyPumpMessages(100)
                        IF GlobalVariableTest86 = 1 THEN
                            EXIT LOOP
                        END IF
                
                    LOOP
                
                    giRunningRecogProcess = 0
                END FUNCTION
                '_________________________________________________________________
                '
                ' SUB  CREATE_RECOGNITION_CONTEXT                           Jarvis
                '_________________________________________________________________
                
                SUB CREATE_RECOGNITION_CONTEXT
                  LOCAL vInput     AS STRING
                
                  SLEEP 10
                
                  'Create an instance of the ISpeechRecoContext Interface
                  oRecoContext = NEWCOM "SAPI.SpInProcRecoContext"
                
                  '** VB WithEvents work around. **
                  'Link the events of oRecoContext to InProcEvents to
                  'process a recognition event.
                  InProcEvents = CLASS "CISpeechRecoContextEventsImplemented"
                  EVENTS FROM oRecoContext CALL InProcEvents
                
                  'Create the InProc Speech Recognizer.
                  oRecognizer = oRecoContext.Recognizer
                
                  'Create the InProc Speech Grammar.
                  oMyGrammar = oRecoContext.CreateGrammar(1)
                
                  'Disable Grammar while loading it.
                  oMyGrammar.State = %SGSDisabled
                
                  'Load the default Dictation Grammar.
                  vInput = ""
                  oMyGrammar.DictationLoad(vInput, %SLOstatic)
                  oMyGrammar.DictationSetState(%SGDSInactive)
                
                  'Load the command and control grammar
                  IF FileExists(EXE.PATH$ + $CandCGrammar) THEN      '"jarvis.xml"
                      oMyGrammar.CmdLoadFromFile(EXE.PATH$ + $CandCGrammar, %SLODynamic) 'SLOStatic)
                      'dim gData As Variant
                      'gData = gsSOLXML
                      'oMyGrammar.CmdLoadFromMemory gData, %SLODynamic         'Load from memory
                
                      oMyGrammar.CmdSetRuleIdState(0, %SGDSInactive)
                      giCCGrammarLoaded = 1
                      '? "Command and Control Grammar loaded"
                      'gsBannerDictation = "*"
                  ELSE
                      'CALL FLASH_NOTE_FOR_SECONDS("Unable to load C&C grammar.", 1.5)
                      '? "Unable to load C&C grammar."
                      giCCGrammarLoaded = 0
                      'gsBannerDictation = ">"
                  END IF
                
                  CALL RECOGNITION_ENABLE
                
                  'Enable Grammar after loading it.
                  oMyGrammar.State = %SGSEnabled
                
                  CALL RECOGNITION_DISABLE
                
                  'Create the Audio Token Category.
                  oCategory = NEWCOM "SAPI.SpObjectTokenCategory"
                
                  'Set the Audio Token category ID.
                  oCategory.SetId("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\AudioInput")
                
                  'Create the Audio Token.
                  oToken = NEWCOM "SAPI.SpObjectToken"
                
                  'Set the Token Category ID.
                  oToken.SetId(oCategory.Default())
                
                  'Give the Recognizer the Token.
                  oRecognizer.PutRef_AudioInput() = oToken
                END SUB
                
                '_________________________________________________________________
                '
                ' SUB RECOGNITION_ENABLE
                '_________________________________________________________________
                
                SUB RECOGNITION_ENABLE
                
                  'Start the recognition by activating one of the grammars
                
                  IF ISTRUE ISOBJECT(oRecoContext) THEN
                
                      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
                
                  ELSE
                      CALL PutEqualSignOnMarque
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                ' SUB RECOGNITION_DISABLE
                '_________________________________________________________________
                
                SUB RECOGNITION_DISABLE
                  'Stop the recognition by deactivating both of the grammars
                  IF ISTRUE ISOBJECT(oRecoContext) THEN
                      CALL PutTildeOnMarque
                      oMyGrammar.DictationSetState(%SGDSInactive)         'Turn Dictation off.
                
                      IF giCCGrammarLoaded = 1 THEN
                          oMyGrammar.CmdSetRuleIdState(0, %SGDSInactive)  'turn command / contol off
                      END IF
                  ELSE
                      CALL PutEqualSignOnMarque
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                ' SUB RECOGNITION_OFF                                 VISTA PLUS
                '_________________________________________________________________
                
                SUB RECOGNITION_OFF
                  CALL RECOGNITION_DISABLE
                
                  RecognitionStatus = 0
                END SUB
                
                
                '_________________________________________________________________
                '
                ' SUB PROCESS_RECOGNITION                      NEW 2020 MAY 18 NEW
                '_________________________________________________________________
                
                SUB PROCESS_RECOGNITION(TheWordPhrase AS STRING)
                  LOCAL lsMyData    AS STRING
                  LOCAL lsMessage   AS STRING
                
                  IF giProcessingRecog = 1 THEN CALL ResetRecognition: EXIT SUB
                  giProcessingRecog = 1
                
                  lsMyData = LCASE$(TRIM$(TheWordPhrase))
                  REPLACE $CRLF WITH "" IN lsMyData
                  REPLACE $CR   WITH "" IN lsMyData
                  REPLACE $TAB  WITH "" IN lsMyData
                  REPLACE "_"   WITH "" IN lsMyData
                
                  TheWordPhrase = ""
                  CALL ResetRecognition
                
                  CALL GetEvents(100)
                
                  '? lsMyData
                  SELECT CASE lsMyData
                      CASE "recognition has started"
                          'update banner
                      CASE "what was that?"
                      CASE "speakers up", _
                          "speakes plus", _
                          "speakers increase", _
                          "haut-parleurs"
                          PostMessageA hDlg, %WM_COMMAND, %SpeakersUp, 0
                      CASE "speakers down", _
                          "speakers minus", _
                          "speakers decrease", _
                          "haut-parleurs vers le bas"
                          PostMessageA hDlg, %WM_COMMAND, %SpeakersDn, 0
                      CASE "microphone up", _
                          "microphone plus", _
                          "microphone increase", _
                          "microphone haut"
                          PostMessageA hDlg, %WM_COMMAND, %MicrophoneUp, 0
                      CASE "microphone down", _
                          "microphone minus", _
                          "microphone decrease", _
                          "microphone vers le bas"
                          PostMessageA hDlg, %WM_COMMAND, %MicrophoneDn, 0
                      CASE "boost up", _
                          "boost plus", _
                          "boost increase", _
                          "augmentation du microphone"
                          PostMessageA hDlg, %WM_COMMAND, %MicrophoneBoostUp, 0
                      CASE "boost down", _
                          "boost minus", _
                          "boost decrease", _
                          "augmentation du microphone"
                          PostMessageA hDlg, %WM_COMMAND, %MicrophoneBoostDn, 0
                
                      CASE "microphone mute", _
                          "microphone you", _
                          "microphone huge", _
                          "couper le microphone"
                          PostMessageA hDlg, %WM_COMMAND, %MicrophoneMuteUnmute, 0
                      CASE "speakers mute", _
                          "speakers you", _
                          "haut-parleurs muets"
                          PostMessageA hDlg, %WM_COMMAND, %SpeakersMuteUnmute, 0
                      CASE "boost mute" 'not used
                
                      CASE "vol mixer", _
                          "volume mixer", _
                          "mélangeur de volume"
                          PostMessageA hDlg, %WM_COMMAND, %ButtonSndVolExeSpeakers, 0
                      CASE "recording mixer", _
                          "table de mixage d'enregistrement"
                          PostMessageA hDlg, %WM_COMMAND, %ButtonSndVolExeMicro, 0
                
                  END SELECT
                
                  giProcessingRecog = 0
                END SUB
                
                '_________________________________________________________________
                '
                ' SUB ResetRecognition                                      Jarvis
                '_________________________________________________________________
                
                SUB ResetRecognition
                
                  GetEvents(80)
                
                  IF RecognitionStatus = 1 THEN
                      'Pause Recognition
                      oRecoContext.Pause()
                
                      GetEvents(8)
                
                      'Resume Recognition
                      oRecoContext.Resume()
                  END IF
                
                END SUB
                
                SUB F698  'REFRESH_MARQUEE_BANNER
                  PostMessageA hDlg, %WM_COMMAND, %ID_RefreshBanner, 0
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  PutTildeOnMarque   the recognition context is available but not activated
                '_________________________________________________________________
                
                SUB PutTildeOnMarque
                  gsBannerListen = "~"
                  CALL F698
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  PutColonOnMarque   means a recognition has happened and is being processed   are you ok
                '_________________________________________________________________
                
                SUB PutColonOnMarque
                  IF RecognitionStatus = 1 THEN
                      IF giMicroMute = 0 THEN
                          gsBannerListen = ":"
                          CALL F698
                      END IF
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  PutQuestionMarkOnMarque   the recognition context is available and active
                '_________________________________________________________________
                
                SUB PutQuestionMarkOnMarque            'currently unused
                
                  IF RecognitionStatus = 1 THEN
                      IF giMicroMute = 0 THEN
                          'microphone is not muted
                          gsBannerListen = "?"
                          CALL F698
                      END IF
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  PutEqualSignOnMarque      no recognition context available
                '_________________________________________________________________
                
                SUB PutEqualSignOnMarque
                  IF RecognitionStatus = 0 THEN
                      gsBannerListen = "="
                      CALL F698
                  END IF
                END SUB
                
                
                '_________________________________________________________________
                '
                ' FileExists - make sure a file or folder exists
                '_________________________________________________________________
                
                FUNCTION FileExists(sFileName AS ASCIIZ) AS LONG
                    LOCAL sFN AS STRING
                    sFN = REMOVE$(sFileName, $CRLF)
                    FUNCTION = PathFileExistsA(BYVAL STRPTR(sFN)) 'Return non-zero if file or folder exist.
                
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   SUB  EnumUILanguagesCB
                '_________________________________________________________________
                
                FUNCTION EnumUILanguagesCB(lpUILanguageString AS ASCIIZ, lParam AS LONG) AS LONG
                 LOCAL zLocale AS ASCIIZ * %MAX_PATH
                
                 GetLocaleInfoA (VAL("&h" & lpUILanguageString), %LOCALE_SENGLANGUAGE, zLocale, %MAX_PATH)
                 sBuffer = zLocale & $SPC & lpUILanguageString & $CRLF
                
                 FUNCTION = %TRUE 'To continue enumeration, the callback function should return TRUE.
                
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  SpeakersMuteSelect
                '_________________________________________________________________
                
                FUNCTION SpeakersMuteSelect AS LONG
                
                    IF giSpeakers = 1 THEN
                
                        IF giMasterSpeakersMuteLevel = 0 THEN
                            MuteSpeakers
                        ELSE
                            UnMuteSpeakers
                        END IF
                    END IF
                
                    FUNCTION = 1
                END FUNCTION
                '_________________________________________________________________
                '
                '   FUNCTION  MicrophoneMuteSelect
                '_________________________________________________________________
                
                FUNCTION MicrophoneMuteSelect AS LONG
                
                    IF giMicrophone = 1 THEN
                
                        IF giMasterMicrophoneMuteLevel = 0 THEN
                            MuteMicrophone
                        ELSE
                            UnMuteMicrophone
                        END IF
                    END IF
                
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  GetVolumeSpeakers
                '_________________________________________________________________
                
                FUNCTION GetVolumeSpeakers AS LONG
                
                    LOCAL xhr     AS LONG
                    LOCAL fVolume AS SINGLE
                
                    IF gsMasterVolumeIsChangeable = "YES" THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeSpeakers) THEN
                
                            xhr = gpIAudioEndpointVolumeSpeakers.GetMasterVolumeLevelScalar(fVolume)
                            IF FAILED(xhr) THEN
                                ? "Unable to get scalar on default stereo speakers."
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                '? "Default Stereo Speakers volume scalar is " + STR$(fVolume)
                            END IF
                
                            giMasterSpeakersLevel = fVolume * 100
                            SendDlgItemMessageA (hDlg, %TrackbarVolSpeakers, %TBM_SETPOS, %TRUE, %TrackbarMax - fVolume * 100)
                            SetDlgItemTextA (hDlg, %LabelVolSpeakers, FORMAT$(fVolume * 100, "##0"))
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  GetVolumeMicrophone
                '_________________________________________________________________
                
                FUNCTION GetVolumeMicrophone() AS LONG
                
                    LOCAL xhr     AS LONG
                    LOCAL fVolume AS SINGLE
                
                    IF giMicrophone = 1 THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
                
                            xhr = gpIAudioEndpointVolumeMicrophone.GetMasterVolumeLevelScalar(fVolume)
                
                            IF FAILED(xhr) THEN
                                'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on default microphone.")
                                ? "Unable to get volume state on default microphone"
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                ' "Default microphone volume get successfully!"
                            END IF
                
                            giMasterMicrophoneLevel = fVolume * 100
                            SendDlgItemMessageA (hDlg, %TrackbarVolMicro, %TBM_SETPOS, %TRUE, %TrackbarMax - fVolume * 100)
                            SetDlgItemTextA (hDlg, %LabelVolMicro, FORMAT$(fVolume * 100, "##0"))
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  GetVolumeMicrophoneBoost
                '_________________________________________________________________
                
                FUNCTION GetVolumeMicrophoneBoost() AS LONG
                
                    LOCAL xhr     AS LONG
                    LOCAL fVolume AS SINGLE
                
                    IF giMicrophone = 1 THEN
                        IF ISOBJECT(pIaudioVolumeLevel) THEN
                
                            xhr = pIaudioVolumeLevel.GetLevel(0, fVolume)
                
                            IF FAILED(xhr) THEN
                                'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on microphone boost.")
                                ? "Unable to get volume state on microphone boost"
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                ' "Default microphone boost volume get successful!"
                            END IF
                
                            giMasterMicroBoostLevel = fVolume
                            SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, %TrackbarMaxBoost - fVolume/10)
                            SetDlgItemTextA (hDlg, %LabelVolMicroBoost, FORMAT$(fVolume, "##0") & " dB")
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  SetVolumeSpeakers
                '_________________________________________________________________
                
                FUNCTION SetVolumeSpeakers(BYVAL SpeakerScalar AS SINGLE) AS LONG
                
                    LOCAL xhr   AS LONG
                
                    IF gsMasterVolumeIsChangeable = "YES" THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeSpeakers) THEN
                
                            xhr = gpIAudioEndpointVolumeSpeakers.SetMasterVolumeLevelScalar(SpeakerScalar, g_guid_1_Context) 'BYVAL %NULL) 'BYREF pguidEventContext AS GUID
                
                            IF FAILED(xhr) THEN
                                ? "Unable to set scalar on default stereo speakers."
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                '? "Default Stereo Speakers volume set successfully!"
                            END IF
                
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  SetVolumeMicrophone
                '_________________________________________________________________
                
                FUNCTION SetVolumeMicrophone(BYVAL MicroScalar AS SINGLE) AS LONG
                
                    LOCAL xhr   AS LONG
                
                    IF giMicrophone = 1 THEN
                        IF ISOBJECT(gpIAudioEndpointVolumeMicrophone) THEN
                
                            xhr = gpIAudioEndpointVolumeMicrophone.SetMasterVolumeLevelScalar(MicroScalar, g_guid_2_Context) 'BYVAL %NULL)  'BYREF pguidEventContext AS GUID
                
                            IF FAILED(xhr) THEN
                                'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on default microphone.")
                                ? "Unable to set volume state on default microphone"
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                ' "Default microphone volume set successfully!"
                            END IF
                
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  SetVolumeMicrophoneBoost
                '_________________________________________________________________
                
                FUNCTION SetVolumeMicrophoneBoost(BYVAL MicroScalar AS SINGLE) AS LONG
                
                    LOCAL xhr   AS LONG
                
                    IF giMicrophone = 1 THEN
                        IF ISOBJECT(pIaudioVolumeLevel) THEN
                
                            xhr = pIaudioVolumeLevel.SetLevel(0, MicroScalar, g_guid_3_Context) 'BYVAL %NULL)  'BYREF pguidEventContext AS GUID
                
                            IF FAILED(xhr) THEN
                                'CALL SHOW_NOTES_NO_WAIT("Unable to set scalar on microphone boost.")
                                ? "Unable to set volume state on microphone boost"
                                FUNCTION = 0
                                EXIT FUNCTION
                            ELSE
                                ' "Default microphone boost volume set successful!"
                            END IF
                
                        END IF
                    END IF
                    FUNCTION = 1
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   SUB  SPEAKERS_UP
                '_________________________________________________________________
                
                SUB SPEAKERS_UP
                  CALL GetVolumeSpeakers
                  IF giMasterSpeakersLevel < 100 THEN
                      giMasterSpeakersLevel = giMasterSpeakersLevel + 1
                      CALL SetVolumeSpeakers(giMasterSpeakersLevel/ 100)
                      CALL RefreshVolumeSpeakersTrackbarPos(giMasterSpeakersLevel/ 100)
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  SPEAKERS_DN
                '_________________________________________________________________
                
                SUB SPEAKERS_DN
                  CALL GetVolumeSpeakers
                  IF giMasterSpeakersLevel > 0 THEN
                      giMasterSpeakersLevel = giMasterSpeakersLevel - 1
                      CALL SetVolumeSpeakers(giMasterSpeakersLevel/ 100)
                      CALL RefreshVolumeSpeakersTrackbarPos(giMasterSpeakersLevel/ 100)
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  MICROPHONE_UP
                '_________________________________________________________________
                
                SUB MICROPHONE_UP
                  CALL GetVolumeMicrophone
                  IF giMasterMicrophoneLevel < 100 THEN
                      giMasterMicrophoneLevel = giMasterMicrophoneLevel + 1
                      CALL SetVolumeMicrophone(giMasterMicrophoneLevel/ 100)
                      CALL RefreshVolumeMicrophoneTrackbarPos(giMasterMicrophoneLevel/ 100)
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  MICROPHONE_DN
                '_________________________________________________________________
                
                SUB MICROPHONE_DN
                  CALL GetVolumeMicrophone
                  IF giMasterMicrophoneLevel > 0 THEN
                      giMasterMicrophoneLevel = giMasterMicrophoneLevel - 1
                      CALL SetVolumeMicrophone(giMasterMicrophoneLevel/ 100)
                      CALL RefreshVolumeMicrophoneTrackbarPos(giMasterMicrophoneLevel/ 100)
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  MICROPHONE_BOOST_UP
                '_________________________________________________________________
                
                SUB MICROPHONE_BOOST_UP
                  CALL GetVolumeMicrophoneBoost
                  IF giMasterMicroBoostLevel < 30 THEN
                      giMasterMicroBoostLevel = giMasterMicroBoostLevel + 10
                      CALL SetVolumeMicrophoneBoost(giMasterMicroBoostLevel)
                      CALL RefreshVolumeMicrophoneBoostTrackbarPos(giMasterMicroBoostLevel)   'workaround
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   SUB  MICROPHONE_BOOST_DN
                '_________________________________________________________________
                
                SUB MICROPHONE_BOOST_DN
                  CALL GetVolumeMicrophoneBoost
                  IF giMasterMicroBoostLevel > 0 THEN
                      giMasterMicroBoostLevel = giMasterMicroBoostLevel - 10
                      CALL SetVolumeMicrophoneBoost(giMasterMicroBoostLevel)
                      CALL RefreshVolumeMicrophoneBoostTrackbarPos(giMasterMicroBoostLevel)   'workaround
                  END IF
                END SUB
                
                '_________________________________________________________________
                '
                '   FUNCTION  RefreshVolumeSpeakersTrackbarPos
                '_________________________________________________________________
                
                FUNCTION RefreshVolumeSpeakersTrackbarPos(BYVAL MicroScalar1 AS SINGLE) AS LONG
                    SendMessageA (hDlg, %WM_APP, %TrackbarVolSpeakers, MicroScalar1 * %TrackbarMax)
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  RefreshVolumeMicrophoneTrackbarPos
                '_________________________________________________________________
                
                FUNCTION RefreshVolumeMicrophoneTrackbarPos(BYVAL MicroScalar2 AS SINGLE) AS LONG
                    SendMessageA (hDlg, %WM_APP, %TrackbarVolMicro, MicroScalar2 * %TrackbarMax)
                END FUNCTION
                
                '_________________________________________________________________
                '
                '   FUNCTION  RefreshVolumeMicrophoneBoostTrackbarPos
                '_________________________________________________________________
                
                FUNCTION RefreshVolumeMicrophoneBoostTrackbarPos(BYVAL MicroScalar3 AS SINGLE) AS LONG
                    SendDlgItemMessageA (hDlg, %TrackbarVolMicroBoost, %TBM_SETPOS, %TRUE, %TrackbarMaxBoost - MicroScalar3/10)
                    SetDlgItemTextA (hDlg, %LabelVolMicroBoost, FORMAT$(MicroScalar3, "##0") & " dB")
                END FUNCTION
                Jarvis.xml
                Code:
                <GRAMMAR LANGID="409">
                
                   <!--
                   Created by James R Fritts on 2018-10-13
                   Extracted from xBot version 7
                   Last updated on: 2020-05-19
                   -->
                
                   <RULE NAME="recStart" TOPLEVEL="ACTIVE">
                      <P>Recognition has started</P>
                   </RULE>
                
                   <RULE NAME="recStop" TOPLEVEL="ACTIVE">
                      <P>stop recognition</P>
                   </RULE>
                
                   <RULE NAME="mixCmd"> TOPLEVEL="ACTIVE">
                     <L>
                        <!--
                        Mixer Command Functions
                        -->
                        <P>volume mixer</P>
                        <P>recording mixer</P>
                
                     </L>
                
                   </RULE>
                
                   <RULE NAME="spkrCmd" TOPLEVEL="ACTIVE">
                     <P>speakers</P>
                     <RULEREF NAME="dirCmd"/>
                   </RULE>
                
                   <RULE NAME="microCmd" TOPLEVEL="ACTIVE">
                     <P>microphone</P>
                     <RULEREF NAME="dirCmd"/>
                   </RULE>
                
                   <RULE NAME="boostCmd" TOPLEVEL="ACTIVE">
                     <P>boost</P>
                     <RULEREF NAME="dirCmd"/>
                   </RULE>
                
                   <RULE NAME="cmdCtrl" TOPLEVEL="ACTIVE">
                     <P>jarvis</P>
                     <O>please</O>
                     <RULEREF NAME="endCmd"/>
                   </RULE>
                
                   <RULE NAME="pressCmd" TOPLEVEL="ACTIVE">
                     <O>press</O>
                     <L>
                        <RULEREF NAME="voiceCmd"/>
                     </L>
                   </RULE>
                
                   <RULE NAME="endCmd">
                
                     <L>
                        <P>get the time</P>
                        <P>turn all lights on</P>
                        <P>turn all lights off</P>
                     </L>
                
                   </RULE>
                
                   <RULE NAME="dirCmd">
                
                     <L>
                        <P>mute</P>
                        <P>up</P>
                        <P>plus</P>
                        <P>increase</P>
                        <P>down</P>
                        <P>minus</P>
                        <P>decrease</P>
                     </L>
                
                   </RULE>
                
                </GRAMMAR>
                Last edited by Jim Fritts; 19 May 2020, 03:15 PM.

                Comment


                • #9
                  Oops, the Jarvis Command and Control Grammar was not applied because this line was REMED.

                  'gsBannerDictation = "*"

                  Also the Jarvis Command & Control Grammar does not work because of this line:


                  <RULEREF NAME="voiceCmd"/>


                  Which calls a name that does not exist.
                  Sorry about that.

                  Here is the update.
                  VistaPlusAudio.zip
                  Last edited by Jim Fritts; 30 Jun 2020, 03:01 PM.

                  Comment

                  Working...
                  X