Announcement

Collapse
No announcement yet.

Slow SAPI

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

  • #41
    Hey Jim!

    When I run what you posted in the Source Code forum ... if I start on 0, press down arrow 4 times, item 1 starts to speak, is cancelled and 4 is fully spoken.

    Is that how it is intended to work?

    Comment


    • #42
      Mike Doty shouldn't be receiving any credit from the academy.
      Gary started all this and Pierre showed how to do without an extra thread.
      I justed reposted the code and tested doing it a little differently using subclass and calling speak on key up and mouse up.
      https://duckduckgo.com instead of google

      Comment


      • #43
        A variant from my previous code...

        Code:
        #COMPILE EXE 'Use José's includes
        #DIM ALL
        #REGISTER NONE
        #INCLUDE "Win32Api.inc"
        #INCLUDE "SApi.inc"
        
        GLOBAL hDlg AS DWORD
        
        %Listbox01 = 101
        '_____________________________________________________________________________
        
        CALLBACK FUNCTION DlgProc
         STATIC zWavFile     AS ASCIIZ * %MAX_PATH
         STATIC wsText       AS WSTRINGZ * 64
         STATIC pISpVoice    AS ISpVoice
         STATIC DoClickSound AS LONG
         LOCAL  index        AS LONG
        
         SELECT CASE CBMSG
        
           CASE %WM_INITDIALOG
             ExpandEnvironmentStrings("%WinDir%\Media\Windows Navigation Start.wav", zWavFile, %MAX_PATH) 'Wav valid from Vista to 10.
             pISpVoice = NEWCOM "SAPI.SpVoice"
        
           CASE %WM_SETCURSOR
             DoClickSound = (CBLPARAM <> MAKDWD(%HTCLIENT, %WM_LBUTTONDOWN)) 'Is mouse left button down
        
           CASE %WM_COMMAND
             SELECT CASE CBCTL
        
               CASE %Listbox01
                 SELECT CASE CBCTLMSG
                   CASE %LBN_SELCHANGE
                     LISTBOX GET SELECT hDlg, %Listbox01 TO index
                     LISTBOX GET TEXT hDlg, %Listbox01, index TO wsText
                     IF DoClickSound THEN 'Or use GetAsyncKeyState(%VK_DOWN) AND &H8000) << HOME-END-PGUP-PGDN-LEFT-RIGHT-UP
                       SndPlaySound(zWavFile, %SND_FILENAME OR %SND_ASYNC)
                       wsText = "<silence msec='500'/>" & wsText
                     END IF
                     pISpVoice.Speak(wsText, %SPF_ASYNC OR %SPF_PURGEBEFORESPEAK, BYVAL %NULL)
                 END SELECT
        
               CASE %IDOK 'Enter key
                 IF GetFocus() = GetDlgItem(CBHNDL, %Listbox01) THEN 'Enter key on listbox
                   DoClickSound = %FALSE 'Prevent click sound
                   PostMessage(CBHNDL, %WM_COMMAND, MAK(DWORD, %Listbox01, %LBN_SELCHANGE), 0) 'Trigger
                 END IF
        
             END SELECT
          END SELECT
        
        END FUNCTION
        '_____________________________________________________________________________
        
        FUNCTION PBMAIN()
         LOCAL hIcon AS DWORD
        
         DIALOG FONT "Segoe UI", 9
         DIALOG NEW %HWND_DESKTOP, "The speaking list", , , 150, 110, _
         %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SIZEBOX OR %WS_SYSMENU, %WS_EX_LEFT TO hDlg
        
         hIcon = ExtractIcon(GetModuleHandle(""), "Shell32.dll", 294) 'o
         SetClassLong(hDlg, %GCL_HICON, hIcon)
        
         DIM List(0 TO 9) AS STRING
         ARRAY ASSIGN List() = "Asynchronous is the way", "Do purge before speak", "No click sound using mouse", _
                               "Ear a click using keyboard", "½ second speak grace delay using keyboard", _
                               "Enter to repeat", "Expand environment strings", "Sound play sound", "Speak", "Silence"
         CONTROL ADD LISTBOX, hDlg, %Listbox01, List(), 5, 5, 140, 105, _
         %LBS_NOTIFY OR %WS_VSCROLL, %WS_EX_LEFT OR %WS_EX_STATICEDGE
        
         DIALOG SHOW MODAL hDlg CALL DlgProc
        
         DestroyIcon(hIcon)
        
        END FUNCTION
        '_____________________________________________________________________________
        '
        Last edited by Pierre Bellisle; 11 May 2019, 11:05 AM.

        Comment


        • #44
          I'm sorry that cannot add anything to the discussion; I lack knowledge and experience on this topic.

          But I have been reading the posts with interest, and to satisfy my curiosity, I have tried some of the code.

          So, I had a user idea - what if I want to repeat the same word 3 or 4 times in sequence?
          It seems a little "klunky" to have to move the arrow up and then down again right away, and then repeat again and again.
          How about I leave the selection bar on a word and press the ENTER key a couple of times? That would be a nice feature, yes?

          Seeing how the up and down arrow keys are recognized via %VK_UP and %VK_DOWN, I tried to add the ENTER key by using the %VK_RETURN value, but it wasn't being recognized.
          (I suppose that pressing the ENTER key wasn't triggering a %LBN_SELCHANGE message?)

          So I looked for something like a "key pressed" message in ListBox Notifications, but I could not find anything appropriate.


          So the question is: how to modify the code in post 37 to recognize the ENTER key, in order to repeat the word that the selector is already on?

          Although I would like to know the answer to this, I do not want to derail the discussion, so please feel free to ignore this!

          Thanks,
          -John



          Comment


          • #45
            Hi John,

            The WM_GetDlgCode message is one place where you can detect when the user presses Enter. You have to subclass the ListBox to get that message, as in the code below which implements your suggestion. I've used similar code for detecting Enter in ListView controls.

            I modified the code from my OP - this does not have all of the improvements from the other posts.

            Code:
            #Compile Exe
            #Dim All
            %Unicode=1
            
            #Debug Error On
            #Debug Display On
            
            #Include "win32api.inc"
            #Include "sapi.inc"
            
            %IDC_ListBox = 500
            %Msg_SAPI_Event  = %WM_User+1
            
            Global psp As ISpVoice, TextToSpeak$$, hDlg,hThread,hList As Dword
            Global OldLBProc As Long
            
            Function PBMain() As Long
               Dim MyArray(7) As String
               Array Assign MyArray() = "zero", "one", "two", "three","four","five","six","seven"
               Dialog Default Font "Tahoma", 21,1
               Dialog New Pixels, 0, "ListBox Test",300,300,150,300, %WS_OverlappedWindow, 0 To hDlg
               Control Add ListBox, hDlg, %IDC_ListBox, MyArray(), 5,5,190,300
               Control Handle hDlg, %IDC_ListBox To hList
               OldLBProc = SetWindowLong(hList, %GWL_WndProc, CodePtr(NewLBProc))
               Dialog Show Modal hDlg Call DlgProc
            End Function
            
            CallBack Function DlgProc() As Long
               Local iRow As Long, temp$
               Select Case Cb.Msg
                  Case %WM_InitDialog
                     pSp = NewCom "SAPI.SpVoice"
                     pSp.SetInterest(SPFEI(%SPEI_WORD_BOUNDARY) Or SPFEI(%SPEI_END_INPUT_STREAM), SPFEI(%SPEI_WORD_BOUNDARY) Or SPFEI(%SPEI_END_INPUT_STREAM))
                     pSp.SetNotifyWindowMessage(hDlg, %MSG_SAPI_EVENT, 0, 0)
                  Case %WM_Command
                     Select Case Cb.Ctl
                        Case %IDC_ListBox
                           Select Case Cb.CtlMsg
                              Case %LBN_SelChange
                                 ListBox Get Select hDlg, %IDC_ListBox To iRow
                                 ListBox Get Text hDlg, %IDC_ListBox, iRow To TextToSpeak$$
                                 Thread Create SimpleReadText(0) To hThread
                                 Thread Close hThread To hThread
                           End Select
                     End Select
               End Select
            End Function
            
            Thread Function SimpleReadText(ByVal X As Long) As Long
               pSp.Speak(ByVal StrPtr(TextToSpeak$$), %SPF_Async or %SPF_PurgeBeforeSpeak, ByVal %Null)
            End Function
            
            Function NewLBProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
               Local iRow As Long
               Select Case Msg
                  Case %WM_GetDlgCode
                     If wParam = %VK_Return Then
                        ListBox Get Select hDlg, %IDC_ListBox To iRow
                        ListBox Get Text hDlg, %IDC_ListBox, iRow To TextToSpeak$$
                        Thread Create SimpleReadText(0) To hThread
                        Thread Close hThread To hThread
                     End If
               End Select
               Function = CallWindowProc(OldLBProc, hWnd, Msg, wParam, lParam)
            End Function

            Comment


            • #46
              This works, but is incorrect coding.
              Perhaps someone can do it correctly this way?
              Code:
              #COMPILE EXE 'Using José's includes  beenetalk3.bas
              #DIM ALL
              #INCLUDE "Win32Api.inc"
              #INCLUDE "SApi.inc"
              
              %Listbox01 = 101
              '_____________________________________________________________________________
              
              CALLBACK FUNCTION DlgProc
               STATIC pISpVoice AS ISpVoice
               STATIC wsText    AS WSTRINGZ * 64
               LOCAL  index     AS LONG
              
               SELECT CASE CBMSG
                 CASE %WM_INITDIALOG
                   pISpVoice = NEWCOM "SAPI.SpVoice"
                 CASE %WM_COMMAND
                   SELECT CASE CBCTL
                     IF CBWPARAM = 1 THEN
                       pISpVoice.Speak(wsText, %SPF_ASYNC OR %SPF_PURGEBEFORESPEAK, BYVAL %NULL)
                     END IF
                     CASE %Listbox01
                       SELECT CASE CBCTLMSG
                         CASE %LBN_SELCHANGE
              
                           LISTBOX GET SELECT CBHNDL, %Listbox01 TO index
                           LISTBOX GET TEXT CBHNDL, %Listbox01, index TO wsText
                           IF (GetAsyncKeyState(%VK_DOWN) AND &H8000) OR _
                              (GetAsyncKeyState(%VK_UP)   AND &H8000) THEN  'Arrow key down
                              'WinBeep(2500, 100)
                             ' BEEP '  see post #26 where Pierre used PlaySound  'this is Pierre's code, not mine.
                              wsText = "<silence msec='500'/>" & wsText
                           END IF
                           pISpVoice.Speak(wsText, %SPF_ASYNC OR %SPF_PURGEBEFORESPEAK, BYVAL %NULL)
                       END SELECT
                   END SELECT
                END SELECT
              END FUNCTION
              '_____________________________________________________________________________
              
              FUNCTION PBMAIN()
               LOCAL hDlg AS DWORD
              
               DIALOG NEW %HWND_DESKTOP, "Speaking list", , , 150, 110, %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SYSMENU TO hDlg
               DIM List(0 TO 9) AS STRING
               ARRAY ASSIGN List() = "Asynchronous is the way", "Do purge before speak", "zero", "one", "two", "three", "four", "five", "six", "seven"
               CONTROL ADD LISTBOX, hDlg, %Listbox01, List(), 5, 5, 120, 105, %LBS_NOTIFY OR %WS_VSCROLL, %WS_EX_LEFT OR %WS_EX_STATICEDGE
               DIALOG SHOW MODAL hDlg CALL DlgProc
              END FUNCTION
              https://duckduckgo.com instead of google

              Comment


              • #47
                All, the source code app was designed to stay quiet when the arrow (left or right or up or down) button is detected in the pushed down position. If a button continues to be detected as down it will indicate the user is executing a fluid and rapid movement. If the listbox position is unchanged from its current position then no voice is generated. Your typing rate will affect the voice output. If the location is changed before the line phrase has completed voicing then the phrase output will be extinguished unless the speech processor is near the end of the phrase.

                Gary,
                Yes, that was the intended behavior.

                John,
                If you want to re-voice the phrase then a mouse drag on the listbox item can accomplish this. The line where the mouse is released will be voiced. Or simply clicking the line will cause it to re-voiced. Yes, a subclass will provide access to VK_RETURN detection as in Gary's example.

                NOTE: The app was designed to work without a subclass routine, provides a new method to clear the voice using "," rather than SKIP, has a fulltime speech thread so no time is wasted recreating the speech thread, stop speech output whenever an arrow button is detected as pushed, method to reduce voice lag which happens when the phrase is very short (the speech processor will continue to voice the line even though another line was selected), etc.

                Comment


                • #48
                  John,

                  Added trapping EnterKey without subclassing from Michael Mattias
                  Doesn't need extra thread

                  Updated Pierre Bellisle's last update in post #43.
                  Notice the click he added moving between items
                  Some of the optional lines remarked

                  Code:
                  '#COMPILE EXE 'Use José's includes   beenetalk4.bas 5/11/19 12:33AM CST
                  '#DIM ALL
                  '#REGISTER NONE
                  '#INCLUDE "Win32Api.inc"
                  #INCLUDE "SApi.inc"
                  
                  GLOBAL hDlg AS DWORD
                  
                  %Listbox01 = 101
                  '_____________________________________________________________________________
                  
                  CALLBACK FUNCTION DlgProc
                   STATIC zWavFile          AS ASCIIZ * %MAX_PATH
                   STATIC wsText            AS WSTRINGZ * 64
                   STATIC pISpVoice         AS ISpVoice
                   STATIC LastCursorMessage AS DWORD
                   LOCAL  index             AS LONG
                   'LOCAL  IDFocus           AS LONG
                  
                   SELECT CASE CBMSG
                  
                     CASE %WM_INITDIALOG
                       ExpandEnvironmentStrings("%WinDir%\Media\Windows Navigation Start.wav", zWavFile, %MAX_PATH) 'Get %WinDir%. Wav is valid in Vista to Ten.
                       pISpVoice = NEWCOM "SAPI.SpVoice"
                  
                     CASE %WM_SETCURSOR
                       LastCursorMessage = CBLPARAM
                  
                     CASE %WM_COMMAND
                      IF CB.CTL = %IDOK THEN   'code from Michael Mattias
                        IF GetDlgCtrlId(GetFocus) = %Listbox01 THEN
                         pISpVoice.Speak(wsText, %SPF_ASYNC OR %SPF_PURGEBEFORESPEAK, BYVAL %NULL)
                        END IF
                       END IF
                  
                       SELECT CASE CBCTL
                         CASE %Listbox01
                           SELECT CASE CBCTLMSG
                             CASE %LBN_SELCHANGE
                               LISTBOX GET SELECT hDlg, %Listbox01 TO index
                               LISTBOX GET TEXT hDlg, %Listbox01, index TO wsText
                               IF LastCursorMessage <> MAKDWD(%HTCLIENT, %WM_LBUTTONDOWN) THEN
                               'Or use GetAsyncKeyState(%VK_DOWN) AND &H8000) << HOME-END-PGUP-PGDN-LEFT-RIGHT-UP
                                 SndPlaySound(zWavFile, %SND_FILENAME OR %SND_ASYNC)
                                 wsText = "<silence msec='500'/>" & wsText
                               END IF
                               pISpVoice.Speak(wsText, %SPF_ASYNC OR %SPF_PURGEBEFORESPEAK, BYVAL %NULL)
                           END SELECT
                       END SELECT
                    END SELECT
                  
                  END FUNCTION
                  '_____________________________________________________________________________
                  
                  FUNCTION PBMAIN()
                   LOCAL hIcon AS DWORD
                  
                   DIALOG FONT "Segoe UI", 9
                   DIALOG NEW %HWND_DESKTOP, "The speaking list", , , 150, 110, _
                   %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_SIZEBOX OR %WS_SYSMENU, %WS_EX_LEFT TO hDlg
                  
                   hIcon = ExtractIcon(GetModuleHandle(""), "Shell32.dll", 294) 'o
                   SetClassLong(hDlg, %GCL_HICON, hIcon)
                  
                   DIM List(0 TO 8) AS STRING
                   ARRAY ASSIGN List() = "Asynchronous is the way", "Do purge before speak", "No click sound using mouse", _
                                         "Ear a click using keyboard", "½ second speak grace delay using keyboard", _
                                         "ExpandEnvironmentStrings", "SoundPlaySound", "Silence", "Speak"
                   CONTROL ADD LISTBOX, hDlg, %Listbox01, List(), 5, 5, 140, 105, _
                   %LBS_NOTIFY OR %WS_VSCROLL, %WS_EX_LEFT OR %WS_EX_STATICEDGE
                  
                   DIALOG SHOW MODAL hDlg CALL DlgProc
                  
                   DestroyIcon(hIcon)
                  
                  END FUNCTION
                  Last edited by Mike Doty; 11 May 2019, 06:10 AM.
                  https://duckduckgo.com instead of google

                  Comment


                  • #49
                    Nice Mike.

                    Comment


                    • #50
                      Things to do:
                      1. Know which listbox item the mouse is over.
                      This would enable saying a tooltip, the item number, or the item itself before clicking.
                      2. Might be useful to isolate mouse to the listbox area unless some other action.

                      Checking out the API's GetCursorPosition, ScreenToClient.
                      https://forum.powerbasic.com/forum/u...screentoclient
                      https://duckduckgo.com instead of google

                      Comment


                      • #51
                        Yup, and TrackPopupMenu and %WM_MOUSEWHEEL with WindowFromPoint if you want to move listbox position by mouse wheel.

                        Comment

                        Working...
                        X