Announcement

Collapse
No announcement yet.

Selecting the default voice for Text-To-Speech

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

  • Selecting the default voice for Text-To-Speech

    Jim has kindly posted some code to allow text to speech output to a wav file. I have 3 voices installed on my system and I'm selecting the default voice in the control panel, however the speech file is always generated using the first one in the dropdown list in the control panel.

    https://forum.powerbasic.com/forum/u...ile-using-sapi

    Can anyone tell me how to select the default voice to use?

    THanks

  • #2
    Hey Steve!
    I know I posted some code here recently on that. Look for something like this ...

    Code:
       If FemaleVoice = 1 Then voice$$ = "Microsoft Zira Desktop - English (United States)"
       If FemaleVoice = 0 Then voice$$ = "Microsoft David Desktop - English (United States)"
       SpSetVoice(psp,voice$$)

    Comment


    • #3
      This is how I do it.

      Code:
            ' Get a reference to the SAPI ISpeechObjectTokens collection
            OBJECT CALL gdoSp.GetVoices( ) TO vRes
            IF ISFALSE OBJRESULT THEN
                LET oTokens = vRes
                vRes = EMPTY
                'Get the number of tokens
                OBJECT GET oTokens.Count TO vRes
                nCount = VARIANT#( vRes )
                'Parse the collection (zero based)
                FOR i = 0 TO nCount - 1
                    vIdx = i AS LONG
                    'Get the item by its index
                    OBJECT CALL oTokens.Item( vIdx ) TO vRes
                    IF ISFALSE OBJRESULT THEN
                        LET oToken = vRes
                        vRes = EMPTY
                        IF ISFALSE OBJRESULT THEN
                            'Get the description
                            OBJECT CALL oToken.GetDescription( ) TO vRes
                            IF ISFALSE OBJRESULT THEN
                                strDesc = VARIANT$( vRes )
                                ' If its the token we are after, set the Voice property
                                'MyVoiceName = "Microsoft Zira Desktop - English (United States)"
                                'MyVoiceName = "Microsoft Anna Desktop - English (United States)"
                                'MyVoiceName = "TOSHIBA male adult (U.S.)"
                                'MyVoiceName = "Microsoft David Desktop - English (United States)"
                                  IF strDesc = lsSpName THEN
                                    LET vToken = oToken
                                    OBJECT SET gdoSp.Voice = vToken
                                    LET oToken = NOTHING
                                    EXIT FOR
                                  END IF
                            END IF
                          LET oToken = NOTHING
                        END IF
                    END IF
                NEXT i
                LET oTokens = NOTHING
            END IF
      If strDesc = lsSpName then you have made the selection you want based on the text description (strDesc) of the voice. Where lsSpName is the target voice you want.

      Note: Any reference to gdoSp should be replaced with oVoice to match the Source Code app.
      And perhaps DIM oVoice AS DISPATCH
      You'll have to change other parts as well. Sorry nothing complete here.

      Comment


      • #4
        Steve you probably use something like this to get a list of the voices..

        Code:
        '_________________________________________________________________
        '
        ' SUB funGetVoices
        '_________________________________________________________________
        
        
        FUNCTION funGetVoices() AS STRING
        ' return the list of voices
          LOCAL vRes         AS VARIANT
          LOCAL vTxt         AS VARIANT
          LOCAL vTime        AS VARIANT
          LOCAL oTokens      AS DISPATCH
          LOCAL oToken       AS DISPATCH
          LOCAL vToken       AS VARIANT
          LOCAL i            AS LONG
          LOCAL vIdx         AS VARIANT
          LOCAL nCount       AS LONG
          LOCAL strDesc      AS STRING
          LOCAL strVoiceList AS STRING
        
          LOCAL liCount      AS LONG
        
            strVoiceList = ""
            IF ISFALSE ISOBJECT( gdoSp ) THEN
                LET gdoSp = NEWCOM "SAPI.SpVoice"
                IF ISFALSE ISOBJECT( gdoSp ) THEN
                    EXIT FUNCTION
                END IF
            END IF
            'Get a reference to the SAPI ISpeechObjectTokens collection
            OBJECT CALL gdoSp.GetVoices( ) TO vRes
            IF ISFALSE OBJRESULT THEN
                SET oTokens = vRes
                vRes = EMPTY
                'Get the number of tokens
                OBJECT GET oTokens.Count TO vRes
                nCount = VARIANT#( vRes )
                'Parse the collection (zero based)
                liCount = 0
        
                FOR i = 0 TO nCount - 1
                    vIdx = i AS LONG
                    ' Get the item by his index
                    OBJECT CALL oTokens.Item( vIdx ) TO vRes
                    IF ISFALSE OBJRESULT THEN
                        SET oToken = vRes
                        vRes = EMPTY
                        IF ISFALSE OBJRESULT THEN
                            'Get the description
                            OBJECT CALL oToken.GetDescription( ) TO vRes
                            IF ISFALSE OBJRESULT THEN
                                strDesc = VARIANT$( vRes )
                                IF strDesc = "TOSHIBA male adult (U.S.)" THEN
                                    GOTO OverThere
                                ELSE
                                    IF liCount <= 8 THEN
                                        strDesc = "00" + TRIM$(STR$(liCount + 1)) + ".  " + strDesc
                                    END IF
                                    IF liCount >= 8 AND liCount <= 98 THEN
                                        strDesc = "0" + TRIM$(STR$(liCount + 1)) + ".  " + strDesc
                                    END IF
                                    strVoiceList = strVoiceList & strDesc & "|"
                                    INCR liCount
                                END IF
                                OverThere:
                            END IF
                        END IF
                        SET oToken = NOTHING
                    END IF
                NEXT i
                SET oTokens = NOTHING
            END IF
        
            strVoiceList = RTRIM$(strVoiceList,"|")
            FUNCTION = strVoiceList
        
        END FUNCTION

        Comment


        • #5
          The example above culls
          "TOSHIBA male adult (U.S.)"
          so it does not put it in the list.

          This may be helpful https://forum.powerbasic.com/forum/u...831#post686831

          Comment


          • #6
            I've updated the code to this and Jim's function funGetVoices() on my system I have Microsoft George, Hazel and Susan in my control panel. The function only returns 2 in the string (one English (Great Britian and the other English US) which is a bit odd.

            Code:
             001.  Microsoft Hazel Desktop - English (Great Britain)|002.  Microsoft Zira Desktop - English (United States)
            When I check the registry I can see those 2 voices Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices

            Also I can't figure out how to modify the SpSetVoice function and compile so I've disabled is at the moment.

            Code:
            ' SED_PBWIN
            
            #COMPILE EXE
            #DIM ALL
            
            #INCLUDE "Win32api.inc"
            
            ENUM SpeechStreamFileMode
            
            SSFMOpenForRead = 0
            SSFMOpenReadWrite = 1
            SSFMCreate = 2
            SSFMCreateForWrite = 3
            
            END ENUM
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            $PROGID_SpeechLib_SpVoice = "SAPI.SpVoice"
            $PROGID_SpeechLib_SpFileStream = "SAPI.SpFileStream"
            $IID_SpeechLib_ISpeechFileStream = GUID$( "{AF67F125-AB39-4E93-B4A2-CC2E66E182A7}" )
            $IID_SpeechLib_ISpeechAudioFormat = GUID$( "{E6E9C590-3E18-40E3-8299-061F98BDE7C7}" )
            $IID_SpeechLib_ISpeechWaveFormatEx = GUID$( "{7A1EF0D5-1581-4741-88E4-209A49F11A10}" )
            $IID_SpeechLib_ISpeechVoice = GUID$( "{269316D8-57BD-11D2-9EEE-00C04F797396}" )
            $IID_SpeechLib_ISpeechVoiceStatus = GUID$( "{8BE47B07-57F6-11D2-9EEE-00C04F797396}" )
            $IID_SpeechLib_ISpeechObjectToken = GUID$( "{C74A3ADC-B727-4500-A84A-B526721C8B8C}" )
            $IID_SpeechLib_ISpeechDataKey = GUID$( "{CE17C09B-4EFA-44D5-A4C9-59D9585AB0CD}" )
            $IID_SpeechLib_ISpeechObjectTokenCategory = GUID$( "{CA7EAC50-2D01-4145-86D4-5AE7D70F4469}" )
            $IID_SpeechLib_ISpeechObjectTokens = GUID$( "{9285B776-2E7B-4BC0-B53E-580EB6FA967F}" )
            $IID_SpeechLib_ISpeechBaseStream = GUID$( "{6450336F-7D49-4CED-8097-49D6DEE37294}" )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechBaseStream
            ' Description     : ISpeechBaseStream Interface
            ' This Interface cannot be created directly it can only
            ' be returned by a Method or Property in this library.
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechBaseStream $IID_SpeechLib_ISpeechBaseStream
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET Format < 1 >( ) AS ISpeechAudioFormat
            PROPERTY SET PutRef_Format < 1 >( BYVAL AudioFormat AS ISpeechAudioFormat )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD READ < 2 >( BYREF Buffer AS VARIANT, BYVAL NumberOfBytes AS LONG ) AS LONG
            METHOD WRITE < 3 >( BYVAL Buffer AS VARIANT ) AS LONG
            METHOD SEEK < 4 >( BYVAL Position AS VARIANT, OPT BYVAL Origin AS LONG ) AS VARIANT
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechObjectTokens
            ' Description     : ISpeechObjectTokens Interface
            ' This Interface cannot be created directly it can only
            ' be returned by a Method or Property in this library.
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechObjectTokens $IID_SpeechLib_ISpeechObjectTokens
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET COUNT < 1 >( ) AS LONG
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD ITEM < 0 >( BYVAL Index AS LONG ) AS ISpeechObjectToken
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET PropGet__NewEnum < - 4 >( ) AS IUNKNOWN
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechObjectTokenCategory
            ' Description     : ISpeechObjectTokenCategory Interface
            ' Class Name      : SpObjectTokenCategory
            ' ClassID         : $CLSID_SpeechLib_SpObjectTokenCategory
            ' ProgID          : $PROGID_SpeechLib_SpObjectTokenCategory
            ' Version ProgID  : $PROGID_SpeechLib_SpObjectTokenCategory1
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechObjectTokenCategory $IID_SpeechLib_ISpeechObjectTokenCategory
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET ID < 1 >( ) AS WSTRING
            PROPERTY SET DEFAULT < 2 >( BYVAL TokenId AS WSTRING )
            PROPERTY GET DEFAULT < 2 >( ) AS WSTRING
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD SetId < 3 >( BYVAL Id AS WSTRING, OPT BYVAL CreateIfNotExist AS INTEGER )
            METHOD GetDataKey < 4 >( OPT BYVAL Location AS LONG ) AS ISpeechDataKey
            METHOD EnumerateTokens < 5 >( OPT BYVAL RequiredAttributes AS WSTRING, OPT BYVAL OptionalAttributes AS WSTRING ) AS _
              ISpeechObjectTokens
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechDataKey
            ' Description     : ISpeechDataKey Interface
            ' This Interface cannot be created directly it can only
            ' be returned by a Method or Property in this library.
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechDataKey $IID_SpeechLib_ISpeechDataKey
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD SetBinaryValue < 1 >( BYVAL ValueName AS WSTRING, BYVAL Value AS VARIANT )
            METHOD GetBinaryValue < 2 >( BYVAL ValueName AS WSTRING ) AS VARIANT
            METHOD SetStringValue < 3 >( BYVAL ValueName AS WSTRING, BYVAL Value AS WSTRING )
            METHOD GetStringValue < 4 >( BYVAL ValueName AS WSTRING ) AS WSTRING
            METHOD SetLongValue < 5 >( BYVAL ValueName AS WSTRING, BYVAL Value AS LONG )
            METHOD GetLongValue < 6 >( BYVAL ValueName AS WSTRING ) AS LONG
            METHOD OpenKey < 7 >( BYVAL SubKeyName AS WSTRING ) AS ISpeechDataKey
            METHOD CreateKey < 8 >( BYVAL SubKeyName AS WSTRING ) AS ISpeechDataKey
            METHOD DeleteKey < 9 >( BYVAL SubKeyName AS WSTRING )
            METHOD DeleteValue < 10 >( BYVAL ValueName AS WSTRING )
            METHOD EnumKeys < 11 >( BYVAL Index AS LONG ) AS WSTRING
            METHOD EnumValues < 12 >( BYVAL Index AS LONG ) AS WSTRING
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechObjectToken
            ' Description     : ISpeechObjectToken Interface
            ' Class Name      : SpObjectToken
            ' ClassID         : $CLSID_SpeechLib_SpObjectToken
            ' ProgID          : $PROGID_SpeechLib_SpObjectToken
            ' Version ProgID  : $PROGID_SpeechLib_SpObjectToken1
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechObjectToken $IID_SpeechLib_ISpeechObjectToken
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET ID < 1 >( ) AS WSTRING
            PROPERTY GET DataKey < 2 >( ) AS ISpeechDataKey
            PROPERTY GET Category < 3 >( ) AS ISpeechObjectTokenCategory
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD GetDescription < 4 >( OPT BYVAL Locale AS LONG ) AS WSTRING
            METHOD SetId < 5 >( BYVAL Id AS WSTRING, OPT BYVAL CategoryID AS WSTRING, OPT BYVAL CreateIfNotExist AS INTEGER )
            METHOD GetAttribute < 6 >( BYVAL AttributeName AS WSTRING ) AS WSTRING
            METHOD CreateInstance < 7 >( OPT BYVAL pUnkOuter AS IUNKNOWN, OPT BYVAL ClsContext AS LONG ) AS IUNKNOWN
            METHOD Remove < 8 >( BYVAL ObjectStorageCLSID AS WSTRING )
            METHOD GetStorageFileName < 9 >( BYVAL ObjectStorageCLSID AS WSTRING, BYVAL KeyName AS WSTRING, BYVAL FileName AS WSTRING, _
              BYVAL Folder AS LONG ) AS WSTRING
            METHOD RemoveStorageFileName < 10 >( BYVAL ObjectStorageCLSID AS WSTRING, BYVAL KeyName AS WSTRING, BYVAL DeleteFile AS _
              INTEGER )
            METHOD IsUISupported < 11 >( BYVAL TypeOfUI AS WSTRING, OPT BYREF IN ExtraData AS VARIANT, OPT BYVAL PB_Object AS _
              IUNKNOWN ) AS INTEGER
            METHOD DisplayUI < 12 >( BYVAL hWnd AS LONG, BYVAL Title AS WSTRING, BYVAL TypeOfUI AS WSTRING, OPT BYREF IN ExtraData AS _
              VARIANT, OPT BYVAL PB_Object AS IUNKNOWN )
            METHOD MatchesAttributes < 13 >( BYVAL Attributes AS WSTRING ) AS INTEGER
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechVoiceStatus
            ' Description     : ISpeechVoiceStatus Interface
            ' This Interface cannot be created directly it can only
            ' be returned by a Method or Property in this library.
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechVoiceStatus $IID_SpeechLib_ISpeechVoiceStatus
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET CurrentStreamNumber < 1 >( ) AS LONG
            PROPERTY GET LastStreamNumberQueued < 2 >( ) AS LONG
            PROPERTY GET LastHResult < 3 >( ) AS LONG
            PROPERTY GET RunningState < 4 >( ) AS LONG
            PROPERTY GET InputWordPosition < 5 >( ) AS LONG
            PROPERTY GET InputWordLength < 6 >( ) AS LONG
            PROPERTY GET InputSentencePosition < 7 >( ) AS LONG
            PROPERTY GET InputSentenceLength < 8 >( ) AS LONG
            PROPERTY GET LastBookmark < 9 >( ) AS WSTRING
            PROPERTY GET LastBookmarkId < 10 >( ) AS LONG
            PROPERTY GET PhonemeId < 11 >( ) AS INTEGER
            PROPERTY GET VisemeId < 12 >( ) AS INTEGER
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechVoice
            ' Description     : ISpeechVoice Interface
            ' Class Name      : SpVoice
            ' ClassID         : $CLSID_SpeechLib_SpVoice
            ' ProgID          : $PROGID_SpeechLib_SpVoice
            ' Version ProgID  : $PROGID_SpeechLib_SpVoice1
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechVoice $IID_SpeechLib_ISpeechVoice
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET STATUS < 1 >( ) AS ISpeechVoiceStatus
            PROPERTY GET Voice < 2 >( ) AS ISpeechObjectToken
            PROPERTY SET PutRef_Voice < 2 >( BYVAL Voice AS ISpeechObjectToken )
            PROPERTY GET AudioOutput < 3 >( ) AS ISpeechObjectToken
            PROPERTY SET PutRef_AudioOutput < 3 >( BYVAL AudioOutput AS ISpeechObjectToken )
            PROPERTY GET AudioOutputStream < 4 >( ) AS ISpeechBaseStream
            PROPERTY SET PutRef_AudioOutputStream < 4 >( BYVAL AudioOutputStream AS ISpeechBaseStream )
            PROPERTY GET Rate < 5 >( ) AS LONG
            PROPERTY SET Rate < 5 >( BYVAL Rate AS LONG )
            PROPERTY GET Volume < 6 >( ) AS LONG
            PROPERTY SET Volume < 6 >( BYVAL Volume AS LONG )
            PROPERTY SET AllowAudioOutputFormatChangesOnNextSet < 7 >( BYVAL Allow AS INTEGER )
            PROPERTY GET AllowAudioOutputFormatChangesOnNextSet < 7 >( ) AS INTEGER
            PROPERTY GET EventInterests < 8 >( ) AS LONG
            PROPERTY SET EventInterests < 8 >( BYVAL EventInterestFlags AS LONG )
            PROPERTY SET PRIORITY < 9 >( BYVAL Priority AS LONG )
            PROPERTY GET PRIORITY < 9 >( ) AS LONG
            PROPERTY SET AlertBoundary < 10 >( BYVAL Boundary AS LONG )
            PROPERTY GET AlertBoundary < 10 >( ) AS LONG
            PROPERTY SET SynchronousSpeakTimeout < 11 >( BYVAL msTimeout AS LONG )
            PROPERTY GET SynchronousSpeakTimeout < 11 >( ) AS LONG
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD Speak < 12 >( BYVAL TEXT AS WSTRING, OPT BYVAL Flags AS LONG ) AS LONG
            METHOD SpeakStream < 13 >( BYVAL Stream AS ISpeechBaseStream, OPT BYVAL Flags AS LONG ) AS LONG
            METHOD Pause < 14 >( )
            METHOD RESUME < 15 >( )
            METHOD Skip < 16 >( BYVAL PB_Type AS WSTRING, BYVAL NumItems AS LONG ) AS LONG
            METHOD GetVoices < 17 >( OPT BYVAL RequiredAttributes AS WSTRING, OPT BYVAL OptionalAttributes AS WSTRING ) AS _
              ISpeechObjectTokens
            METHOD GetAudioOutputs < 18 >( OPT BYVAL RequiredAttributes AS WSTRING, OPT BYVAL OptionalAttributes AS WSTRING ) AS _
              ISpeechObjectTokens
            METHOD WaitUntilDone < 19 >( BYVAL msTimeout AS LONG ) AS INTEGER
            METHOD SpeakCompleteEvent < 20 >( ) AS LONG
            METHOD IsUISupported < 21 >( BYVAL TypeOfUI AS WSTRING, OPT BYREF IN ExtraData AS VARIANT ) AS INTEGER
            METHOD DisplayUI < 22 >( BYVAL hWndParent AS LONG, BYVAL Title AS WSTRING, BYVAL TypeOfUI AS WSTRING, OPT BYREF IN _
              ExtraData AS VARIANT )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechWaveFormatEx
            ' Description     : ISpeechWaveFormatEx Interface
            ' Class Name      : SpWaveFormatEx
            ' ClassID         : $CLSID_SpeechLib_SpWaveFormatEx
            ' ProgID          : $PROGID_SpeechLib_SpWaveFormatEx
            ' Version ProgID  : $PROGID_SpeechLib_SpWaveFormatEx1
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechWaveFormatEx $IID_SpeechLib_ISpeechWaveFormatEx
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET FormatTag < 1 >( ) AS INTEGER
            PROPERTY SET FormatTag < 1 >( BYVAL FormatTag AS INTEGER )
            PROPERTY GET Channels < 2 >( ) AS INTEGER
            PROPERTY SET Channels < 2 >( BYVAL Channels AS INTEGER )
            PROPERTY GET SamplesPerSec < 3 >( ) AS LONG
            PROPERTY SET SamplesPerSec < 3 >( BYVAL SamplesPerSec AS LONG )
            PROPERTY GET AvgBytesPerSec < 4 >( ) AS LONG
            PROPERTY SET AvgBytesPerSec < 4 >( BYVAL AvgBytesPerSec AS LONG )
            PROPERTY GET BlockAlign < 5 >( ) AS INTEGER
            PROPERTY SET BlockAlign < 5 >( BYVAL BlockAlign AS INTEGER )
            PROPERTY GET BitsPerSample < 6 >( ) AS INTEGER
            PROPERTY SET BitsPerSample < 6 >( BYVAL BitsPerSample AS INTEGER )
            PROPERTY GET ExtraData < 7 >( ) AS VARIANT
            PROPERTY SET ExtraData < 7 >( BYVAL ExtraData AS VARIANT )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechAudioFormat
            ' Description     : ISpeechAudioFormat Interface
            ' Class Name      : SpAudioFormat
            ' ClassID         : $CLSID_SpeechLib_SpAudioFormat
            ' ProgID          : $PROGID_SpeechLib_SpAudioFormat
            ' Version ProgID  : $PROGID_SpeechLib_SpAudioFormat1
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechAudioFormat $IID_SpeechLib_ISpeechAudioFormat
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET TYPE < 1 >( ) AS LONG
            PROPERTY SET TYPE < 1 >( BYVAL AudioFormat AS LONG )
            PROPERTY GET GUID < 2 >( ) AS WSTRING
            PROPERTY SET GUID < 2 >( BYVAL PB_Guid AS WSTRING )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD GetWaveFormatEx < 3 >( ) AS ISpeechWaveFormatEx
            METHOD SetWaveFormatEx < 4 >( BYVAL PB_WaveFormatEx AS ISpeechWaveFormatEx )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' Interface Name  : ISpeechFileStream
            ' Description     : ISpeechFileStream Interface
            ' Class Name      : SpFileStream
            ' ClassID         : $CLSID_SpeechLib_SpFileStream
            ' ProgID          : $PROGID_SpeechLib_SpFileStream
            ' Version ProgID  : $PROGID_SpeechLib_SpFileStream1
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            INTERFACE ISpeechFileStream $IID_SpeechLib_ISpeechFileStream
            INHERIT IDISPATCH
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            PROPERTY GET Format < 1 >( ) AS ISpeechAudioFormat
            PROPERTY SET PutRef_Format < 1 >( BYVAL Rhs AS ISpeechAudioFormat )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            METHOD READ < 2 >( BYREF Buffer AS VARIANT, BYVAL NumberOfBytes AS LONG ) AS LONG
            METHOD WRITE < 3 >( BYVAL Buffer AS VARIANT ) AS LONG
            METHOD SEEK < 4 >( BYVAL Position AS VARIANT, OPT BYVAL Origin AS LONG ) AS VARIANT
            METHOD OPEN < 100 >( BYVAL FileName AS WSTRING, OPT BYVAL FileMode AS LONG, OPT BYVAL PB_DoEvents AS INTEGER )
            METHOD CLOSE < 101 >( )
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            END INTERFACE
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            ' SpSetVoice
            ' Sets the voice used by the speech engine.
            ' ========================================================================================
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            #IF 1 = 0
            FUNCTION SpSetVoice( _
                  BYVAL pISpVoice AS ISpVoice, _        ' Reference to the IspVoice interface
                  BYVAL bstrVoice AS WSTRING _        ' ANSI STRING - Voice to set, e.g. "Microsoft Mary"
                  ) AS LONG        ' HRESULT
                LOCAL hr AS LONG
                LOCAL pIEnumSpObjectTokens AS IEnumSpObjectTokens
                LOCAL nCount AS DWORD
                LOCAL i AS DWORD
                LOCAL pISpObjectToken AS ISpObjectToken
                LOCAL celtFetched AS DWORD
                LOCAL pszValue AS WSTRINGZ PTR
                LOCAL bstrDesc AS WSTRING
                IF ISNOTHING( pISpVoice ) THEN FUNCTION = %E_POINTER : EXIT FUNCTION
                ' // Get a reference to an enumerator for the voices collection
                ' // using the helper function SpEnumTokens
                hr = SpEnumTokens( $$SPCAT_VOICES, BYVAL %NULL, BYVAL %NULL, pIEnumSpObjectTokens )
                ' // Parse the collection
                IF SUCCEEDED( hr ) THEN
                    pIEnumSpObjectTokens.GetCount( nCount )
                    FOR i = 0 TO nCount - 1
                        hr = pIEnumSpObjectTokens.Next( 1, pISpObjectToken, celtFetched )
                        IF FAILED( hr ) OR celtFetched = 0 THEN EXIT FOR
                        hr = SpGetDescription( pISpObjectToken, pszValue )
                        IF hr = %S_OK AND pszValue <> %NULL THEN
                            bstrDesc = @pszValue
                            CoTaskMemFree BYVAL pszValue
                            IF bstrDesc = bstrVoice THEN
                                pISpVoice.SetVoice pISpObjectToken
                                pISpObjectToken = NOTHING
                                EXIT FOR
                            END IF
                        END IF
                        pISpObjectToken = NOTHING
                    NEXT
                    pIEnumSpObjectTokens = NOTHING
                END IF
                FUNCTION = hr
            END FUNCTION
            #ENDIF
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            'END OF EXTRACT
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            FUNCTION funGetVoices( ) AS STRING
                ' return the list of voices
                LOCAL vRes AS VARIANT
                LOCAL vTxt AS VARIANT
                LOCAL vTime AS VARIANT
                LOCAL oTokens AS DISPATCH
                LOCAL oToken AS DISPATCH
                LOCAL vToken AS VARIANT
                LOCAL i AS LONG
                LOCAL vIdx AS VARIANT
                LOCAL nCount AS LONG
                LOCAL strDesc AS STRING
                LOCAL strVoiceList AS STRING
                DIM oVoice AS DISPATCH
                LOCAL liCount AS LONG
                strVoiceList = ""
                IF ISFALSE ISOBJECT( oVoice ) THEN
                    LET oVoice = NEWCOM "SAPI.SpVoice"
                    IF ISFALSE ISOBJECT( oVoice ) THEN
                        EXIT FUNCTION
                    END IF
                END IF
                'Get a reference to the SAPI ISpeechObjectTokens collection
                OBJECT CALL oVoice.GetVoices( ) TO vRes
                IF ISFALSE OBJRESULT THEN
                    SET oTokens = vRes
                    vRes = EMPTY
                    'Get the number of tokens
                    OBJECT GET oTokens.Count TO vRes
                    nCount = VARIANT#( vRes )
                    'Parse the collection (zero based)
                    liCount = 0
                    FOR i = 0 TO nCount        ' - 1
                        vIdx = i AS LONG
                        ' Get the item by his index
                        OBJECT CALL oTokens.Item( vIdx ) TO vRes
                        IF ISFALSE OBJRESULT THEN
                            SET oToken = vRes
                            vRes = EMPTY
                            IF ISFALSE OBJRESULT THEN
                                'Get the description
                                OBJECT CALL oToken.GetDescription( ) TO vRes
                                IF ISFALSE OBJRESULT THEN
                                    strDesc = VARIANT$( vRes )
                                    IF strDesc = "TOSHIBA male adult (U.S.)" THEN
                                        GOTO OverThere
                                    ELSE
                                        IF liCount < = 8 THEN
                                            strDesc = "00" + TRIM$( STR$( liCount + 1 )) + ".  " + strDesc
                                        END IF
                                        IF liCount > = 8 AND liCount < = 98 THEN
                                            strDesc = "0" + TRIM$( STR$( liCount + 1 )) + ".  " + strDesc
                                        END IF
                                        strVoiceList = strVoiceList & strDesc & "|"
                                        INCR liCount
                                    END IF
            OverThere :
                                END IF
                            END IF
                            SET oToken = NOTHING
                        END IF
                    NEXT i
                    SET oTokens = NOTHING
                END IF
                strVoiceList = RTRIM$( strVoiceList, "|" )
                FUNCTION = strVoiceList
            END FUNCTION
            
            
            
            '----------------------------------------------------------------------------(')
            
            
            
            FUNCTION PBMAIN( ) AS LONG
                DIM wszFile AS WSTRINGZ * %MAX_PATH
                DIM oFileStream AS ISpeechFileStream
                DIM oVoice AS ISpeechVoice
                MSGBOX funGetVoices
                LET oVoice = NEWCOM $PROGID_SpeechLib_SpVoice
                IF ISFALSE ISOBJECT( oVoice ) THEN
                    MSGBOX "Cannot get object: oVoice"
                    EXIT FUNCTION
                END IF
                wszFile = CURDIR$ + "\TTS_Test.wav"
                LET oFileStream = NEWCOM $PROGID_SpeechLib_SpFileStream
                IF ISFALSE ISOBJECT( oFileStream ) THEN
                    SET oVoice = NOTHING
                    MSGBOX "Cannot get object: oFileStream"
                    EXIT FUNCTION
                END IF
                oFileStream.Open wszFile, %SpeechStreamFileMode.SSFMCreateForWrite, %TRUE
                oVoice.PutRef_AudioOutputStream = oFileStream
                oVoice.Speak "this is the default voice"
                oFileStream.Close
                SET oVoice = NOTHING
                SET oFileStream = NOTHING
                '
            END FUNCTION

            Comment


            • #7
              Steve see this link...
              https://forum.powerbasic.com/forum/u...ice#post772928

              The solution to your issue is only a click away. Enjoy
              jimbo

              Comment


              • #8
                Here is another one using a lot of José stuff and includes.
                A little bit of Gary's stuff, and a little bit of mine.

                As is... Yours to double check...

                Click image for larger version  Name:	TextToSpeechToWave02.png Views:	1 Size:	69.7 KB ID:	774154

                Code:
                'Use José Roca includes
                #COMPILE EXE '#Win 9.07 (D:\Basic\Bas\Jose Roca\Forum\Jose\Windows API Headers\1.19 (PB9.x)\uz)#
                #DIM ALL
                #REGISTER NONE
                #INCLUDE "Win32Api.inc"
                #INCLUDE "CommCtrl.inc"
                #INCLUDE "Ole2Utils.inc"
                #INCLUDE "Sapi.inc"
                #IF %PB_REVISION < &H1000
                #INCLUDE "SapiUtils.inc"
                #ENDIF
                #RESOURCE "SomeResource.pbr"
                
                %SVSFParseAutodetect = &H0   '0
                %SVSFParseSapi       = &H80  '128
                %SVSFParseSsml       = &H100 '256
                %SVSFParseMask       = &H180 '384
                
                $PROGID_SAPISpFileStream  = "SAPI.SpFileStream"
                $PROGID_SAPISpFileStream1 = "SAPI.SpFileStream.1"
                %SPF_ASYNC                = 1
                %SVSFlagsAsync            = %SPF_ASYNC
                %NotUsed                  = 0
                
                GLOBAL hDlg    AS DWORD
                GLOBAL DoSpeak AS DWORD
                GLOBAL DoPause AS DWORD
                
                $AppName        = "TextToSpeechToWave"
                %LabelVoice     = 101
                %LabelFormat    = 102
                %LabelVolume    = 103
                %LabelSpeed     = 104
                %LabelText      = 105
                %LabelLangId    = 106
                
                %ListboxVoice   = 201
                
                %TextboxText    = 301
                
                %ButtonSpeak    = 401
                %ButtonWave     = 402
                %ButtonStop     = 403
                %ButtonPause    = 404
                %ButtonSapiCpl  = 405
                %ButtonExit     = 406
                
                %TrackbarVolume = 501
                %TrackbarSpeed  = 502
                
                %FrameVolume    = 601
                %FrameSpeed     = 602
                
                %ComboFormat    = 701
                %ComboLangId    = 702
                
                %PauseIsOff     = 0
                %PauseSetItOn   = 1
                %PauseSetItOff  = 2
                %PauseIsOn      = 3
                
                $SPCAT_VOICES   = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices"
                $SPCAT_VOICES10 = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices"
                '_____________________________________________________________________________
                
                FUNCTION pUnicodeToAnsi(BYVAL pUnicode AS DWORD)AS STRING
                 LOCAL CharCount AS DWORD
                 LOCAL sAnsi     AS STRING
                
                 IF pUnicode THEN 'Avoid gpf
                   CharCount = LStrlenW(BYVAL pUnicode) 'Get dual bytes character count
                   sAnsi     = NUL$(CharCount) 'Initialize string
                   WideCharToMultiByte(%CP_ACP, %NULL, BYVAL pUnicode, CharCount, _
                                       BYVAL STRPTR(sAnsi), CharCount, BYVAL %NULL, BYVAL %NULL)
                   FUNCTION = sAnsi
                 END IF
                
                END FUNCTION
                '_____________________________________________________________________________
                
                SUB FormatComboFill()
                 DIM SpeechFormat(4 TO 68) AS STRING
                 LOCAL Looper              AS LONG
                
                 SpeechFormat(04) = "SAFT8kHz8BitMono"
                 SpeechFormat(05) = "SAFT8kHz8BitStereo"
                 SpeechFormat(06) = "SAFT8kHz16BitMono"
                 SpeechFormat(07) = "SAFT8kHz16BitStereo"
                 SpeechFormat(08) = "SAFT11kHz8BitMono"
                 SpeechFormat(09) = "SAFT11kHz8BitStereo"
                 SpeechFormat(10) = "SAFT11kHz16BitMono"
                 SpeechFormat(11) = "SAFT11kHz16BitStereo"
                 SpeechFormat(12) = "SAFT12kHz8BitMono"
                 SpeechFormat(13) = "SAFT12kHz8BitStereo"
                 SpeechFormat(14) = "SAFT12kHz16BitMono"
                 SpeechFormat(15) = "SAFT12kHz16BitStereo"
                 SpeechFormat(16) = "SAFT16kHz8BitMono"
                 SpeechFormat(17) = "SAFT16kHz8BitStereo"
                 SpeechFormat(18) = "SAFT16kHz16BitMono"
                 SpeechFormat(19) = "SAFT16kHz16BitStereo"
                 SpeechFormat(20) = "SAFT22kHz8BitMono"
                 SpeechFormat(21) = "SAFT22kHz8BitStereo"
                 SpeechFormat(22) = "SAFT22kHz16BitMono"
                 SpeechFormat(23) = "SAFT22kHz16BitStereo"
                 SpeechFormat(24) = "SAFT24kHz8BitMono"
                 SpeechFormat(25) = "SAFT24kHz8BitStereo"
                 SpeechFormat(26) = "SAFT24kHz16BitMono"
                 SpeechFormat(27) = "SAFT24kHz16BitStereo"
                 SpeechFormat(28) = "SAFT32kHz8BitMono"
                 SpeechFormat(29) = "SAFT32kHz8BitStereo"
                 SpeechFormat(30) = "SAFT32kHz16BitMono"
                 SpeechFormat(31) = "SAFT32kHz16BitStereo"
                 SpeechFormat(32) = "SAFT44kHz8BitMono"
                 SpeechFormat(33) = "SAFT44kHz8BitStereo"
                 SpeechFormat(34) = "SAFT44kHz16BitMono"
                 SpeechFormat(35) = "SAFT44kHz16BitStereo"
                 SpeechFormat(36) = "SAFT48kHz8BitMono"
                 SpeechFormat(37) = "SAFT48kHz8BitStereo"
                 SpeechFormat(38) = "SAFT48kHz16BitMono"
                 SpeechFormat(39) = "SAFT48kHz16BitStereo"
                 SpeechFormat(40) = "SAFTTrueSpeech_8kHz1BitMono"
                 SpeechFormat(41) = "SAFTCCITT_ALaw_8kHzMono"
                 SpeechFormat(42) = "SAFTCCITT_ALaw_8kHzStereo"
                 SpeechFormat(43) = "SAFTCCITT_ALaw_11kHzMono"
                 SpeechFormat(44) = "SAFTCCITT_ALaw_11kHzStereo"
                 SpeechFormat(45) = "SAFTCCITT_ALaw_22kHzMono"
                 SpeechFormat(46) = "SAFTCCITT_ALaw_22kHzStereo"
                 SpeechFormat(47) = "SAFTCCITT_ALaw_44kHzMono"
                 SpeechFormat(48) = "SAFTCCITT_ALaw_44kHzStereo"
                 SpeechFormat(49) = "SAFTCCITT_uLaw_8kHzMono"
                 SpeechFormat(50) = "SAFTCCITT_uLaw_8kHzStereo"
                 SpeechFormat(51) = "SAFTCCITT_uLaw_11kHzMono"
                 SpeechFormat(52) = "SAFTCCITT_uLaw_11kHzStereo"
                 SpeechFormat(53) = "SAFTCCITT_uLaw_22kHzMono"
                 SpeechFormat(54) = "SAFTCCITT_uLaw_22kHzStereo"
                 SpeechFormat(55) = "SAFTCCITT_uLaw_44kHzMono"
                 SpeechFormat(56) = "SAFTCCITT_uLaw_44kHzStereo"
                 SpeechFormat(57) = "SAFTADPCM_8kHzMono"
                 SpeechFormat(58) = "SAFTADPCM_8kHzStereo"
                 SpeechFormat(59) = "SAFTADPCM_11kHzMono"
                 SpeechFormat(60) = "SAFTADPCM_11kHzStereo"
                 SpeechFormat(61) = "SAFTADPCM_22kHzMono"
                 SpeechFormat(62) = "SAFTADPCM_22kHzStereo"
                 SpeechFormat(63) = "SAFTADPCM_44kHzMono"
                 SpeechFormat(64) = "SAFTADPCM_44kHzStereo"
                 SpeechFormat(65) = "SAFTGSM610_8kHzMono"
                 SpeechFormat(66) = "SAFTGSM610_11kHzMono"
                 SpeechFormat(67) = "SAFTGSM610_22kHzMono"
                 SpeechFormat(68) = "SAFTGSM610_44kHzMono"
                
                 FOR Looper = 4 TO 68
                   COMBOBOX ADD hDlg, %ComboFormat, SpeechFormat(Looper)
                 NEXT
                
                END SUB
                '______________________________________________________________________________
                
                FUNCTION TabExpand(BYVAL hControl AS DWORD, BYVAL sTab AS STRING, BYVAL TabLen AS LONG)AS STRING
                 'Unprecise way to do tab expansion via spaces
                 LOCAL hDC      AS DWORD
                 LOCAL hFontOld AS DWORD
                 LOCAL hFont    AS DWORD
                 LOCAL SizeTxt  AS SIZEL
                 LOCAL SizeSpc  AS SIZEL
                 LOCAL sTxt     AS STRING
                 LOCAL sSpace   AS STRING
                
                 sSpace   = $SPC
                 hDC      = GetDC(hControl)
                 hFont    = SendMessage(hControl, %WM_GETFONT, 0, 0) 'Use the control font to measure strings
                 hFontOld = SelectObject(hDC, hFont) 'Use the control font to measure strings
                 GetTextExtentPoint32(hDC, BYVAL STRPTR(sSpace), LEN(sSpace), SizeSpc) 'Measure a single space width
                 sTxt = RTRIM$(PARSE$(sTab, $TAB, 1))
                 GetTextExtentPoint32(hDC, BYVAL STRPTR(sTxt), LEN(sTxt), SizeTxt) 'Measure what's before the TAB character
                 SelectObject(hDC, hFontOld)
                 ReleaseDC(hControl, hDC)
                 FUNCTION = sTxt & SPACE$(TabLen * SizeSpc.cx - SizeTxt.cx \ SizeSpc.cx) & PARSE$(sTab, $TAB, 2) 'Compute number of space to align the text after the TAB character
                
                END FUNCTION
                '______________________________________________________________________________
                
                SUB LangIdComboFill()
                 DIM LangId(0 TO 125) AS STRING
                 LOCAL Looper         AS LONG
                
                 'Locale language                                    ID in hex
                 LangId(000) = "Afrikaans                    " & $TAB & "0436"
                 LangId(001) = "Albanian                     " & $TAB & "041c"
                 LangId(002) = "Arabic_Saudi_Arabia          " & $TAB & "0401"
                 LangId(003) = "Arabic_Iraq                  " & $TAB & "0801"
                 LangId(004) = "Arabic_Egypt                 " & $TAB & "0c01"
                 LangId(005) = "Arabic_Libya                 " & $TAB & "1001"
                 LangId(006) = "Arabic_Algeria               " & $TAB & "1401"
                 LangId(007) = "Arabic_Morocco               " & $TAB & "1801"
                 LangId(008) = "Arabic_Tunisia               " & $TAB & "1c01"
                 LangId(009) = "Arabic_Oman                  " & $TAB & "2001"
                 LangId(010) = "Arabic_Yemen                 " & $TAB & "2401"
                 LangId(011) = "Arabic_Syria                 " & $TAB & "2801"
                 LangId(012) = "Arabic_Jordan                " & $TAB & "2c01"
                 LangId(013) = "Arabic_Lebanon               " & $TAB & "3001"
                 LangId(014) = "Arabic_Kuwait                " & $TAB & "3401"
                 LangId(015) = "Arabic_UAE                   " & $TAB & "3801"
                 LangId(016) = "Arabic_Bahrain               " & $TAB & "3c01"
                 LangId(017) = "Arabic_Qatar                 " & $TAB & "4001"
                 LangId(018) = "Armenian                     " & $TAB & "042b"
                 LangId(019) = "Azeri_Latin                  " & $TAB & "042c"
                 LangId(020) = "Azeri_Cyrillic               " & $TAB & "082c"
                 LangId(021) = "Basque                       " & $TAB & "042d"
                 LangId(022) = "Belarusian                   " & $TAB & "0423"
                 LangId(023) = "Bulgarian                    " & $TAB & "0402"
                 LangId(024) = "Catalan                      " & $TAB & "0403"
                 LangId(025) = "Chinese_Taiwan               " & $TAB & "0404"
                 LangId(026) = "Chinese_PRC                  " & $TAB & "0804"
                 LangId(027) = "Chinese_Hong_Kong            " & $TAB & "0c04"
                 LangId(028) = "Chinese_Singapore            " & $TAB & "1004"
                 LangId(029) = "Chinese_Macau                " & $TAB & "1404"
                 LangId(030) = "Croatian                     " & $TAB & "041a"
                 LangId(031) = "Czech                        " & $TAB & "0405"
                 LangId(032) = "Danish                       " & $TAB & "0406"
                 LangId(033) = "Dutch_Standard               " & $TAB & "0413"
                 LangId(034) = "Dutch_Belgian                " & $TAB & "0813"
                 LangId(035) = "English_United_States        " & $TAB & "0409"
                 LangId(036) = "English_United_Kingdom       " & $TAB & "0809"
                 LangId(037) = "English_Australian           " & $TAB & "0c09"
                 LangId(038) = "English_Canadian             " & $TAB & "1009"
                 LangId(039) = "English_New_Zealand          " & $TAB & "1409"
                 LangId(040) = "English_Irish                " & $TAB & "1809"
                 LangId(041) = "English_South_Africa         " & $TAB & "1c09"
                 LangId(042) = "English_Jamaica              " & $TAB & "2009"
                 LangId(043) = "English_Caribbean            " & $TAB & "2409"
                 LangId(044) = "English_Belize               " & $TAB & "2809"
                 LangId(045) = "English_Trinidad             " & $TAB & "2c09"
                 LangId(046) = "English_Zimbabwe             " & $TAB & "3009"
                 LangId(047) = "English_Philippines          " & $TAB & "3409"
                 LangId(048) = "Estonian                     " & $TAB & "0425"
                 LangId(049) = "Faeroese                     " & $TAB & "0438"
                 LangId(050) = "Farsi                        " & $TAB & "0429"
                 LangId(051) = "Finnish                      " & $TAB & "040b"
                 LangId(052) = "French_Standard              " & $TAB & "040c"
                 LangId(053) = "French_Belgian               " & $TAB & "080c"
                 LangId(054) = "French_Canadian              " & $TAB & "0c0c"
                 LangId(055) = "French_Swiss                 " & $TAB & "100c"
                 LangId(056) = "French_Luxembourg            " & $TAB & "140c"
                 LangId(057) = "French_Monaco                " & $TAB & "180c"
                 LangId(058) = "Georgian                     " & $TAB & "0437"
                 LangId(059) = "German_Standard              " & $TAB & "0407"
                 LangId(060) = "German_Swiss                 " & $TAB & "0807"
                 LangId(061) = "German_Austrian              " & $TAB & "0c07"
                 LangId(062) = "German_Luxembourg            " & $TAB & "1007"
                 LangId(063) = "German_Liechtenstein         " & $TAB & "1407"
                 LangId(064) = "Greek                        " & $TAB & "0408"
                 LangId(065) = "Hebrew                       " & $TAB & "040d"
                 LangId(066) = "Hindi                        " & $TAB & "0439"
                 LangId(067) = "Hungarian                    " & $TAB & "040e"
                 LangId(068) = "Icelandic                    " & $TAB & "040f"
                 LangId(069) = "Indonesian                   " & $TAB & "0421"
                 LangId(070) = "Italian_Standard             " & $TAB & "0410"
                 LangId(071) = "Italian_Swiss                " & $TAB & "0810"
                 LangId(072) = "Japanese                     " & $TAB & "0411"
                 LangId(073) = "Kazakh                       " & $TAB & "043f"
                 LangId(074) = "Konkani                      " & $TAB & "0457"
                 LangId(075) = "Korean                       " & $TAB & "0412"
                 LangId(076) = "Latvian                      " & $TAB & "0426"
                 LangId(077) = "Lithuanian                   " & $TAB & "0427"
                 LangId(078) = "FYRO Macedonian              " & $TAB & "042f"
                 LangId(079) = "Malay_Malaysia               " & $TAB & "043e"
                 LangId(080) = "Malay_Brunei_Darussalam      " & $TAB & "083e"
                 LangId(081) = "Marathi                      " & $TAB & "044e"
                 LangId(082) = "Norwegian_Bokmal             " & $TAB & "0414"
                 LangId(083) = "Norwegian_Nynorsk            " & $TAB & "0814"
                 LangId(084) = "Polish                       " & $TAB & "0415"
                 LangId(085) = "Portuguese_Brazilian         " & $TAB & "0416"
                 LangId(086) = "Portuguese_Standard          " & $TAB & "0816"
                 LangId(087) = "Romanian                     " & $TAB & "0418"
                 LangId(088) = "Russian                      " & $TAB & "0419"
                 LangId(089) = "Sanskrit                     " & $TAB & "044f"
                 LangId(090) = "Serbian_Latin                " & $TAB & "081a"
                 LangId(091) = "Serbian_Cyrillic             " & $TAB & "0c1a"
                 LangId(092) = "Slovak                       " & $TAB & "041b"
                 LangId(093) = "Slovenian                    " & $TAB & "0424"
                 LangId(094) = "Spanish_Traditional_Sort     " & $TAB & "040a"
                 LangId(095) = "Spanish_Mexican              " & $TAB & "080a"
                 LangId(096) = "Spanish_Modern_Sort          " & $TAB & "0c0a"
                 LangId(097) = "Spanish_Guatemala            " & $TAB & "100a"
                 LangId(098) = "Spanish_Costa_Rica           " & $TAB & "140a"
                 LangId(099) = "Spanish_Panama               " & $TAB & "180a"
                 LangId(100) = "Spanish_Dominican_Republic   " & $TAB & "1c0a"
                 LangId(101) = "Spanish_Venezuela            " & $TAB & "200a"
                 LangId(102) = "Spanish_Colombia             " & $TAB & "240a"
                 LangId(103) = "Spanish_Peru                 " & $TAB & "280a"
                 LangId(104) = "Spanish_Argentina            " & $TAB & "2c0a"
                 LangId(105) = "Spanish_Ecuador              " & $TAB & "300a"
                 LangId(106) = "Spanish_Chile                " & $TAB & "340a"
                 LangId(107) = "Spanish_Uruguay              " & $TAB & "380a"
                 LangId(108) = "Spanish_Paraguay             " & $TAB & "3c0a"
                 LangId(109) = "Spanish_Bolivia              " & $TAB & "400a"
                 LangId(110) = "Spanish_El_Salvador          " & $TAB & "440a"
                 LangId(111) = "Spanish_Honduras             " & $TAB & "480a"
                 LangId(112) = "Spanish_Nicaragua            " & $TAB & "4c0a"
                 LangId(113) = "Spanish_Puerto_Rico          " & $TAB & "500a"
                 LangId(114) = "Swahili                      " & $TAB & "0441"
                 LangId(115) = "Swedish                      " & $TAB & "041d"
                 LangId(116) = "Swedish_Finland              " & $TAB & "081d"
                 LangId(117) = "Tamil                        " & $TAB & "0449"
                 LangId(118) = "Tatar                        " & $TAB & "0444"
                 LangId(119) = "Thai                         " & $TAB & "041e"
                 LangId(120) = "Turkish                      " & $TAB & "041f"
                 LangId(121) = "Ukrainian                    " & $TAB & "0422"
                 LangId(122) = "Urdu                         " & $TAB & "0420"
                 LangId(123) = "Uzbek_Latin                  " & $TAB & "0443"
                 LangId(124) = "Uzbek_Cyrillic               " & $TAB & "0843"
                 LangId(125) = "Vietnamese                   " & $TAB & "042a"
                 FOR Looper = 0 TO 125
                   LangId(Looper) = TabExpand(GetDlgItem(hDlg, %ComboLangId), RTRIM$(LangId(Looper)), 18)
                   COMBOBOX ADD hDlg, %ComboLangId, LangId(Looper)
                 NEXT
                 COMBOBOX SELECT hDlg, %ComboLangId, 1
                
                END SUB
                '______________________________________________________________________________
                
                FUNCTION FileNameIncrement(sFileName AS STRING) AS STRING
                 LOCAL sFileNameNum  AS STRING
                 LOCAL sFileDir      AS STRING
                 LOCAL sPath         AS STRING
                 LOCAL Num           AS LONG
                 LOCAL NumFound      AS LONG
                 LOCAL NumFoundBig   AS LONG
                 LOCAL LastBackSlash AS LONG
                 LOCAL LastDot       AS LONG
                 LOCAL NumPos        AS LONG
                
                 LastBackSlash = INSTR(-1, sFileName, "\")
                 LastDot       = INSTR(-1, sFileName, ".")
                
                 IF LastDot < LastBackSlash THEN LastDot = 0
                
                 IF LastBackSlash THEN 'Some Path
                   sPath = LEFT$(sFileNAme, LastBackSlash)
                   IF LastDot THEN 'Path and .ext
                     NumPos = LastDot - 1
                   ELSE 'Path but no .ext
                     NumPos = LEN(sFileName) + 1
                   END IF
                 ELSE 'No path
                   IF LastDot THEN 'No path but some .ext
                     NumPos = LastDot - 1
                   ELSE 'No path, no .ext
                     NumPos = LEN(sFileName) + 1
                   END IF
                 END IF
                
                 'Find already existing bigger number
                 sFileNameNum = LEFT$(sFileName, NumPos) & "(????)" & MID$(sFileName, NumPos + 1)
                 sFileDir = DIR$(sFileNameNum)
                 DO WHILE LEN(sFileDir)
                   NumFound = VAL(MID$(sPath & sFileDir, NumPos + 2, 4))
                   IF NumFound > NumFoundBig THEN
                     NumFoundBig = NumFound
                   END IF
                   sFileDir = DIR$
                 LOOP
                 DIR$ CLOSE
                
                 Num = NumFoundBig + 1
                 FUNCTION = LEFT$(sFileName, NumPos) & _
                            "(" & FORMAT$(NumFoundBig + 1, "0000") & ")" & _
                            MID$(sFileName, NumPos + 1)
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION GetLastVisibleLine(BYVAL hEdit AS DWORD, BYREF VisibleLineCount AS LONG) AS LONG
                 LOCAL EditRect                    AS RECT
                 LOCAL BottomLeftPixel             AS POINTL
                 LOCAL CharZeroOffsetAndLineNumber AS DWORD
                 LOCAL CharZeroOffset              AS DWORD
                 LOCAL FirstVisibleLine            AS LONG
                 LOCAL LastVisibleLine             AS LONG
                 LOCAL zClass                      AS ASCIIZ * 25
                
                 SendMessage(hEdit, %EM_GETRECT, %NotUsed, BYVAL VARPTR(EditRect))
                 BottomLeftPixel.Y = (EditRect.nBottom - EditRect.nTop)
                 BottomLeftPixel.X = (EditRect.nRight - EditRect.nLeft)
                
                 GetClassName(hEdit, zClass, SIZEOF(zClass))
                 IF UCASE$(zClass) = "EDIT" THEN
                   'Get first char offset and line number from this pixel position
                   CharZeroOffsetAndLineNumber = SendMessage(hEdit, %EM_CHARFROMPOS, %NotUsed, MAKDWD(BottomLeftPixel.X, BottomLeftPixel.Y))
                   LastVisibleLine = HI(INTEGER, CharZeroOffsetAndLineNumber)
                 ELSE 'RichEdit
                   'Get first char offset from this pixel position
                   CharZeroOffset = SendMessage(hEdit, %EM_CHARFROMPOS, %NotUsed, BYVAL VARPTR(BottomLeftPixel))
                   LastVisibleLine = SendMessage(hEdit, %EM_LINEFROMCHAR, CharZeroOffset, %NotUsed)
                 END IF
                
                 FirstVisibleLine = SendMessage(hEdit, %EM_GETFIRSTVISIBLELINE, %NotUsed, %NotUsed)
                 IF LastVisibleLine < 0 THEN 'Edit height is so small that caret is not shown. Probably due to a window resized to it's minimum.
                   LastVisibleLine = FirstVisibleLine
                 END IF
                 VisibleLineCount = 1 + LastVisibleLine - FirstVisibleLine
                
                 FUNCTION = LastVisibleLine
                
                END FUNCTION
                '_____________________________________________________________________________
                
                FUNCTION EditProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS LONG, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
                 'Subclass edit with a a one liner and no variables needed...
                 'EditProc(0, %WM_NULL, SetWindowLong(hEdit, %GWL_WNDPROC, CODEPTR(EditProc)), 0)
                 LOCAL  Caret                       AS POINTAPI
                 STATIC pEditProc                   AS DWORD
                 LOCAL  CharZeroOffsetAndLineNumber AS DWORD
                 LOCAL  pixXY                       AS DWORD
                 LOCAL  LastVisibleLine             AS LONG
                 LOCAL  VisibleLineCount            AS LONG
                 LOCAL  SelEnd                      AS INTEGER
                 LOCAL  CharZeroOffset              AS INTEGER
                 LOCAL  CurrentLine                 AS INTEGER
                 LOCAL  NextLineFirstChar           AS INTEGER
                 LOCAL  PrevLineFirstChar           AS INTEGER
                 LOCAL  FirstVisibleLine            AS INTEGER
                 LOCAL  LineCount                   AS INTEGER
                 LOCAL  LastLine                    AS INTEGER
                 STATIC HorizontalPixel             AS INTEGER
                 LOCAL  VerticalPixel               AS INTEGER
                 LOCAL  ControlKey                  AS WORD
                 LOCAL  ShiftKey                    AS WORD
                 LOCAL  AltKey                      AS WORD
                
                 SELECT CASE wMsg 'Keyboard Input Notifications
                
                   CASE %WM_NULL
                     IF hWnd = 0 THEN pEditProc = wParam
                
                   CASE %WM_NCDESTROY
                       SetWindowLong(hWnd, %GWL_WNDPROC, pEditProc) 'Unsubclass edit
                
                   CASE %WM_KEYDOWN
                     ControlKey = (LOWRD(GetKeyState(%VK_CONTROL) AND &H8000))
                     ShiftKey   = (LOWRD(GetKeyState(%VK_SHIFT)   AND &H8000))
                     AltKey     = (LOWRD(GetKeyState(%VK_MENU)    AND &H8000))
                
                     SELECT CASE wParam
                
                       CASE %VK_UP 'Up arrow pressed
                         GOSUB GetEditInfo 'Set LineCount, LastLine, CharZeroOffset, Caret(XY), HorizontalPixel, CurrentLine and FirstVisibleLine, also scroll caret into view
                         IF (CurrentLine > 0) AND (ShiftKey = %FALSE) THEN 'Not on first line
                           PrevLineFirstChar = SendMessage(hWnd, %EM_LINEINDEX, CurrentLine - 1, %NotUsed)
                           IF Caret.X < 0 THEN 'Negative, like -20,000, if textbox height is so low that caret is not shown
                             CharZeroOffset = PrevLineFirstChar
                             SendMessage(hWnd, %EM_SETSEL, CharZeroOffset, CharZeroOffset) 'Set caret to offset
                             SendMessage(hWnd, %EM_SCROLLCARET, %NotUsed , %NotUsed)       'Scroll the caret in
                           ELSE 'Caret is visible
                             IF (ControlKey) OR (CurrentLine = FirstVisibleLine) THEN
                               SendMessage(hWnd, %EM_SCROLL, %SB_LINEUP, %NotUsed) 'Scroll text one line down
                             END IF
                             pixXY = SendMessage(hWnd, %EM_POSFROMCHAR, PrevLineFirstChar, %NotUsed) 'Char may not be visible
                             VerticalPixel = HI(INTEGER, pixXY)
                             CharZeroOffsetAndLineNumber = SendMessage(hWnd, %EM_CHARFROMPOS, %NotUsed, _
                                                                       MAKDWD(HorizontalPixel, VerticalPixel)) 'Get offset and line number from this pixel position
                             IF HI(INTEGER, CharZeroOffsetAndLineNumber) < CurrentLine - 1 THEN 'Only when textbox is 1 line height
                               CharZeroOffsetAndLineNumber = SendMessage(hWnd, %EM_CHARFROMPOS, %NotUsed, _
                                                                         MAKDWD(HorizontalPixel, 1)) 'Get offset and line number from this pixel position
                             END IF
                             CharZeroOffset = LO(INTEGER, CharZeroOffsetAndLineNumber)
                             SendMessage(hWnd, %EM_SETSEL, CharZeroOffset, CharZeroOffset) 'Set caret to offset
                           END IF
                           EXIT FUNCTION 'Do not forward key
                         END IF
                
                       CASE %VK_DOWN 'Down arrow pressed
                         GOSUB GetEditInfo 'Set LineCount, LastLine, CharZeroOffset, Caret(XY), HorizontalPixel, CurrentLine and FirstVisibleLine, also scroll caret into view
                         IF (CurrentLine < LastLine) AND (ShiftKey = %FALSE) THEN 'Not on last line
                           NextLineFirstChar = SendMessage(hWnd, %EM_LINEINDEX, CurrentLine + 1, %NotUsed)
                           CharZeroOffset = NextLineFirstChar
                           IF Caret.X >= 0 THEN 'Negative, like -20,000, if textbox height is so low that caret is not shown
                             pixXY = SendMessage(hWnd, %EM_POSFROMCHAR, NextLineFirstChar, %NotUsed)
                             VerticalPixel = HI(INTEGER, pixXY) '-1 if on last line that is empty and at least 2 lines
                             IF VerticalPixel >= 0 THEN
                               CharZeroOffsetAndLineNumber = SendMessage(hWnd, %EM_CHARFROMPOS, %NotUsed, MAKDWD(HorizontalPixel, VerticalPixel)) 'Get offset and line number from this pixel position
                               CharZeroOffset = LO(INTEGER, CharZeroOffsetAndLineNumber)
                             END IF
                           END IF
                           SendMessage(hWnd, %EM_SETSEL, CharZeroOffset, CharZeroOffset) 'Set caret to offset
                           IF ControlKey THEN
                             SendMessage(hWnd, %EM_SCROLL, %SB_LINEDOWN, %NotUsed) 'Scroll text one line down
                           END IF
                           SendMessage(hWnd, %EM_SCROLLCARET, %NotUsed, %NotUsed)  'Scroll the caret into view
                           EXIT FUNCTION                                           'Do not forward key
                         END IF
                
                       CASE %VK_PGDN 'Page down pressed
                         GOSUB GetEditInfo 'Set LineCount, LastLine, CharZeroOffset, Caret(XY), HorizontalPixel, CurrentLine and FirstVisibleLine, also scroll caret into view
                         LastVisibleLine = GetLastVisibleLine(hWnd, VisibleLineCount)
                         IF VisibleLineCount = 1 THEN
                           PostMessage(hWnd, %WM_KEYDOWN, %VK_DOWN, 0)
                           EXIT FUNCTION
                         END IF
                         IF (LastVisibleLine < LastLine) AND (ShiftKey = %FALSE) THEN 'Not on last line
                           SendMessage(hWnd, %EM_SCROLL, %SB_PAGEDOWN, %NotUsed)
                           NextLineFirstChar = SendMessage(hWnd, %EM_LINEINDEX, CurrentLine + VisibleLineCount - 1, %NotUsed)
                           CharZeroOffset = NextLineFirstChar
                           IF Caret.X >= 0 THEN 'Negative, like -20,000, if textbox height is so low that caret is not shown
                             pixXY = SendMessage(hWnd, %EM_POSFROMCHAR, NextLineFirstChar, %NotUsed)
                             VerticalPixel = HI(INTEGER, pixXY) '-1 if on last line that is empty and at least 2 lines
                             IF VerticalPixel >= 0 THEN
                               IF VerticalPixel = 0 THEN VerticalPixel = 1
                               CharZeroOffsetAndLineNumber = SendMessage(hWnd, %EM_CHARFROMPOS, %NotUsed, MAKDWD(HorizontalPixel, VerticalPixel)) 'Get offset and line number from this pixel position
                               CharZeroOffset = LO(INTEGER, CharZeroOffsetAndLineNumber)
                             END IF
                           END IF
                           SendMessage(hWnd, %EM_SETSEL, CharZeroOffset, CharZeroOffset) 'Set caret to offset
                           EXIT FUNCTION 'Do not forward key
                         END IF
                
                       CASE %VK_PGUP
                         GOSUB GetEditInfo 'Set LineCount, LastLine, CharZeroOffset, Caret(XY), HorizontalPixel, CurrentLine and FirstVisibleLine, also scroll caret into view
                         LastVisibleLine = GetLastVisibleLine(hWnd, VisibleLineCount)
                         IF VisibleLineCount = 1 THEN
                           PostMessage(hWnd, %WM_KEYDOWN, %VK_UP, 0)
                           EXIT FUNCTION
                         END IF
                         IF (FirstVisibleLine > 0) AND (ShiftKey = %FALSE) THEN 'Not on last line
                           SendMessage(hWnd, %EM_SCROLL, %SB_PAGEUP, %NotUsed)  'Scroll text one page down
                           PrevLineFirstChar = SendMessage(hWnd, %EM_LINEINDEX, CurrentLine - VisibleLineCount + 1, %NotUsed)
                           CharZeroOffset = PrevLineFirstChar
                           IF Caret.X >= 0 THEN 'Negative, like -20,000, if textbox height is so low that caret is not shown
                             pixXY = SendMessage(hWnd, %EM_POSFROMCHAR, PrevLineFirstChar, %NotUsed)
                             VerticalPixel = HI(INTEGER, pixXY) '-1 if on last line that is empty and at least 2 lines
                             IF VerticalPixel >= 0 THEN
                               IF VerticalPixel = 0 THEN VerticalPixel = 1
                               CharZeroOffsetAndLineNumber = SendMessage(hWnd, %EM_CHARFROMPOS, %NotUsed, MAKDWD(HorizontalPixel, VerticalPixel)) 'Get offset and line number from this pixel position
                               CharZeroOffset = LO(INTEGER, CharZeroOffsetAndLineNumber)
                             END IF
                           END IF
                           SendMessage(hWnd, %EM_SETSEL, CharZeroOffset, CharZeroOffset) 'Set caret to offset
                           EXIT FUNCTION 'Do not forward key
                         END IF
                
                       CASE %VK_LEFT
                         HorizontalPixel = 0
                
                       CASE %VK_RIGHT
                         HorizontalPixel = 0
                
                       CASE %VK_HOME
                         IF ControlKey = %FALSE THEN
                           HorizontalPixel = 0
                         END IF
                
                       CASE %VK_END
                         IF ControlKey = %FALSE THEN
                           HorizontalPixel = 0
                         END IF
                
                     END SELECT
                
                   CASE %WM_GETDLGCODE
                     FUNCTION = %DLGC_WANTALLKEYS
                     EXIT FUNCTION
                
                   CASE %WM_KEYUP
                     SELECT CASE wParam
                       CASE %VK_DOWN 'Down arrow pressed
                         IF ControlKey THEN
                         END IF
                     END SELECT
                
                   CASE %WM_CHAR
                     SELECT CASE wParam
                       CASE 1 'Control-A
                         SendMessage(hWnd, %EM_SETSEL, 0, - 1) 'Select everything
                         EXIT FUNCTION
                     END SELECT
                
                 END SELECT
                
                 FUNCTION = CallWindowProc(pEditProc, hWnd, wMsg, wParam, lParam)
                
                 EXIT FUNCTION '---------------------------------------------------------------
                 'Get LineCount, LastLine, CharZeroOffset, Caret(XY), HorizontalPixel, CurrentLine and FirstVisibleLine, also scroll caret into view
                 GetEditInfo:
                 LineCount = SendMessage(hWnd, %EM_GETLINECOUNT, %NotUsed, %NotUsed) 'Never 0, always 1 or more
                 LastLine  = LineCount - 1
                
                 SendMessage(hWnd, %EM_GETSEL, VARPTR(CharZeroOffset), VARPTR(SelEnd)) 'Get offset pos, zero based, $CRLF included.
                
                 SendMessage(hWnd, %EM_SCROLLCARET, %NotUsed , %NotUsed) 'Scroll the caret into view
                
                 GetCaretPos(Caret) 'Get caret pixel pos, -20,000 if textbox height is so low that caret is not shown
                 IF HorizontalPixel = 0 THEN
                   HorizontalPixel = Caret.X 'Remember for passing throught a blank or shorter line
                 END IF
                
                 CurrentLine = SendMessage(hWnd, %EM_LINEFROMCHAR, CharZeroOffset, %NotUsed)
                 FirstVisibleLine = SendMessage(hWnd, %EM_GETFIRSTVISIBLELINE, %NotUsed, %NotUsed)
                 RETURN
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION ExeName(Action AS LONG) AS STRING 'Return the ExeName of this APP or ExeName of calling app if DLL
                 LOCAL zFileName   AS ASCIIZ * %MAX_PATH
                 LOCAL PathFileLen AS LONG
                 LOCAL FileExtLen  AS LONG
                 LOCAL DotPos      AS LONG
                 LOCAL SlashPos    AS LONG
                
                 PathFileLen = GetModuleFileName(0, zFileName, %MAX_PATH)
                 SlashPos    = INSTR(-1, zFileName, "\")
                 DotPos      = INSTR(-1, zFileName, ".")
                 IF DotPos < SlashPos THEN DotPos = 0
                 FileExtLen = PathFileLen - SlashPos
                
                 SELECT CASE Action
                
                   CASE 1 'C: - Drive
                     IF ASC(zFileName, 2) = 58 THEN 'X: 58
                       FUNCTION = LEFT$(zFileName, 2)
                     END IF
                
                   CASE 2 'C:\Subdir\ - Drive and Path (Including last backslash)
                     FUNCTION = LEFT$(zFileName, SlashPos)
                
                   CASE 3 '\Subdir\ - Path (Including first and last backslash)
                     FUNCTION = MID$(zFileName, 3, SlashPos - 2)
                
                   CASE 4 'C:\Subdir\File - Drive, Path and Filename (No Extention, Dot excluded)
                     IF DotPos THEN
                       FUNCTION = LEFT$(zFileName, DotPos - 1)
                     ELSE
                       FUNCTION = LEFT$(zFileName, PathFileLen)
                     END IF
                
                   CASE 5 'C:\Subdir\File.Exe - Path, Filename and Extention
                     FUNCTION = LEFT$(zFileName, PathFileLen)
                
                   CASE 6 'File - Filename (No Extention)
                     IF DotPos THEN
                       FUNCTION = MID$(zFileName, SlashPos + 1, FileExtLen - (PathFileLen - DotPos) - 1)
                     ELSE
                       FUNCTION = MID$(zFileName, SlashPos + 1, FileExtLen)
                     END IF
                
                   CASE 7 'File.Exe - Filename and Extention
                     FUNCTION = MID$(zFileName, SlashPos + 1, FileExtLen)
                
                   CASE 8 '.Exe - Extention (Including Dot)
                     FUNCTION = MID$(zFileName, DotPos)
                
                 END SELECT
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION VoiceGet(BYVAL sCategoryId AS STRING, BYVAL RegistrySelection AS LONG) AS LONG
                 LOCAL pISpVoice            AS IspVoice
                 LOCAL pIEnumSpObjectTokens AS IEnumSpObjectTokens
                 LOCAL pISpObjectToken      AS ISpObjectToken
                 LOCAL ppToken              AS ISpObjectToken
                 LOCAL sVoice               AS STRING
                 LOCAL sVoiceDefault        AS STRING
                 LOCAL dwCount              AS DWORD
                 LOCAL Looper               AS DWORD
                 LOCAL dwCeltFetched        AS DWORD
                 LOCAL dwValue              AS DWORD
                 LOCAL hr                   AS LONG
                 LOCAL ListboxIndex         AS LONG
                
                 pISpVoice = NEWCOM CLSID $CLSID_SpVoice 'Create an instance of the ISpVoice interface
                 IF ISOBJECT(pISpVoice) THEN
                
                   pISpVoice.GetVoice(ppToken) 'Get default voice
                   hr = SpGetDescription(ppToken, dwValue)
                   IF hr = %S_OK AND dwValue <> %NULL THEN
                     sVoiceDefault = pUnicodeToAnsi(dwValue) 'Get default voice
                     CoTaskMemFree(dwValue)
                   END IF
                   ppToken = NOTHING
                
                   'Get a reference to an enumerator for the voices collection using the helper function SpEnumTokens
                   sCategoryId = UCODE$(sCategoryId & $NUL)
                   hr = SpEnumTokens(BYVAL STRPTR(sCategoryId), BYVAL %NULL, BYVAL %NULL, pIEnumSpObjectTokens)
                   IF hr = %NO_ERROR THEN
                     pIEnumSpObjectTokens.GetCount(dwCount)
                     FOR Looper = 0 TO dwCount - 1
                       hr = pIEnumSpObjectTokens.Next(1, pISpObjectToken, dwCeltFetched)
                       IF FAILED(hr) OR dwCeltFetched = 0 THEN EXIT FOR
                       hr = SpGetDescription(pISpObjectToken, dwValue)
                       IF hr = %S_OK AND dwValue <> %NULL THEN
                         sVoice = pUnicodeToAnsi(dwValue)
                         ListboxIndex = SendDlgItemMessage(hDlg, %ListboxVoice, %LB_ADDSTRING, 0, STRPTR(sVoice))
                         LISTBOX SET USER hDlg, %ListboxVoice, ListboxIndex + 1, RegistrySelection
                         IF pUnicodeToAnsi(dwValue) = sVoiceDefault THEN
                           FUNCTION = ListboxIndex + 1
                         END IF
                         CoTaskMemFree(dwValue)
                       END IF
                       pISpObjectToken = NOTHING
                     NEXT
                     pIEnumSpObjectTokens = NOTHING
                   END IF
                   pISpVoice = NOTHING
                 END IF
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION VoiceSet(sVoice AS STRING, pISpVoice AS ISpVoice, BYVAL RegistrySelection AS LONG) AS LONG
                 LOCAL pIEnumSpObjectTokens AS IEnumSpObjectTokens
                 LOCAL pISpObjectToken      AS ISpObjectToken
                 LOCAL sCategoryId          AS STRING
                 LOCAL hr                   AS LONG
                 LOCAL dwCount              AS DWORD
                 LOCAL Looper               AS DWORD
                 LOCAL dwCeltFetched        AS DWORD
                 LOCAL dwValue              AS DWORD
                
                 IF RegistrySelection = 10 THEN
                   sCategoryId = UCODE$($SPCAT_VOICES10 & $NUL)
                 ELSE
                   sCategoryId = UCODE$($SPCAT_VOICES & $NUL)
                 END IF
                
                 hr = SpEnumTokens(BYVAL STRPTR(sCategoryId), BYVAL %NULL, BYVAL %NULL, pIEnumSpObjectTokens)
                 IF hr = %NO_ERROR THEN
                   pIEnumSpObjectTokens.GetCount(dwCount)
                   FOR Looper = 0 TO dwCount - 1
                     hr = pIEnumSpObjectTokens.Next(1, pISpObjectToken, dwCeltFetched)
                     IF FAILED(hr) OR dwCeltFetched = 0 THEN EXIT FOR
                     hr = SpGetDescription(pISpObjectToken, dwValue)
                     IF hr = %S_OK AND dwValue <> %NULL THEN
                       IF pUnicodeToAnsi(dwValue) = sVoice THEN
                         hr = pISpVoice.SetVoice(pISpObjectToken)
                       END IF
                       CoTaskMemFree(dwValue)
                     END IF
                     pISpObjectToken = NOTHING
                   NEXT
                   pIEnumSpObjectTokens = NOTHING
                 END IF
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION VoiceSetWav(sVoice AS STRING, pISpVoice AS ISpeechVoice, BYVAL RegistrySelection AS LONG) AS LONG
                 LOCAL pIEnumSpObjectTokens AS IEnumSpObjectTokens
                 LOCAL pISpObjectToken      AS ISpObjectToken
                 LOCAL sCategoryId          AS STRING
                 LOCAL hr                   AS LONG
                 LOCAL dwCount              AS DWORD
                 LOCAL Looper               AS DWORD
                 LOCAL dwCeltFetched        AS DWORD
                 LOCAL dwValue              AS DWORD
                
                 IF RegistrySelection = 10 THEN
                   sCategoryId = UCODE$($SPCAT_VOICES10 & $NUL)
                 ELSE
                   sCategoryId = UCODE$($SPCAT_VOICES & $NUL)
                 END IF
                 hr = SpEnumTokens(BYVAL STRPTR(sCategoryId), BYVAL %NULL, BYVAL %NULL, pIEnumSpObjectTokens)
                 IF hr = %NO_ERROR THEN
                   pIEnumSpObjectTokens.GetCount(dwCount)
                   FOR Looper = 0 TO dwCount - 1
                     hr = pIEnumSpObjectTokens.Next(1, pISpObjectToken, dwCeltFetched)
                     IF FAILED(hr) OR dwCeltFetched = 0 THEN EXIT FOR
                     hr = SpGetDescription(pISpObjectToken, dwValue)
                     IF hr = %S_OK AND dwValue <> %NULL THEN
                       IF pUnicodeToAnsi(dwValue) = sVoice THEN
                         pISpVoice.putref_Voice = pISpObjectToken
                         FUNCTION = %TRUE
                       END IF
                       CoTaskMemFree(dwValue)
                     END IF
                     pISpObjectToken = NOTHING
                   NEXT
                   pIEnumSpObjectTokens = NOTHING
                 END IF
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION VoiceToWav() AS LONG
                 LOCAL FileStream        AS ISpeechFileStream
                 LOCAL Voice2            AS ISpeechVoice
                 LOCAL sFileName         AS STRING
                 LOCAL sText             AS STRING
                 LOCAL sBuffer           AS STRING
                 LOCAL wVolume           AS DWORD
                 LOCAL wSpeed            AS DWORD
                 LOCAL wFormat           AS DWORD
                 LOCAL index             AS LONG
                 LOCAL RegistrySelection AS LONG
                
                 FileStream = NEWCOM $PROGID_SAPISpFileStream
                 IF ISOBJECT(FileStream) THEN
                   COMBOBOX GET SELECT hDlg, %ComboFormat TO wFormat
                   wFormat = wFormat + 3
                   FileStream.Format.Type = wFormat
                   sFileName = ExeName(4) & ".wav"
                   sFileName = FileNameIncrement(sFileName)
                
                   #IF %PB_REVISION < &H1000
                   FileStream.Open(UCODE$(sFilename & $NUL), %SSFMCreateForWrite, %TRUE)
                   #ELSE
                   FileStream.Open(sFilename, %SSFMCreateForWrite, %TRUE)
                   #ENDIF
                   Voice2 = NEWCOM CLSID $CLSID_SpVoice
                   IF ISOBJECT(Voice2) THEN
                     LISTBOX GET TEXT hDlg, %ListboxVoice TO sBuffer
                     LISTBOX GET SELECT hDlg, %ListboxVoice TO index
                     LISTBOX GET USER hDlg, %ListboxVoice, index TO RegistrySelection
                     VoiceSetWav(sBuffer, Voice2, RegistrySelection)
                     Voice2.putref_AudioOutputStream = FileStream
                
                     CONTROL GET TEXT hDlg, %LabelVolume TO sBuffer
                     wVolume = VAL(sBuffer)
                     Voice2.Volume = wVolume
                
                     CONTROL GET TEXT hDlg, %LabelSpeed TO sBuffer
                     wSpeed = VAL(sBuffer)
                     Voice2.Rate = wSpeed
                
                     CONTROL GET TEXT hDlg, %TextboxText TO sText
                
                     #IF %PB_REVISION < &H1000
                     Voice2.Speak UCODE$(sText & $NUL)
                     #ELSE
                     Voice2.Speak sText & $NUL
                     #ENDIF
                
                     Voice2.WaitUntilDone(&HFFFFFFFF)
                     Voice2 = NOTHING
                   END IF
                   FileStream.Close
                   FileStream = NOTHING
                
                   LOCAL zFolder  AS ASCIIZ * %MAX_PATH
                   LOCAL zVerb    AS ASCIIZ * 64
                   LOCAL SEI      AS SHELLEXECUTEINFO
                   zVerb            = "OPEN"
                   zFolder          = LEFT$(sFileName, INSTR(-1, sFileName, "\"))
                   SEI.cbSize       = SIZEOF(SEI)
                   SEI.fmask        = %SEE_MASK_NOCLOSEPROCESS OR %SEE_MASK_FLAG_DDEWAIT
                   SEI.hWnd         = %HWND_DESKTOP
                   SEI.lpVerb       = VARPTR(zVerb)
                   SEI.LpFile       = STRPTR(sFileName)
                   SEI.lpDirectory  = VARPTR(zFolder)
                   SEI.nShow        = %SW_SHOWNORMAL
                   SEI.hInstApp     = 0
                   SEI.lpIdList     = %NULL
                   SEI.lpClass      = %NULL
                   SEI.hkeyClass    = %NULL
                   SEI.dwHotKey     = %NULL
                   SEI.hProcess     = 0
                   IF ShellExecuteEx(SEI) THEN
                     SHELL("Explorer.exe /select, " & $DQ & sFileName & $DQ) 'See new file in Windows Explorer
                   END IF
                
                 END IF
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION Speak() AS LONG
                 LOCAL pISpVoice         AS ISpVoice
                 LOCAL eventStatus       AS SPVOICESTATUS
                 LOCAL sText             AS STRING
                 LOCAL wsText            AS STRING
                 LOCAL sBuffer           AS STRING
                 LOCAL ulStreamNumber    AS DWORD
                 LOCAL wVolume           AS DWORD
                 LOCAL wSpeed            AS DWORD
                 LOCAL wFormat           AS DWORD
                 LOCAL index             AS LONG
                 LOCAL ofset             AS LONG
                 LOCAL RegistrySelection AS LONG
                
                
                 pISpVoice = NEWCOM CLSID $CLSID_SpVoice 'Create an instance of the ISpVoice interface
                 IF ISNOTHING(pISpVoice) THEN EXIT FUNCTION
                
                 CONTROL GET TEXT hDlg, %TextboxText TO sText 'Speak some text
                
                 wsText = UCODE$(sText & $NUL)
                
                 LISTBOX GET TEXT hDlg, %ListboxVoice TO sBuffer
                 LISTBOX GET SELECT hDlg, %ListboxVoice TO index
                 LISTBOX GET USER hDlg, %ListboxVoice, index TO RegistrySelection
                 VoiceSet(sBuffer, pISpVoice, RegistrySelection)
                
                 COMBOBOX GET SELECT hDlg, %ComboFormat TO wFormat
                 wFormat = wFormat + 3
                
                 pISpVoice.Speak(BYVAL STRPTR(wsText), %SVSFlagsAsync OR %SVSFIsXML OR %SVSFPersistXML, ulStreamNumber)
                 DO
                   IF DoPause THEN
                     IF DoPause = %PauseSetItOn  THEN pISpVoice.Pause()  : DoPause = %PauseIsOn
                     IF DoPause = %PauseSetItOff THEN pISpVoice.Resume() : DoPause = %PauseIsOff
                   ELSE
                
                     pISpVoice.GetStatus(eventStatus, BYVAL %NULL)
                     CONTROL SEND hDlg, %TextboxText, %EM_SETSEL, eventStatus.ulInputWordPos + ofset, _
                     eventStatus.ulInputWordPos + eventStatus.ulInputWordLen + ofset
                
                     CONTROL SEND hDlg, %TextboxText, %EM_SCROLLCARET, 0, 0
                
                     CONTROL GET TEXT hDlg, %LabelVolume TO sBuffer
                     wVolume = VAL(sBuffer)
                     pISpVoice.SetVolume(wVolume) 'Volume from zero to 100
                
                     CONTROL GET TEXT hDlg, %LabelSpeed TO sBuffer
                     wSpeed = VAL(sBuffer)
                     pISpVoice.SetRate(wSpeed) 'Talking speed from -10 to 10
                
                   END IF
                   IF pISpVoice.WaitUntilDone(50) = %FALSE THEN EXIT DO 'Milisec
                   IF DoSpeak = %FALSE THEN EXIT DO
                   DIALOG DOEVENTS
                 LOOP
                
                 pISpVoice = NOTHING 'Release the interface
                
                END FUNCTION
                '_____________________________________________________________________________
                
                CALLBACK FUNCTION DlgProc
                 STATIC hControlFocus     AS DWORD
                 LOCAL  DefaultVoice      AS LONG
                 LOCAL  index             AS LONG
                 LOCAL  RegistrySelection AS LONG
                
                 SELECT CASE CBMSG
                
                   CASE %WM_INITDIALOG
                     CONTROL SEND CBHNDL, %TrackbarVolume, %TBM_SETRANGE, %TRUE, MAKDWD(0, 100) 'Set range
                     CONTROL SEND CBHNDL, %TrackbarVolume, %TBM_SETTICFREQ, 10, 0               'Set tic frequency
                     CONTROL SEND CBHNDL, %TrackbarVolume, %TBM_SETPAGESIZE, 0, 10              'Set page size
                
                     CONTROL SEND CBHNDL, %TrackbarSpeed, %TBM_SETRANGE, %TRUE, MAKDWD(0, 20)   'Set range
                     CONTROL SEND CBHNDL, %TrackbarSpeed, %TBM_SETTICFREQ, 2, 0                 'Set tic frequency
                     CONTROL SEND CBHNDL, %TrackbarSpeed, %TBM_SETPAGESIZE, 0, 20               'Set page size
                     CONTROL SEND CBHNDL, %TrackbarSpeed, %TBM_SETPOS, %TRUE, 10                'Set initial position
                
                     LangIdComboFill
                     FormatComboFill
                     COMBOBOX SELECT hDlg, %ComboFormat, %SAFT44kHz16BitMono - 3
                
                     DefaultVoice = VoiceGet($SPCAT_VOICES, 0)
                     IF DefaultVoice > 0 THEN LISTBOX SELECT hDlg, %ListboxVoice, DefaultVoice
                     DefaultVoice = VoiceGet($SPCAT_VOICES10, 10)
                     IF DefaultVoice > 0 THEN LISTBOX SELECT hDlg, %ListboxVoice, DefaultVoice
                     CONTROL DISABLE hDlg, %ButtonPause
                
                     'Subclass edit with a a one liner and no variables needed...
                     EditProc(0, %WM_NULL, SetWindowLong(GetDlgItem(hDlg, %TextboxText), %GWL_WNDPROC, CODEPTR(EditProc)), 0)
                
                   CASE %WM_COMMAND
                     SELECT CASE LOWRD(CBWPARAM)
                
                       CASE %ButtonSpeak 'and Stop
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           IF DoSpeak = %FALSE THEN
                             SendDlgItemMessage(hDlg, %TextboxText, %EM_SETREADONLY, %TRUE, 0)
                             CONTROL SET TEXT hDlg, %ButtonSpeak, "&Stop"
                             CONTROL DISABLE hDlg, %ButtonWave
                             CONTROL DISABLE hDlg, %ButtonExit
                             CONTROL DISABLE hDlg, %ComboFormat
                             CONTROL DISABLE hDlg, %ListboxVoice
                             CONTROL ENABLE hDlg, %ButtonPause
                             DoSpeak = %TRUE
                             Speak
                             DoSpeak = %FALSE
                             CONTROL SET TEXT hDlg, %ButtonSpeak, "&Speak"
                             CONTROL ENABLE hDlg, %ButtonWave
                             CONTROL ENABLE hDlg, %ButtonExit
                             CONTROL ENABLE hDlg, %ComboFormat
                             CONTROL ENABLE hDlg, %ListboxVoice
                             CONTROL DISABLE hDlg, %ButtonPause
                             SendDlgItemMessage(hDlg, %TextboxText, %EM_SETREADONLY, %FALSE, 0)
                           ELSE
                             DoSpeak = %FALSE
                           END IF
                         END IF
                
                       CASE %ButtonPause
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           IF DoPause = %PauseIsOff THEN
                             DoPause = %PauseSetItOn
                             CONTROL SET TEXT hDlg, %ButtonPause, "Res&ume"
                             CONTROL DISABLE hDlg, %ButtonSpeak
                           ELSE
                             DoPause = %PauseSetItOff '1 = pause, 2 = resume
                             CONTROL SET TEXT hDlg, %ButtonPause, "Pa&use"
                             CONTROL ENABLE hDlg, %ButtonSpeak
                           END IF
                         END IF
                
                       CASE %ButtonWave
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           CONTROL DISABLE hDlg, %ButtonSpeak
                           CONTROL DISABLE hDlg, %ButtonWave
                           CONTROL DISABLE hDlg, %ButtonExit
                           CONTROL DISABLE hDlg, %TextboxText
                           VoiceToWav
                           CONTROL ENABLE hDlg, %TextboxText
                           CONTROL ENABLE hDlg, %ButtonSpeak
                           CONTROL ENABLE hDlg, %ButtonWave
                           CONTROL ENABLE hDlg, %ButtonExit
                         END IF
                
                       CASE %ButtonSapiCpl
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           LOCAL ProcessId AS DWORD
                           ProcessId = SHELL("rundll32.exe shell32.dll,Control_RunDLL C:\Windows\system32\speech\SpeechUX\SAPI.cpl")
                         END IF
                
                       CASE %ButtonExit
                         IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN
                           DIALOG END hDlg
                         END IF
                
                       CASE %ListboxVoice
                         IF CBCTLMSG = %LBN_SELCHANGE  THEN
                           LISTBOX GET SELECT hDlg, %ListboxVoice TO index
                           LISTBOX GET USER hDlg, %ListboxVoice, index TO RegistrySelection
                           CONTROL SET TEXT hDlg, %LabelVoice, "Voice available" & _
                           IIF$(RegistrySelection = 10, " - Speech_OneCore", " - Speech")
                         END IF
                
                     END SELECT
                
                   CASE %WM_VSCROLL
                     SELECT CASE LOWRD(CBWPARAM)
                
                       CASE %TB_THUMBTRACK, %TB_LINEDOWN, %TB_LINEUP, %TB_PAGEDOWN, %TB_PAGEUP, %TB_TOP, %TB_BOTTOM
                         SELECT CASE GetDlgCtrlID(CBLPARAM)
                
                           CASE %TrackbarVolume
                             CONTROL SET TEXT CBHNDL, %LabelVolume, STR$(SendMessage(CBLPARAM, %TBM_GETPOS, 0, 0)) 'Update label with position
                
                           CASE %TrackbarSpeed
                             CONTROL SET TEXT CBHNDL, %LabelSpeed, STR$(10 - SendMessage(CBLPARAM, %TBM_GETPOS, 0, 0)) 'Update label with position
                
                         END SELECT
                     END SELECT
                
                   CASE %WM_SIZE
                     LOCAL ClientSizeX AS LONG
                     LOCAL ClientSizeY AS LONG
                     IF CBWPARAM <> %SIZE_MINIMIZED THEN
                       ClientSizeX = LO(WORD, CBLPARAM)
                       ClientSizeY = HI(WORD, CBLPARAM)
                       SetWindowPos(GetDlgItem(hDlg, %ListboxVoice), 0, 0, 0, ClientSizeX - 165, 90, %SWP_NOZORDER OR %SWP_NOMOVE)
                       SetWindowPos(GetDlgItem(hDlg, %TextboxText), 0, 0, 0, ClientSizeX - 165, ClientSizeY - 187, %SWP_NOZORDER OR %SWP_NOMOVE)
                
                       SetWindowPos(GetDlgItem(hDlg, %FrameVolume),    0, ClientSizeX - 142, 10, 60, ClientSizeY - 172, %SWP_NOZORDER)
                       SetWindowPos(GetDlgItem(hDlg, %TrackbarVolume), 0, ClientSizeX - 130, 25, 30, ClientSizeY - 212, %SWP_NOZORDER)
                       SetWindowPos(GetDlgItem(hDlg, %LabelVolume),    0, ClientSizeX - 130, ClientSizeY - 182, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                
                       SetWindowPos(GetDlgItem(hDlg, %FrameSpeed),     0, ClientSizeX - 70, 10, 60, ClientSizeY - 172, %SWP_NOZORDER)
                       SetWindowPos(GetDlgItem(hDlg, %TrackbarSpeed),  0, ClientSizeX - 60, 25, 30, ClientSizeY - 212, %SWP_NOZORDER)
                       SetWindowPos(GetDlgItem(hDlg, %LabelSpeed),     0, ClientSizeX - 58, ClientSizeY - 182, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                
                       SetWindowPos(GetDlgItem(hDlg, %ButtonSapiCpl), 0, ClientSizeX - 144, ClientSizeY - 36 - 120, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                       SetWindowPos(GetDlgItem(hDlg, %ButtonSpeak),   0, ClientSizeX - 144, ClientSizeY - 36 - 90, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                       SetWindowPos(GetDlgItem(hDlg, %ButtonPause),   0, ClientSizeX - 144, ClientSizeY - 36 - 60, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                       SetWindowPos(GetDlgItem(hDlg, %ButtonWave),    0, ClientSizeX - 144, ClientSizeY - 36 - 30, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                       SetWindowPos(GetDlgItem(hDlg, %ButtonExit),    0, ClientSizeX - 144, ClientSizeY - 36, 0, 0, %SWP_NOZORDER OR %SWP_NOSIZE)
                       InvalidateRect(hDlg, BYVAL %NULL, %TRUE) : UpdateWindow(hDlg)
                     END IF
                
                   CASE %WM_NCACTIVATE
                     IF CBWPARAM THEN             'Application is getting focus
                       IF hControlFocus THEN      'Check if valid focus
                         SetFocus(hControlFocus)  'Restore focus on control
                         hControlFocus = 0
                       END IF
                     ELSE                         'Application is about to loose focus
                       hControlFocus = GetFocus() 'Remember witch control have focus
                     END IF
                
                   CASE %WM_SYSCOMMAND
                     IF (CBWPARAM AND &HFFF0) = %SC_CLOSE THEN '[x]
                       IF DoPause = %PauseIsOn THEN DoPause = %PauseSetItOff
                       IF DoSpeak = %TRUE THEN DoSpeak = %FALSE
                       'FUNCTION = 1 : EXIT FUNCTION 'App will not end
                     END IF
                
                  END SELECT
                
                END FUNCTION
                '______________________________________________________________________________
                
                FUNCTION PBMAIN()
                
                 DIALOG FONT "Segoe UI", 9
                 DIALOG NEW %HWND_DESKTOP, $AppName, , , 375, 330, _
                 %WS_CAPTION OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX OR %WS_SIZEBOX OR %WS_SYSMENU, 0 TO hDlg
                
                 SetClassLong(hDlg, %GCL_HICON, ExtractIcon(GetModuleHandle(""), "%SystemRoot%\system32\wpdshext.dll", 6))
                
                 CONTROL ADD LABEL, hDlg, %LabelVoice, "Voice available", 5, 5, 150, 10
                 CONTROL ADD LISTBOX, hDlg, %ListboxVoice, , 5, 15, 283, 50
                
                 CONTROL ADD LABEL, hDlg, %LabelFormat, "Format", 5, 60, 60, 10
                 CONTROL ADD COMBOBOX, hDlg, %ComboFormat, , 5, 70, 125, 100, _
                 %CBS_DROPDOWNLIST OR %WS_TABSTOP OR %WS_VSCROLL, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT
                
                 CONTROL ADD LABEL, hDlg, %LabelLangId, "Language id reference", 135, 60, 100, 10
                 CONTROL ADD COMBOBOX, hDlg, %ComboLangId, , 135, 70, 125, 100, _
                 %CBS_DROPDOWNLIST OR %WS_TABSTOP OR %WS_VSCROLL, %WS_EX_CLIENTEDGE OR %WS_EX_LEFT
                
                 CONTROL ADD LABEL, hDlg, %LabelText, "Speech", 5, 85, 60, 10
                
                 LOCAL sText AS STRING
                 sText = _
                 "<?xml version='1.0' encoding='UTF-8'?>"                        & $CRLF & _ '
                 "<speak version=""2.0"" xmlns=""http://www.w3.org/2001/10/synthesis"" xml:lang=""en-US"">" & $CRLF & _ '
                 "<sentence>"                                                    & $CRLF & _ '
                 "<!-- This  is a remark -->"                                    & $CRLF & _ '
                 "Speech test for Windows 7 and Windows 10."                     & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "Volume is from 0 to 100.  "                                    & $CRLF & _ '
                 "<volume level='25'> Volume is now 25.  "                       & $CRLF & _ '
                 "<volume level='100'> going to 100."                            & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "Pitch is from <pitch middle='-10'> -10 to"                     & $CRLF & _ '
                 "<pitch middle='+10'> +10."                                     & $CRLF & _ '
                 "<pitch middle='0'> Pitch is now 0."                            & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "Speed is from <rate absspeed='-10'> -10 to —"                  & $CRLF & _ '
                 "<rate absspeed='+10'> 10 positive. —"                          & $CRLF & _ '
                 "<rate absspeed='0'>back to 0. "                                & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "<emph> spelling word </emph>!."                                & $CRLF & _ '
                 "<spell> word </spell>. — "                                     & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "<say-as interpret-as='hms12'> 4:00pm </say-as>"                & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "<context ID = 'number_cardinal'>Cardinal 3432 .</context>"     & $CRLF & _ '
                 "<context ID = 'number_digit'>Digit 3432 .</context>"           & $CRLF & _ '
                 "<context ID = 'number_fraction'>Fraction 3/15 .</context>"     & $CRLF & _ '
                 "<context ID = 'number_decimal'>Decimal 123.45 .</context>"     & $CRLF & _ '
                 "<context ID = 'number_currency'>Currency $123.45 .</context>"  & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "Now a one second silence... <silence msec='1000' />. Done."    & $CRLF & _ '
                 "The em-dash '—' inserts a ½ second — delay."                   & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "<!--"                                                          & $CRLF & _ '
                 " Changing voice if available"                                  & $CRLF & _ '
                 "--/>"                                                          & $CRLF & _ '
                 "<voice required='Gender=Female;Age!=Child'>"                   & $CRLF & _ 'If it exist, else silence
                 "<voice required='Gender=Female;Age!=Teen'>"                    & $CRLF & _ 'If it exist, else silence
                 "<voice required='Language=409'>' English US voice. </voice>"   & $CRLF & _ '
                 "<lang langid='40c'> Choisir le Français France si disponible. </lang>"      & $CRLF & _ '
                 "<lang langid='809'> Setting voice to British English if available. </lang>" & $CRLF & _ '
                 ""                                                              & $CRLF & _ '
                 "</sentence></speak>"                                           & $CRLF '& _ '
                
                 CONTROL ADD TEXTBOX, hDlg, %TextboxText, sText, 5, 95, 283, 201, %WS_TABSTOP OR %WS_BORDER OR _
                 %ES_LEFT OR %ES_NOHIDESEL OR %WS_HSCROLL  OR %WS_VSCROLL OR %ES_WANTRETURN OR %ES_MULTILINE
                
                 CONTROL ADD FRAME, hDlg, %FrameVolume, " Volume ", 293, 5, 35, 140
                 CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarVolume, "&V", 300, 13, 25, 122, _
                 %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %TBS_VERT OR %TBS_LEFT OR %TBS_AUTOTICKS
                
                 CONTROL ADD LABEL, hDlg, %LabelVolume, "100", 301, 135, 20, 9, %SS_CENTER
                
                 CONTROL ADD FRAME, hDlg, %FrameSpeed, "  Speed ", 333, 5, 35, 140
                 CONTROL ADD "MsCtls_TrackBar32", hDlg, %TrackbarSpeed, "&D", 340, 13, 25, 122, _
                 %WS_CHILD OR %WS_VISIBLE OR %WS_TABSTOP OR %TBS_VERT OR %TBS_LEFT OR %TBS_AUTOTICKS
                 CONTROL ADD LABEL, hDlg, %LabelSpeed, "0", 342, 135, 20, 9, %SS_CENTER
                
                 CONTROL ADD BUTTON, hDlg, %ButtonSapiCpl, "SAPI &CPL", 293, 214, 77, 15
                
                 CONTROL ADD BUTTON, hDlg, %ButtonSpeak, "&Speak", 293, 231, 77, 15
                
                 CONTROL ADD BUTTON, hDlg, %ButtonPause, "Pa&use", 293, 248, 77, 15
                
                 CONTROL ADD BUTTON, hDlg, %ButtonWave, "Make &wave file", 293, 265, 77, 15
                
                 CONTROL ADD BUTTON, hDlg, %ButtonExit, "E&xit", 293, 282, 77, 15
                
                 DIALOG SHOW MODAL    hDlg CALL DlgProc
                
                END FUNCTION
                '______________________________________________________________________________
                '
                Last edited by Pierre Bellisle; 5 Aug 2018, 02:20 PM.

                Comment


                • #9
                  Windows 10 has two different TTS engines installed by default. There are the WinRT (Tablet Mode, used to be called Metro) speech synthesis APIs (in the Windows.Media.SpeechSynthesis namespace), and the SAPI (Desktop Mode) speech synthesis APIs (in the System.Speech.Synthesis namespace, and the COM ISpVoice interface).

                  David and Zira are SAPI voices; the language packs install WinRT voices.

                  If you want to use the language pack voices, In Pierre's Code (Post #8)

                  change
                  Code:
                  $SPCAT_VOICES   = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices"
                  To
                  Code:
                  $SPCAT_VOICES   = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices"

                  Comment


                  • #10
                    Code above is updated so both "Speech" and "Speech_OneCore" voices entrys are added to the list...

                    Comment


                    • #11
                      Pierre,
                      your changes do add both Speech" and "Speech_OneCore" voices entries to the list.
                      but only speaks with default voice, not the selected list item.

                      VoiceGet function should be changed

                      Code:
                      FUNCTION VoiceGet(BYVAL sCategoryId AS STRING, BYVAL RegistrySelection AS LONG) AS LONG
                      .
                      .
                             IF hr = %S_OK AND dwValue <> %NULL THEN
                               LISTBOX ADD hDlg, %ListboxVoice, pUnicodeToAnsi(dwValue) TO ListboxIndex    'added this
                      LISTBOX SET USER hDlg, %ListboxVoice, ListboxIndex, RegistrySelection       'added this
                               IF pUnicodeToAnsi(dwValue) = sVoiceDefault THEN
                                 LISTBOX GET COUNT hDlg, %ListboxVoice TO ListboxIndex
                                 LISTBOX SELECT hDlg, %ListboxVoice, ListboxIndex
                      'LISTBOX SET USER hDlg, %ListboxVoice, ListboxIndex, RegistrySelection    ' not sure this is needed
                               END IF
                               CoTaskMemFree(dwValue)
                             END IF
                      .
                      .
                      END FUNCTION

                      Comment


                      • #12
                        I did fix many bugs, above code is updated...

                        Comment


                        • #13
                          Brilliant Pierre,
                          how do you pause the speech text for a fixed duration say 3 to 5 secs before start of another sentence?

                          I have tried using "......................" but to no avail as it only last a second.

                          Comment


                          • #14
                            The em-dash "—" inserts a ½ second only delay.
                            One way could be to use XML text to get many options such as pausing... <break time=""5s"" /> will set a five second pause.

                            Comment


                            • #15
                              Thanks Pierre, how to incorporate the XML scripts into the textbox ?

                              As I have tried adding <break time=""5s"" /> into it, the voice just reads it out aloud

                              Comment


                              • #16
                                The Microsoft implementation of SSML is based on Speech Synthesis Markup Language (SSML).
                                You have to give header, and version info, etc...
                                Copy and paste the following short example as is in the textbox of the program I posted above.

                                Code:
                                <?xml version="1.0" encoding="ISO-8859-1"?>
                                <speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="en-US">
                                <sentence>
                                Now going for a two seconds pause... <break time="2s" /> The pause is over!.
                                </sentence>
                                </speak>
                                Last edited by Pierre Bellisle; 2 Aug 2018, 10:49 PM.

                                Comment


                                • #17
                                  Hi Pierre

                                  After copying and pasting the above scripts into the textbox and when it runs it still reads aloud whatever on that script !
                                  Hence it is not functioning as it should be ?

                                  Comment


                                  • #18
                                    Works fine for me under W7 and W10.
                                    I uploaded a new version that use "$PROGID_SAPISpFileStream1" with a pause button, a button to call SAPI control panel dialog, and simpler XML text.
                                    Another way for you might be to add some pause/resume custom calls in the pISpVoice.Speak() loop.

                                    Comment


                                    • #19
                                      Did not work for me in Windows 7, probably due to settings on my laptop. Thanks anyway ,Pierre , I would use the pause/resume button for this purpose.

                                      Comment


                                      • #20
                                        Interesting, I just tried on a fresh Windows 7-64 installation and all is fine...

                                        Might be a missing dot net framework related file or some other one.
                                        If you feel like it, you may download and install "Speech SDK 5.1" at
                                        https://www.microsoft.com/en-us/down....aspx?id=10121
                                        and see if it help. Note that there is a link to the SAPI.chm help file.

                                        There is also: "Speech Utilities for Microsoft Speech Technologies"
                                        Tools to troubleshoot, analyze, and diagnose elements of speech applications.
                                        at https://www.microsoft.com/en-us/down....aspx?id=18885

                                        Hope you will find a solution if the pause/resume functions are not adequate for your context.


                                        Comment

                                        Working...
                                        X