Announcement

Collapse
No announcement yet.

Thread loop speed with Sleep

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

  • Thread loop speed with Sleep

    I ran into a phenomenon that I don't understand.

    Basically it goes like this. If I run this code, it displays the frequency the loop cycles. It is a very high frequency if the SLEEP is 0 or is omitted. If it is SLEEP 1 (ms), it should be nominal 1000Hz while SLEEP 2 it's 500Hz.

    However, the frequency varies from machine to machine. On my vista laptop with dual core, it runs at 64Hz nominal until I run Firefox on a page that has graphics - it then runs at the predicted frequency. Closing Firefox causes it to return to 64Hz. MSIE however doesn't have the same effect, nor does opening Firefox to a local jpg file. I also found if I open and run my DVD player or other prog that uses DirectX video, the speed goes to where it should.

    On my wife's XP laptop it runs at 171 Hz regardless of other apps running. On another machine with no other progs running it shows predicted speed.

    Any ideas? Virus infection? Spyware?

    Anyway, here's the code.... can anyone else replicate?

    Code:
    #COMPILE EXE
    #DIM ALL
    #IF NOT %DEF(%WINAPI)
        #INCLUDE "WIN32API.INC"
    #ENDIF
    
    GLOBAL qFreq AS QUAD
    GLOBAL qStartCount AS QUAD
    GLOBAL qEndCount AS QUAD
    
    FUNCTION InitHiresTimer() AS DWORD
        CALL QueryPerformanceFrequency(qFreq)
    END FUNCTION
    
    MACRO startHiresTimer
        CALL QueryPerformanceCounter(qStartCount)
    END MACRO
    
    FUNCTION display_loopfreq() AS STRING
        QueryPerformanceCounter(qEndCount)
        FUNCTION =  FORMAT$(qFreq/(qEndCount-QStartCount),"#.0")+" Hz"
        QueryPerformanceCounter(qStartCount)
    END FUNCTION
    
    'THREAD FUNCTION loopthread(BYVAL x AS DWORD) AS DWORD 'pbwin9
    FUNCTION loopthread(BYVAL x AS DWORD) AS DWORD
        initHirestimer()
        startHiresTimer()
        DO
            SLEEP 2   'compare with sleep 1, sleep 0, dialog doevents
            DIALOG SET TEXT x, display_loopfreq()
        LOOP UNTIL gThreadrun = 0
    END FUNCTION
    
    FUNCTION PBMAIN () AS LONG
        LOCAL x AS DWORD
        LOCAL hdlg AS DWORD
        LOCAL hThread AS DWORD
        DIALOG NEW %HWND_DESKTOP, "", 0,0, 100,0 TO hdlg
        DIALOG SHOW MODELESS hDlg
        GLOBAL gThreadrun AS DWORD
        gThreadrun = 1
        THREAD CREATE loopthread(hdlg) TO hThread
        THREAD CLOSE hThread TO hThread
    
        MSGBOX "Quit?",,"Loopspeed" 'This holds loop running until msgbox closes
        gThreadrun = 0
    END FUNCTION

  • #2
    SLEEP is not some kind of timing control function. You can't use SLEEP with a specific value and expect a consistant timing of the thread.

    All sleep does is relinquish the current threads time slice to the next thread which Windows chooses to be next. A value of zero relinquishes whats left of the current threads time slice and a non-zero value makes the thread sleep for that many milliseconds max.

    What you don't see is how other threads are being executed by Windows.

    Especially with Windows XP and later, there may be dozens of processes and threads running, even if you are not running a single program. Just take a look at how many services the PC is running at the time and you will see how much Windows is doing. Depending upon the software installed, the hardware, drivers, etc. each PC is uniquely different in how many processes or threads may be running at any given time.

    To make things even more complex, processes and threads have different priorities. For example some hardware drivers may have a very high priority while other threads/processes have a lower priority. Windows is determining how much time each process or thread is given for its time slice and then it continuously moves from thread/process to thread/process. It never stops.

    A clue to all of this is in the description of Windows as a "multi-tasking" operating, meaning it is doing many, many different things seemly all at the same time (actually it is only doing one thing at a time, but keeps switching threads so fast, they appear to be running simultaneously.

    Do not think you can somehow get exact control of all of this with the Sleep command. You have no control of all of this timing. You can change a threads priority to get more time. You can make a thread sleep to relinquish some of its time. Yet you can't control the exact amount of time being used, since Windows has all control of this and it depends upon how many processes/threads are running at any given time, what each process/thread is doing and their priority settings.

    Now add to this how much memory the PC has, which can also affect this, since when time slices are switched (a context switch) Windows has to do some fancy footwork to make each process think it is all alone and has its own memory blocks (including the settings on the CPU registers). The problem is that what makes all of this work is that a certain amount of memory is needed and if the memory is below that threshhold, Windows must do page swapping to the harddrive. So depending upon how much page swapping is going on, things can either be slowed down or speed up.

    Simply put, there are too many factors involved here to think you can somehow control exact timing with the little old SLEEP command.

    To make things worse, your code is calling a GUI command within the worker thread.

    ie.

    Code:
       DO
            SLEEP 2   'compare with sleep 1, sleep 0, dialog doevents
            DIALOG SET TEXT x, display_loopfreq()
        LOOP UNTIL gThreadrun = 0
    You may not consider this significant, but it is oh so much!

    DIALOG SET TEXT calls API's which will force Windows to so a context switch (switch threads) and jump from the worker thread to the primary GUI thread. This context switch adds a lot of over head and slows things down. You are simply just adding one more problem to the mix. You call SLEEP (which could possibly switch to the next thread which may be the primary thread) and immediately afterwards force a context switch again by calling DIALOG SET TEXT.

    One must fully understand how threads really work before using them and also the ramifications of calls to things like SLEEP or GUI commands.

    It is easy to create and use threads, but it is much more difficult to calculate the effects of the code you put into that thread and how it effects timing.
    Last edited by Chris Boss; 27 Jun 2009, 09:57 PM.
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

    Comment


    • #3
      Sorry Rory. Chris is right. I have a program which sends morse code which requires correct timing to get the correct length of each "dit" and "dah" else the code ends up all jumbled. Using DOS as a platform it is easy to keep correct timing but with Windows I have to use a seperate piece of equipment to make the morse code readable. Don't worry, I spent a lot of time before admitting defeat.
      Denys C Brosnan

      Comment


      • #4
        I guess I should just give up and admit I'm too stupid to do this. All my apps have to be single-threaded cause I'm just too thick to use threading properly.

        The point was not to try to control the loop frequency with the sleep, the sleep was just to keep the loop from hogging processor time. That the loop frequency fell close to what the sleep period would create was incidental. I KNOW BETTER THAN TO USE SLEEP IN TIMING LOOPS DAGNABBIT! However I AM concerned with how fast I can get a loop to run because time constraints talking to the outside world.

        The point I am trying to understand is what is causing the loop to bog down when there are only "hidden" processes and why does it suddenly stop bogging when other processes are running.

        OK so show me how it should really be done.
        Last edited by Rory Davis; 27 Jun 2009, 11:06 PM.

        Comment


        • #5
          My density is pretty high regarding multiple threads as well, and I haven't had much need for 'em yet. I do know that you can't have all the processor time, and you can't predict just how much you can have. Your loop runs about 4khz on my Athlon64 using SLEEP 0. You can also use a DIALOG DOEVENTS 0 instead, with about the same results. I've run some very fast loops by not updating GUI displays on every pass. You could update every hundred passes and probably gain a lot.

          Comment


          • #6
            Anyway, here's the code.... can anyone else replicate?
            Yes.

            I get similar results with my PC (Athlon 64 X2 WinXP SP2).
            I see about 64hz which rises to around 512 whem windows media player is active (playing a DVD etc) with Sleep 1.
            The speed rises to 2000+ if there is no Sleep or Dialog DoEvent in the threaded function.
            I also noted that if I include a msg pump ahead of the MsgBox..
            Code:
                Do Until IsWin(hDlg) = 0
                  Dialog DoEvents 
                Loop
            the speed is halved BUT if I drag the Dialog caption bar it doubles again while the mouse button is down (I'd noticed that behavior before in other apps).

            I can't explain any of this except to say that Chris is right in that you can't really predict how many time slices the OS will give your application. From the above it looks as if ActiveX muscles in on the act and also that the OS allows more time slices to your app when it knows a drag operation is going on. All of which makes running time critical tasks a bit awkward .
            Rgds, Dave

            Comment


            • #7
              This might be useful:



              Code:
              Sub Sleepy(milliseconds As Long) 'a better sleep
                Dim x As Long, Counter As Long
                Counter = (milliseconds \ 11) + 1  'minimum 1 loop (10 milliseconds)
                For x = 1 To Counter
                  DIALOG DoEvents
                  Sleep 10
                Next
              End Sub
              The world is full of apathy, but who cares?

              Comment


              • #8
                Rory,
                perhaps this explanation might help:


                If you want quicker response times you can try using multimedia timers which give a resloution of about 1ms:
                Code:
                'PBWin9.01 program
                $INCLUDE "WIN32API.INC"
                DECLARE FUNCTION test( BYVAL uID AS LONG, BYVAL uMsg AS LONG, _
                                         BYVAL dwUser AS LONG, BYVAL dw1 AS LONG, BYVAL dw2 AS LONG) AS LONG
                
                GLOBAL msec, CloseFlag AS LONG
                GLOBAL Freq AS QUAD
                GLOBAL Result AS STRING
                
                %UpButton=500
                %DownButton=501
                %CloseButton=502
                %OutputText=503
                %TargetText=504
                
                
                CALLBACK FUNCTION UpButtonClick ()
                IF CB.MSG = %WM_COMMAND AND CB.CTLMSG = %BN_CLICKED THEN
                    DECR msec
                    FUNCTION = 1
                END IF
                 
                END FUNCTION
                
                
                CALLBACK FUNCTION DownButtonClick ()
                IF CB.MSG = %WM_COMMAND AND CB.CTLMSG = %BN_CLICKED THEN
                    INCR msec
                    FUNCTION = 1
                END IF
                
                END FUNCTION
                
                
                CALLBACK FUNCTION CloseButtonClick ()
                IF CB.MSG = %WM_COMMAND AND CB.CTLMSG = %BN_CLICKED THEN
                    CloseFlag=1
                    FUNCTION = 1
                END IF
                
                END FUNCTION
                
                
                
                FUNCTION WINMAIN (BYVAL hInstance     AS LONG, _
                                  BYVAL hPrevInstance AS LONG, _
                                  BYVAL lpCmdLine           AS ASCIIZ PTR, _
                                  BYVAL iCmdShow      AS LONG) AS LONG
                
                
                LOCAL count0,count1 AS QUAD
                LOCAL hDlg, TimerHandle AS LONG
                'start the timer
                '1=milliseconds between triggers, 0=maximum timer resolution, test=the routine to call
                TimerHandle = timeSetEvent ( BYVAL 1, BYVAL 0, CODEPTR(test), BYVAL 0&, BYVAL %TIME_PERIODIC)
                
                msec=100' start off aiming for 100msec (10Hz)
                QueryPerformanceFrequency freq
                QueryPerformanceCounter count0
                 
                                             
                DIALOG NEW 0,"Test",,,100,100 TO hDlg
                CONTROL ADD BUTTON, hDlg, %UpButton,"Up",10,10,30,20 CALL UpButtonClick
                CONTROL ADD BUTTON, hDlg, %Downbutton,"Down",10,40,30,20 CALL DownButtonClick
                CONTROL ADD BUTTON, hDlg, %CloseButton,"Close",10,70,30,20 CALL CloseButtonClick
                CONTROL ADD LABEL, hDlg, %OutputText,"Freq = ####Hz",50,70,40,20
                CONTROL ADD LABEL, hDlg, %TargetText,"",50,10,40,20
                
                DIALOG SHOW MODELESS hDlg
                
                DO
                    DIALOG DOEVENTS
                 '   sleep 10
                    CONTROL SET TEXT  hDlg, %OutputText, Result
                    CONTROL SET TEXT  hDlg, %TargetText, "Target= "+FORMAT$((1000/msec),"###.000")+"Hz"
                LOOP UNTIL CloseFlag
                
                'stop the timer
                timeKillEvent TimerHandle
                DIALOG END hDlg
                END FUNCTION
                
                
                
                FUNCTION test ( BYVAL uID AS LONG, BYVAL uMsg AS LONG, _
                                        BYVAL dwUser AS LONG, BYVAL dw1 AS LONG, BYVAL dw2 AS LONG) AS LONG
                'this is the routine that is run everytime the timer triggers
                STATIC COUNT AS LONG
                STATIC count1, count2 AS QUAD
                
                      INCR COUNT
                      IF COUNT >= msec THEN
                          COUNT=0
                          QueryPerformanceCounter count2
                          Result = "Freq = "+FORMAT$((freq/(count2-count1)),"0000.000")+"Hz"
                          count1 = count2
                      END IF
                      
                END FUNCTION

                Comment


                • #9
                  Denys,
                  I'm sure there are better ways of sending Morse code but here's one simple method. It assumes you have a system speaker in your computer:
                  Code:
                  'PBCC5.01 program
                  $INCLUDE "WIN32API.INC"
                  
                  FUNCTION WINMAIN (BYVAL hInstance     AS LONG, _
                                    BYVAL hPrevInstance AS LONG, _
                                    BYVAL lpCmdLine           AS ASCIIZ PTR, _
                                    BYVAL iCmdShow      AS LONG) AS LONG
                  
                  SendMorse("Hello from Mister Morse")
                            
                  END FUNCTION
                  
                  
                  SUB SendMorse(m AS STRING)
                  LOCAL r AS LONG
                  LOCAL symbol, letter, Morse AS STRING
                  LOCAL DitDuration, DahDuration AS LONG
                  
                  DitDuration=50 'milliseconds
                  DahDuration = 3 * DitDuration
                  
                  'a-m
                  DATA .-,-...,-.-.,-..,.,..-.,--.,....,..,.---,-.-,.-..,--
                  'n-z
                  DATA -.,---,.--.,--.-,.-.,...,-,..-,...-,.--,-..-,-.--,--..
                  '0-9
                  DATA -----,.----,..---,...--,....-,.....,-....,--...,---..,----.
                  
                  FOR r = 1 TO LEN(m)
                      letter = LCASE$(MID$(m,r,1))
                      SELECT CASE letter
                          CASE "a" TO "z"
                              symbol = READ$(ASC(letter)-ASC("a")+1)
                          CASE "0" TO "9"
                              symbol = READ$(ASC(letter)-ASC("0")+27)
                  
                          CASE ELSE
                              symbol = " "
                          END SELECT
                  
                       Morse = Morse + " " + symbol
                       PRINT r,letter,symbol
                  
                  NEXT
                  
                  FOR r& = 1 TO LEN(Morse)
                      SELECT CASE MID$(Morse,r,1)
                          CASE "."
                              winbeep 500,DitDuration
                              winbeep 20000,DitDuration
                          CASE "-"
                              winbeep 500,DahDuration
                              winbeep 20000,DitDuration
                  
                          CASE " "
                              winbeep 20000,DahDuration
                  
                      END SELECT
                  
                  NEXT
                  
                  END SUB
                  A better sounding way would be to create some .WAV files of dits and dahs and silence and string them together to play but that's more complicated to program. However, once strung together, the sound system in the computer will play them at the required speed and timing regardless of what else is running on the computer.

                  If you're trying to trigger an outside device then the the multimedia timer demonstrated in the previous post will probably be accurate enough for Morse Code. It's not exact but is probably closer in timing than a human could get.

                  Paul.

                  Comment


                  • #10
                    Thanks Paul, the link you posted was exactly the explanation I needed.

                    Comment


                    • #11
                      > [SLEEP] makes the thread sleep for that many milliseconds max.

                      I believe that should be "that many milliseconds MINIMUM."

                      However, the help for SLEEP() doesn't:
                      Pause the current thread of the application for a specified number of milliseconds (mSec), allowing other processes (or threads) to continue.
                      That Kinda makes it sound like a precision thing, huh?

                      >OK so show me how it should really be done.

                      If you mean "precision timing" as a general proposition, it ain't SLEEP, or even a waitable timer, or a LOOP querying TIX(). The preemptive multitasking of Windows is downright incompatible with precision timing.

                      But if you mean running a background thread with a GUI, I think this is a pretty good starter demo:
                      GUI + Worker Thread + Abort Demo 11-24-07


                      MCM
                      Michael Mattias
                      Tal Systems (retired)
                      Port Washington WI USA
                      [email protected]
                      http://www.talsystems.com

                      Comment


                      • #12
                        A better sounding way would be to create some .WAV files of dits and dahs and silence and string them together to play but that's more complicated to program.
                        I like this idea, a lot.

                        Maybe even create WAVs for each letter of the alphabet plus combined letters of the common words used in telegrams eg "STOP". The the user might be able to type and have the sounds generated in real time, without learning Morse code at all. (I learned as a Cub Scout. I have forgotten).

                        You'd think there must be some Windows functions to "Build WAV or other sound file by specifying a table of frequency and duration" or something like that, wouldn't you?

                        [LATER]

                        Hmm, thank you, Google(r)....


                        1. WinMorse 2.0 (winmorse.com)
                        is a FREE text to Morse Code conversion application for Windows. WinMorse converts text into Morse Code, using various Output Formats. Here are some ideas for what you can do with the files created by WinMorse: Use for Morse Code practice/training/testing Accessibility tool for the vision impaired Create Morse Code…
                        Type in your message, convert using above to WAV, play. (?)
                        Last edited by Michael Mattias; 28 Jun 2009, 10:58 AM. Reason: Add link info
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #13
                          I went away to do a little programming, and now with the recent posts above, this is all old hat. It doesn't make real letters and words, but it sounds kinda kewl and seems pretty even timing-wise.
                          Code:
                          #COMPILE EXE
                          #DIM ALL
                          DECLARE FUNCTION sndPlaySound LIB "WINMM.DLL" ALIAS "sndPlaySoundA" (lpszSoundName AS ASCIIZ, BYVAL uFlags AS DWORD) AS LONG
                          DECLARE FUNCTION GetTickCount LIB "KERNEL32.DLL" ALIAS "GetTickCount" () AS DWORD
                          
                          FUNCTION PBMAIN () AS LONG
                           LOCAL daOrDit AS STRING, ii AS LONG
                            FOR ii = 1 TO 100
                            IF RND < .5 THEN daOrDit = "dah.wav" ELSE daOrDit = "dit.wav"
                            sndPlaySound BYCOPY daOrDit, 0
                            IF RND < .05 THEN millisecDelay(150)
                            NEXT
                          
                          END FUNCTION
                          
                          FUNCTION millisecDelay(x AS LONG) AS LONG
                          
                             LOCAL y AS LONG
                             y = getTickCount
                             DO
                                IF getTickCount - y >= x THEN EXIT FUNCTION
                             LOOP
                          
                          END FUNCTION
                          Attached Files
                          Last edited by John Gleason; 28 Jun 2009, 12:36 PM. Reason: added compiled .exe to zip

                          Comment


                          • #14
                            Just because you can't guarantee the speed of the thread, does not mean that using a thread won't work for what you need.

                            The first question is:

                            "What task does the thread need to accomplish" ?

                            Second, how accurate a time interval is required ?

                            In many instances, as long as the thread runs at a reasonable pace, there may be no need to have absolute control of the speed.

                            For example, lets say that one needs to poll a device or something at least 10 times a second. Thats 100 milliseconds.

                            A thread would work reasonably well.

                            The thread could run a continuous loop and use a QueryPerformanceCounter to get an accurate count for the frame rate (each iteration) to see how fast the routine is running (no matter how Windows handles time slices).

                            Calculate how much time it took for the current iteration, since the last one using high performance timers and then determine whether the current iteration has taken took long or whether it has finished faster than what is needed. If the current iteration has taken longer than was is required, then simply don't call SLEEP and your thread will run as fast as is possible to keep up. If there is time left for the current iteration, then call SLEEP based on the calculation of how much time is left for the iteration (frame) so you can reliquish the time slice to other threads/processes.

                            If the time slice for each iteration is not enough to keep up with the speed you need then you need to have some kind of skip iteration built in to compensate and then flag to the user and the program that the thread can not keep up.

                            This is how it is done with video I think. With video in the old days if the thread could not keep up with the requested frame rate, the software would drop frames.

                            Now if you can't keep up to the required frame rate, then you may need to increase the priority of the thread so Windows gives it more time for each time slice while it decreases the time given to other threads.

                            This is why Windows allows you to set thread priority levels.

                            Each process is given a specific thread priority and each thread within the process is given a thread priority. The SetThreadPriority API function for example lets you add or subtract from the current priority of a thread.

                            There are very high priorities left for things like device drivers and hardware in the rare instance you need absolute control over the speed but that is beyond the scope of my knowledge and requires some special programming skills.
                            Chris Boss
                            Computer Workshop
                            Developer of "EZGUI"
                            http://cwsof.com
                            http://twitter.com/EZGUIProGuy

                            Comment


                            • #15
                              I thought this thread was about "SLEEP" not Morse Code and only used it as an example. However not to be outdone I will have a look at your code Paul and make up my own mind after trying it out. Such variables as speed (from 25 to 250 letters per minute), ratio (varying the 3 dits to 1 dah timing) etc must come into it. Sound does not come into it as it must switch a radio transmitters output on and off for each dit or dah using either a COM port or a PARALLEL port. The arrangement I have works exceedingly well changing a character such as "a" to the necessary dit/dah combination. But I try out your code and see whether it gives any additional benefit.
                              73 Denys
                              Denys C Brosnan

                              Comment


                              • #16
                                Michael,
                                this version generates a wav file of the whole text and plays it through the speakers.
                                Code:
                                'PBCC5 program
                                #DIM ALL
                                #BREAK ON
                                #INCLUDE "win32api.inc"
                                
                                FUNCTION PBMAIN()
                                
                                MorseCode("SOS SOS Hello from Mister Morse SOS", 80,1000,50)  'What to send, dot duration, frequency and % volume
                                     
                                PRINT "press key to end."
                                WAITKEY$
                                END FUNCTION
                                
                                
                                'define types
                                TYPE WAVEheader
                                rID              AS STRING*4  ' Contains the characters "RIFF"
                                rLen             AS LONG      ' The length of the data in the next chunk
                                wID              AS STRING*4  ' Contains the characters "WAVE"
                                fId              AS STRING*4  ' Contains the characters "fmt "
                                fLen             AS LONG      ' Length of data in the format chunk
                                wFormatTag       AS INTEGER   ' specifies the wave format, eg 1 = Pulse Code Modulation
                                nChannels        AS INTEGER   ' Number of channels, 1=mono, 2=stereo
                                nSamplesPerSec   AS LONG      ' Playback frequency
                                nAvgBytesPerSec  AS LONG      ' Indicates the average number of bytes a second the data should be
                                                              ' transferred at = nChannels * nSamplesPerSec * (nBitsPerSample / 8)
                                nBlockAlign      AS INTEGER   ' Indicates the block alignment of the data in the data chunk. Software
                                                              ' needs to process a multiplt of nBlockAlign at a time.
                                                              ' nBlockAlign = nChannels * (nBitsPerSample / 8)
                                wBitsPerSample   AS INTEGER   ' Format specific data area
                                dId              AS STRING*4  ' Contains the characters "data"
                                dLen             AS LONG      ' Length of data in the dData field
                                END TYPE
                                
                                
                                TYPE twominwav
                                     wv AS waveheader
                                     dta AS ASCIIZ *1000000
                                END TYPE
                                
                                
                                SUB MorseCode(Phrase AS STRING, DitDuration AS LONG, Freq AS LONG, Volume AS LONG)
                                STATIC sound AS twominwav
                                LOCAL ShortGap, LetterGap, WordGap, r, k AS LONG
                                LOCAL DitSamples, DahSamples, TotalSamples, Char AS LONG
                                LOCAL SCALE AS EXT
                                LOCAL p AS BYTE PTR
                                LOCAL symbol, letter, Morse AS STRING
                                
                                IF DitDuration > 330 THEN DitDuration = 330  'limit of 1 second for a dah
                                
                                DitSamples = 8000/1000 * DitDuration  '8000 samples/sec, 1000 msec/sec
                                DahSamples = 3 * DitSamples
                                ShortGap = DitSamples
                                LetterGap = 2 * DitSamples  'should be 3*DitSamples but dot or dash always ends in a 1 Dit gap so only 2 more to add
                                WordGap = 4 * DitSamples  'should be 7*DitSamples but the letter always ends in a 3 Dit gap so only 4 more to add
                                
                                SCALE=2*3.14159265358979323#/8000
                                  
                                p=VARPTR(sound.dta)
                                
                                FOR char = 1 TO LEN(Phrase)
                                    
                                    letter = LCASE$(MID$(Phrase,char,1))
                                            
                                    SELECT CASE letter
                                        CASE "a" TO "z"
                                            symbol = READ$(ASC(letter)-ASC("a")+1)
                                            
                                        CASE "0" TO "9"
                                            symbol = READ$(ASC(letter)-ASC("0")+27)
                                
                                        CASE ELSE
                                            symbol = " "
                                    END SELECT
                                        
                                                  
                                    FOR k = 1 TO LEN (symbol)
                                
                                    'generate the tone samples
                                    SELECT CASE MID$(symbol,k,1)
                                        CASE "."
                                            FOR r = 0 TO DitSamples-1
                                                 @p[r]=volume*(SIN(r * SCALE * Freq) ) +128
                                            NEXT
                                            p = p + DitSamples
                                            TotalSamples = TotalSamples + DitSamples
                                
                                            FOR r = 0 TO ShortGap-1
                                                 @p[r]= 128     'silence
                                            NEXT
                                            p = p + ShortGap
                                            TotalSamples = TotalSamples + ShortGap
                                
                                        CASE "-"
                                            FOR r = 0 TO DahSamples-1
                                                 @p[r]=volume*(SIN(r * SCALE * Freq) ) +128
                                            NEXT
                                            p = p + DahSamples
                                            TotalSamples = TotalSamples + DahSamples
                                
                                            FOR r = 0 TO ShortGap-1
                                                 @p[r]= 128     'silence
                                            NEXT
                                            p = p + DitSamples
                                            TotalSamples = TotalSamples + ShortGap
                                
                                        CASE " "
                                            FOR r = 0 TO WordGap-1
                                                 @p[r]=128     'silence
                                            NEXT
                                            p = p + WordGap
                                            TotalSamples = TotalSamples + WordGap
                                
                                        END SELECT
                                    
                                    NEXT
                                    
                                    FOR r = 0 TO LetterGap-1
                                         @p[r]=128     'silence
                                    NEXT
                                    p = p + LetterGap
                                        TotalSamples = TotalSamples + LetterGap
                                        
                                NEXT
                                    
                                'sort out the wav header
                                sound.wv.rID             = "RIFF"
                                sound.wv.rLen            = 36+TotalSamples
                                sound.wv.wID             = "WAVE"
                                sound.wv.fId             = "fmt "
                                sound.wv.fLen            = 16
                                sound.wv.wFormatTag      = 1
                                sound.wv.nChannels       = 1 'nChannels
                                sound.wv.nSamplesPerSec  = 8000 'nSamplesPerSec
                                sound.wv.nAvgBytesPerSec = 8000  '44100  '88200 'nChannels * nSamplesPerSec * (wBitsPerSample / 8)
                                sound.wv.nBlockAlign     = 1 ' nChannels * (wBitsPerSample / 8)
                                sound.wv.wBitsPerSample  = 8 'wBitsPerSample
                                sound.wv.dId             = "data"
                                sound.wv.dLen            = TotalSamples
                                
                                
                                playsound( BYVAL VARPTR(sound),0 ,%SND_MEMORY OR %SND_ASYNC )
                                
                                     
                                'a-m
                                DATA .-,-...,-.-.,-..,.,..-.,--.,....,..,.---,-.-,.-..,--
                                'n-z
                                DATA -.,---,.--.,--.-,.-.,...,-,..-,...-,.--,-..-,-.--,--..
                                '0-9
                                DATA -----,.----,..---,...--,....-,.....,-....,--...,---..,----.
                                       
                                END SUB

                                Comment


                                • #17
                                  > thought this thread was about "SLEEP" not Morse Code and only used it as an example

                                  Isn't it amazing that when you describe your application instead of/in addition to the function or statement you are currently try to deploy in that application, you end up with more and better suggestions?
                                  Michael Mattias
                                  Tal Systems (retired)
                                  Port Washington WI USA
                                  [email protected]
                                  http://www.talsystems.com

                                  Comment


                                  • #18
                                    >this version generates a wav file of the whole text and plays it through the speakers.

                                    Now THAT is *VERY* cool! Nice work!

                                    MCM

                                    PS:
                                    You might want to post that in the Source Code Forum with a 'searchable' title, eg "Generate and Play Morse Code WAV files from text input"
                                    Last edited by Michael Mattias; 4 Jul 2009, 08:25 AM.
                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment

                                    Working...
                                    X