Author Topic: LCD Clock With Speech And Bouncing Ball  (Read 4032 times)

0 Members and 1 Guest are viewing this topic.

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
LCD Clock With Speech And Bouncing Ball
« on: January 27, 2022, 06:55:28 pm »
Using an old LCD clock I made a few years ago, I added a woman's voice to it when you click it, and a ball that bounces around. The speech reads the title of the month, not the number of the month, as well as the date and time.

Edit: You will need to add this LCD.TTF font file to your folder, or Windows Fonts folder. I think you can just add it to your QB64 folder where you save this app anyway. If not, put it in your Windows Fonts folder that has the rest of your fonts. 

Code: QB64: [Select]
  1. 'By SierraKen on January 27, 2022.
  2. _Title "Digital Time and Date - Click to Listen."
  3.  
  4. Screen _NewImage(400, 200, 32)
  5. rootpath$ = Environ$("SYSTEMROOT") 'normally "C:\WINDOWS"
  6. fontfile$ = rootpath$ + "\Fonts\Lcd.ttf" 'TTF file in Windows
  7. style$ = "bold" 'font style is not case sensitive
  8. f& = _LoadFont(fontfile$, 72, style$)
  9. _Font f& '
  10. ballx = 200: bally = 100
  11. redo:
  12. dx = (Rnd * 5) - 5
  13. dy = (Rnd * 5) - 5
  14. If dx = 0 And dy = 0 Then GoTo redo:
  15. If dx < .25 And dx > -.25 Then GoTo redo:
  16. If dy < .25 And dy > -.25 Then GoTo redo:
  17.  
  18.  
  19. Dim message As String
  20. Dim message2 As String
  21.     _Limit 30
  22.  
  23.     t$ = Time$
  24.     hour$ = Left$(t$, 2)
  25.     h = Val(hour$)
  26.     If h > 11 Then pmam$ = " PM"
  27.     If h < 12 Then pmam$ = " AM"
  28.     If h > 12 Then h = h - 12: hour$ = Str$(h)
  29.     minute$ = Mid$(t$, 4, 2)
  30.     second$ = Right$(t$, 2)
  31.     Color _RGB32(127, 255, 127), _RGB32(0, 0, 0)
  32.     _PrintString (10, 10), hour$ + ":" + minute$ + ":" + second$ + pmam$
  33.     _PrintString (10, 100), Date$
  34.     ball ballx, bally, dx, dy
  35.         mouseX = _MouseX
  36.         mouseY = _MouseY
  37.         mouseLeftButton = _MouseButton(1)
  38.     Loop
  39.     If mouseLeftButton = -1 Then
  40.         mouseLeftButton = 0
  41.         If Left$(Date$, 2) = "01" Then month$ = "January"
  42.         If Left$(Date$, 2) = "02" Then month$ = "February"
  43.         If Left$(Date$, 2) = "03" Then month$ = "March"
  44.         If Left$(Date$, 2) = "04" Then month$ = "April"
  45.         If Left$(Date$, 2) = "05" Then month$ = "May"
  46.         If Left$(Date$, 2) = "06" Then month$ = "June"
  47.         If Left$(Date$, 2) = "07" Then month$ = "July"
  48.         If Left$(Date$, 2) = "08" Then month$ = "August"
  49.         If Left$(Date$, 2) = "09" Then month$ = "September"
  50.         If Left$(Date$, 2) = "10" Then month$ = "October"
  51.         If Left$(Date$, 2) = "11" Then month$ = "November"
  52.         If Left$(Date$, 2) = "12" Then month$ = "December"
  53.         day$ = Mid$(Date$, 4, 2)
  54.         year$ = Right$(Date$, 4)
  55.         message = "The time is " + hour$ + minute$ + pmam$ + " and " + second$ + " seconds."
  56.         speak message
  57.         message = "The date today is " + month$ + " " + day$ + " " + year$
  58.         speak message
  59.     End If
  60.  
  61.  
  62.  
  63.     _Display
  64.     Cls
  65.  
  66.  
  67. Sub ball (ballx, bally, dx, dy)
  68.     _Limit 100
  69.  
  70.     ballx = ballx + dx
  71.     bally = bally + dy
  72.     more:
  73.     If ballx < 0 Then dx = 5: dy = (Rnd * 5) - 5
  74.     If ballx > 400 Then dx = -5: dy = (Rnd * 5) - 5
  75.     If bally < 0 Then dy = 5: dx = (Rnd * 5) - 5
  76.     If bally > 200 Then dy = -5: dx = (Rnd * 5) - 5
  77.     If dx = 0 And dy = 0 Then GoTo more:
  78.     If dx < .25 And dx > -.25 Then GoTo more:
  79.     If dy < .25 And dy > -.25 Then GoTo more:
  80.  
  81.     r = 15
  82.     c = _RGB(0, 0, 255)
  83.     cx = ballx
  84.     cy = bally
  85.     fillCircle cx, cy, r, c
  86.  
  87.     _Display
  88.     Line (0, 0)-(_Width, _Height), _RGB32(0, 0, 0, 20), BF
  89.  
  90.  
  91. 'from Steve Gold standard
  92. Sub fillCircle (CX As Integer, CY As Integer, R As Integer, C As _Unsigned Long)
  93.     Dim Radius As Integer, RadiusError As Integer
  94.     Dim X As Integer, Y As Integer
  95.     Radius = Abs(R): RadiusError = -Radius: X = Radius: Y = 0
  96.     If Radius = 0 Then PSet (CX, CY), C: Exit Sub
  97.     Line (CX - X, CY)-(CX + X, CY), C, BF
  98.     While X > Y
  99.         RadiusError = RadiusError + Y * 2 + 1
  100.         If RadiusError >= 0 Then
  101.             If X <> Y + 1 Then
  102.                 Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  103.                 Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  104.             End If
  105.             X = X - 1
  106.             RadiusError = RadiusError - X * 2
  107.         End If
  108.         Y = Y + 1
  109.         Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  110.         Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  111.     Wend
  112.  
  113. 'Windows only, I think
  114. Sub speak (message As String)
  115.     out$ = "Powershell -Command " + Chr$(34)
  116.     out$ = out$ + "Add-Type -AssemblyName System.Speech; "
  117.     out$ = out$ + "$Speech = New-Object System.Speech.Synthesis.SpeechSynthesizer; "
  118.     out$ = out$ + "$Speech.SelectVoice('Microsoft Zira Desktop'); "
  119.     out$ = out$ + "$Speech.Speak('" + message + "');" + Chr$(34)
  120.     Shell _Hide out$
  121.  
* LCD.TTF (Filesize: 70.98 KB, Downloads: 153)
« Last Edit: January 27, 2022, 10:19:20 pm by SierraKen »

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: LCD Clock With Speech And Bouncing Ball
« Reply #1 on: January 27, 2022, 07:41:17 pm »
hmm... don't have that font on my Windows 10.


Marked as best answer by SierraKen on January 28, 2022, 07:26:39 am

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: LCD Clock With Speech And Bouncing Ball
« Reply #2 on: January 27, 2022, 08:33:15 pm »
Updated this to use _DONTWAIT in the shell call so that your ball won't quit moving and the clock won't stop updating the time.

Code: QB64: [Select]
  1. 'By SierraKen on January 27, 2022.
  2. _Title "Digital Time and Date - Click to Listen."
  3.  
  4. Screen _NewImage(400, 200, 32)
  5. rootpath$ = Environ$("SYSTEMROOT") 'normally "C:\WINDOWS"
  6. fontfile$ = rootpath$ + "\Fonts\courbd.ttf" 'TTF file in Windows
  7. style$ = "bold" 'font style is not case sensitive
  8. f& = _LoadFont(fontfile$, 58, style$)
  9. _Font f& '
  10. ballx = 200: bally = 100
  11. redo:
  12. dx = (Rnd * 5) - 5
  13. dy = (Rnd * 5) - 5
  14. If dx = 0 And dy = 0 Then GoTo redo:
  15. If dx < .25 And dx > -.25 Then GoTo redo:
  16. If dy < .25 And dy > -.25 Then GoTo redo:
  17.  
  18.  
  19. Dim message As String
  20. Dim message2 As String
  21.  
  22. Color _RGB32(127, 255, 127), _RGB32(0, 0, 0)
  23.         mouseX = _MouseX
  24.         mouseY = _MouseY
  25.         mouseLeftButton = _MouseButton(1)
  26.     Loop
  27.     If mouseLeftButton = -1 And SpeakTime = 0 Then 'Don't spam speak if the user goes crazy with mouse clicks!
  28.         mouseLeftButton = 0
  29.         If Left$(Date$, 2) = "01" Then month$ = "January"
  30.         If Left$(Date$, 2) = "02" Then month$ = "February"
  31.         If Left$(Date$, 2) = "03" Then month$ = "March"
  32.         If Left$(Date$, 2) = "04" Then month$ = "April"
  33.         If Left$(Date$, 2) = "05" Then month$ = "May"
  34.         If Left$(Date$, 2) = "06" Then month$ = "June"
  35.         If Left$(Date$, 2) = "07" Then month$ = "July"
  36.         If Left$(Date$, 2) = "08" Then month$ = "August"
  37.         If Left$(Date$, 2) = "09" Then month$ = "September"
  38.         If Left$(Date$, 2) = "10" Then month$ = "October"
  39.         If Left$(Date$, 2) = "11" Then month$ = "November"
  40.         If Left$(Date$, 2) = "12" Then month$ = "December"
  41.         day$ = Mid$(Date$, 4, 2)
  42.         year$ = Right$(Date$, 4)
  43.         SpeakTime = 1 'Set the flag to speak the time.
  44.     End If
  45.     _Limit 30
  46.  
  47.     t$ = Time$
  48.     hour$ = Left$(t$, 2)
  49.     h = Val(hour$)
  50.     If h > 11 Then pmam$ = " PM" Else pmam$ = " AM"
  51.     If h > 12 Then h = h - 12: hour$ = Str$(h)
  52.     minute$ = Mid$(t$, 4, 2)
  53.     second$ = Right$(t$, 2)
  54.  
  55.     If SpeakTime = 1 Then 'Speak the first line
  56.         StartTimer = Timer + 4: SpeakTime = 2 'set the flag to wait 4 seconds before speaking the second line
  57.         message = "The time is " + hour$ + minute$ + pmam$ + " and " + second$ + " seconds."
  58.         speak message
  59.     ElseIf SpeakTime = 2 And Timer > StartTimer Then 'speak the second line
  60.         StartTimer = Timer + 4: SpeakTime = 3 'wait 4 seconds for the second line to finish.
  61.         message = "The date today is " + month$ + " " + day$ + " " + year$
  62.         speak message
  63.     ElseIf SpeakTime = 3 And Timer > StartTimer Then
  64.         SpeakTime = 0 'we're finished speaking.  Allow the user to click the clock again if they want.
  65.     End If
  66.  
  67.     _PrintString (10, 10), hour$ + ":" + minute$ + ":" + second$ + pmam$
  68.     _PrintString (10, 100), Date$
  69.     ball ballx, bally, dx, dy
  70.  
  71.     _Display
  72.     Cls
  73.  
  74.  
  75. Sub ball (ballx, bally, dx, dy)
  76.     ballx = ballx + dx
  77.     bally = bally + dy
  78.     more:
  79.     If ballx < 0 Then dx = 5: dy = (Rnd * 5) - 5
  80.     If ballx > 400 Then dx = -5: dy = (Rnd * 5) - 5
  81.     If bally < 0 Then dy = 5: dx = (Rnd * 5) - 5
  82.     If bally > 200 Then dy = -5: dx = (Rnd * 5) - 5
  83.     If dx = 0 And dy = 0 Then GoTo more:
  84.     If dx < .25 And dx > -.25 Then GoTo more:
  85.     If dy < .25 And dy > -.25 Then GoTo more:
  86.  
  87.     r = 15
  88.     c = _RGB(0, 0, 255)
  89.     cx = ballx
  90.     cy = bally
  91.     fillCircle cx, cy, r, c
  92.     Line (0, 0)-(_Width, _Height), _RGB32(0, 0, 0, 20), BF
  93.  
  94.  
  95. 'from Steve Gold standard
  96. Sub fillCircle (CX As Integer, CY As Integer, R As Integer, C As _Unsigned Long)
  97.     Dim Radius As Integer, RadiusError As Integer
  98.     Dim X As Integer, Y As Integer
  99.     Radius = Abs(R): RadiusError = -Radius: X = Radius: Y = 0
  100.     If Radius = 0 Then PSet (CX, CY), C: Exit Sub
  101.     Line (CX - X, CY)-(CX + X, CY), C, BF
  102.     While X > Y
  103.         RadiusError = RadiusError + Y * 2 + 1
  104.         If RadiusError >= 0 Then
  105.             If X <> Y + 1 Then
  106.                 Line (CX - Y, CY - X)-(CX + Y, CY - X), C, BF
  107.                 Line (CX - Y, CY + X)-(CX + Y, CY + X), C, BF
  108.             End If
  109.             X = X - 1
  110.             RadiusError = RadiusError - X * 2
  111.         End If
  112.         Y = Y + 1
  113.         Line (CX - X, CY - Y)-(CX + X, CY - Y), C, BF
  114.         Line (CX - X, CY + Y)-(CX + X, CY + Y), C, BF
  115.     Wend
  116.  
  117. 'Windows only, I think
  118. Sub speak (message As String)
  119.     out$ = "Powershell -Command " + Chr$(34)
  120.     out$ = out$ + "Add-Type -AssemblyName System.Speech; "
  121.     out$ = out$ + "$Speech = New-Object System.Speech.Synthesis.SpeechSynthesizer; "
  122.     out$ = out$ + "$Speech.SelectVoice('Microsoft Zira Desktop'); "
  123.     out$ = out$ + "$Speech.Speak('" + message + "');" + Chr$(34)
  124.  

Note that I'm manually having to allocate some time so the voice has time to process and speak here.  On my system, 4 seconds seems more than enough for each line to process and do their thing.  Older, slower computers might need an increase in that delay a bit so words don't get jumbled together or processed at the same time, since there's no callback at work to actually let us know when the text to speech is finished.

If you look, I've also reordered the order in which we process things here.  There's a noticable lag in when we click the time and when the Text To Speech reads the time, which ends up with the second being voiced being a few seconds behind what is displayed on the screen.  (It takes a bit to say "The time is Blah: Blah: Blah M and Blah seconds", so the voice is out of sync with the display.)  I reordered things to try and minimize this disparity as much as possible, but it's still about always a 4 second lag. 

If testing on various machines proves this to *always* be a 4 second lag, one simple solution would be to just add 4 seconds to the timer before creating the string to speak.  Then when Ziva says "thirty-two seconds", the display will read 32 seconds and not 36 (because you clicked it back at the 32 second mark and it took that long to say everything else first.)  Not sure if it's even an issue that would bug anyone else, or if it's just one of those little personal quirks of mine.  Anywho, I thought I'd point it out and explain why the number and voice doesn't match for things currently.  ;)

Fix it if you like.  If not, just leave it be the way it is.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: LCD Clock With Speech And Bouncing Ball
« Reply #3 on: January 27, 2022, 10:22:13 pm »
Thanks for telling me B+. It must be a font I once got online a long time ago. I just added it to the first post for people to download.

Steve, whenever I used _DONTWAIT, the voice overlapped onto itself and made a real thick woman's voice. I also tried _DELAYs like you mention, but that was no different than waiting anyways without a _DONTWAIT. I think you are right though, every different type of computer does slightly different things with the speed of the voice. I'm just going to leave it as it is. But I appreciate the suggestions.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: LCD Clock With Speech And Bouncing Ball
« Reply #4 on: January 28, 2022, 03:22:52 am »
Did you even try my code above? It solves your overlap problem without the use of any _DELAY statement by timed execution of your calls.

    If SpeakTime = 1 Then 'Speak the first line
        StartTimer = Timer + 4: SpeakTime = 2 'set the flag to wait 4 seconds before speaking the second line
        message = "The time is " + hour$ + minute$ + pmam$ + " and " + second$ + " seconds."
        speak message
    ElseIf SpeakTime = 2 And Timer > StartTimer Then 'speak the second line
        StartTimer = Timer + 4: SpeakTime = 3 'wait 4 seconds for the second line to finish.
        message = "The date today is " + month$ + " " + day$ + " " + year$
        speak message
    ElseIf SpeakTime = 3 And Timer > StartTimer Then
        SpeakTime = 0 'we're finished speaking.  Allow the user to click the clock again if they want.
    End If

First, it sets a flag to start the first voice when the mouse is clicked.  Then it starts a timer.  Four seconds later it starts the second voice.  Four seconds after that, it resets the flag and allows mouse clicks again.

_DELAY completely stops program execution.  When you want the process to keep going, you introduce timed responses similar to above to prevent the overlapping effect.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: LCD Clock With Speech And Bouncing Ball
« Reply #5 on: January 28, 2022, 12:26:30 pm »
Wow I just tried yours Steve, thank you! Yesterday I had a lot on my mind with my neighbor passing away. I woke up this morning and the first thing that entered my mind was, "You were wrong about the speech line being too long, it wasn't the speech line, it was the 's in date's that messed it up." So I just tried my version with just going to the SUB once and it worked. Then I tried your version and yours is better because you use the TIMER and DONTWAIT to keep the ball rolling. Thank you! I'll make yours the best one on the thread because it also doesn't need the added LCD font. Of course we could change yours so it only goes to the SUB once, but that's a bit too picky and wasted time, in my opinion.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: LCD Clock With Speech And Bouncing Ball
« Reply #6 on: January 28, 2022, 01:08:01 pm »
Wow I just tried yours Steve, thank you! Yesterday I had a lot on my mind with my neighbor passing away. I woke up this morning and the first thing that entered my mind was, "You were wrong about the speech line being too long, it wasn't the speech line, it was the 's in date's that messed it up." So I just tried my version with just going to the SUB once and it worked. Then I tried your version and yours is better because you use the TIMER and DONTWAIT to keep the ball rolling. Thank you! I'll make yours the best one on the thread because it also doesn't need the added LCD font. Of course we could change yours so it only goes to the SUB once, but that's a bit too picky and wasted time, in my opinion.

Powershell has several options which you can use as command delimiters.  quotes, single quotes, brackets all seem to work the same way, more or less, from my testing.   One is a START whatever and the second is an END whatever, so things like the following are all valid:

File.Open = "C:\foo directory\foo the file.txt"
File.Open = 'C:\foo directory\foo the file.txt"
File.Open = {C:\foo directory\foo the file.txt}

Now. someone with more experience than me with Powershell scripts might be able to explain the difference in all those delimiters, but all I know is from my limited amount of playing around with them, they all seem to act the same to group the contents between them into one chunk of data.

The reason why quotes won't work in your SHELL call is from the fact that you're wrapping the whole shell statement inside quotes.

    out$ = "Powershell -Command " + Chr$(34)
    out$ = out$ + ...more stuff... + Chr$(34)

A set of stray quotes in the middle of those will mess up the formatting and leave powershell erroring out on what you're actually wanting it to do.

Single quotes are the same way 00 they're already being used in your code:

   out$ = out$ + "$Speech.SelectVoice('Microsoft Zira Desktop'); "

Toss an additional apostrophe in there, and it'll view that as the start of your delimiter, and formatting will get all confuzzled and goof up.

Quotes won't work.  Single Quotes won't work.  Brackets won't work.  I don't think the @ symbol works (if I remember correctly, it's got some reserved purpose as well).   There's several command keys reserved for use with Powershell, and if you're going to let the user type in whatever they want to read it back to them, you'll want to research what all of those might be.   Honestly, I even doubt that less than and greater than symbols will work without needing adjustment.  "<" and ">" are used in html to delimit commands, and windows speech has it's own form for html which it supports, making use of the same style syntax for SSML (Speech Synthesis Markup Language).

https://docs.microsoft.com/en-us/azure/cognitive-services/speech-service/speech-synthesis-markup?tabs=csharp

As I mentioned before, Speech Synthesis in Windows is quite a powerful little tool, once you start delving into the depths of what it can actually do for you!  Here's an example of some SSML written:

Code: Text: [Select]
  1. <speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
  2.        xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">
  3.     <voice name="en-US-AriaNeural">
  4.         <mstts:express-as style="cheerful">
  5.             That'd be just amazing!
  6.         </mstts:express-as>
  7.     </voice>
  8. </speak>

Notice how we're choosing Aria to speak, and we're telling to speak in a cheeful tone!!

And, as she says, "That'd be just amazing!"  Much more than just some lame old "text to speak" robotic voice which we used to hear when talking about speech synthesis!  ;)



End end point is just:  Be careful with what you allow in your text to be spoken.  There's a TON of reserved characters and delimiters and such that can't just be inserted and tossed around all willy-nilly wherever someone wants.  If you're going to write something to let the user enter whatever they want, you'll have to validate and cleanse their input to make it suitable for the syntax which the speech synthesizer expects to find and work with. 
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SierraKen

  • Forum Resident
  • Posts: 1454
    • View Profile
Re: LCD Clock With Speech And Bouncing Ball
« Reply #7 on: January 28, 2022, 02:47:49 pm »
True, the easiest way to scan the text for symbols would be using the ASCII chart and CHR$. I also found out that if you need an apostrophe in a word to talk with, just don't use it, like date's sounds the same as dates. And don't should sound the same as dont I would think.

Offline SquirrelMonkey

  • Newbie
  • Posts: 29
  • Youtuber and GIPHY artist
    • View Profile
    • Joluijten.com
Re: LCD Clock With Speech And Bouncing Ball
« Reply #8 on: January 28, 2022, 03:01:47 pm »
Looks great!