Adding speech to MMBasic

Yup, it’s another “let’s wire up a SYN6988 board” thing, this time for MMBasic running on the Armmite STM32F407 Module (aka ‘Armmite F4’). This board is also known as the BLACK_F407VE, which also makes a nice little MicroPython platform.

Uh, let’s not dwell too much on how the SYN6988 seems to parse 19:51 as “91 minutes to 20” …

Wiring

SYN6988Armmite F4
RXPA09 (COM1 TX)
TXPA10 (COM1 RX)
RDYPA08
your choice of 3.3 V and GND connections, of course

Where to buy: AliExpress — KAIKAI Electronics Wholesale Store : High-end Speech Synthesis Module Chinese/English Speech Synthesis XFS5152 Real Pronunciation TTS

Yes, I know it says it’s an XFS5152, but I got a SYN6988 and it seems to be about as reliable a source as one can find. The board is marked YS-V6E-V1.03, and even mentions SYN6988 on the rear silkscreen:

Code

REM                 SYN6988 speech demo - MMBasic / Armmite F4
REM                 scruss, 2023-07

OPEN "COM1:9600" AS #5
REM                 READY line on PA8
SETPIN PA8, DIN, PULLUP

REM    you can ignore font/text commands
CLS
FONT 1
TEXT 0,15,"[v1]Hello - this is a speech demo."
say("[v1]Hello - this is a speech demo.")
TEXT 0,30,"[x1]soundy[d]"
say("[x1]soundy[d]"): REM    chimes
TEXT 0,45,"The time is "+LEFT$(TIME$,5)+"."
say("The time is "+LEFT$(TIME$,5)+".")
END

SUB say(a$)
  LOCAL dl%,maxlof%
  REM     data length is text length + 2 (for the 1 and 0 bytes)
  dl%=2+LEN(a$)
  maxlof%=LOF(#5)
  REM     SYN6988 simple data packet
  REM      byte  1 : &HFD
  REM      byte  2 : data length (high byte)
  REM      byte  3 : data length (low byte)
  REM      byte  4 : &H01
  REM      byte  5 : &H00
  REM      bytes 6-: ASCII string data
  PRINT #5, CHR$(&hFD)+CHR$(dl%\256)+CHR$(dl% MOD 256)+CHR$(1)+CHR$(0)+a$;
  DO WHILE LOF(#5)<maxlof%
  REM       pause while sending text
    PAUSE 5
  LOOP
  DO WHILE PIN(PA8)<>1
    REM       wait until RDY is high
    PAUSE 5
  LOOP
  DO WHILE PIN(PA8)<>0
    REM       wait until SYN6988 signals READY
    PAUSE 5
  LOOP
END SUB

For more commands, please see Embedded text commands

Heres the auto-translated manual for the SYN6988:

Autumn in Canada: PicoMite version

more leaves

So I ported autumn in canada from OpenProcessing to PicoMite BASIC on the Raspberry Pi Pico:

a small black screen images with text in the centre: autumn in canada scruss, 2021-11 just watch ...
no leaves
a small black screen images with text in the centre: autumn in canada scruss, 2021-11 just watch ... with one red and one orange maple leaf sitting on top of it
a couple of leaves
a small black screen images with text in the centre: autumn in canada scruss, 2021-11 just watch ... with four red/yellow/orange maple leaves sitting on top of it
more leaves
a small black screen images with text in the centre: autumn in canada scruss, 2021-11 just watch ... with sixteen simulated fallen maple leaves mostly covering it
plenty of leaves
a small black screen image completely covered with many simulated fallen maple leaves
far too many leaves

The biggest thing that tripped me up was that PicoMite BASIC starts arrays at 0. OPTION BASE 1 fixes that oversight. It would have been nice to have OpenProcessing’s HSV colour space, and an editor that could handle lines longer than 80 characters that didn’t threaten to bomb out if you hit the End key, but it’ll serve.

Source below:

' autumn in canada
' scruss, 2021-11
' a take on my https://openprocessing.org/sketch/995420 for picomite

OPTION base 1
RANDOMIZE TIMER
' *** initialize polar coords of leaf polygon and colour array
DIM leaf_rad(24), leaf_ang(24), px%(24), py%(24)
FOR i=1 TO 24
    READ leaf_rad(i)
NEXT i
FOR i=1 TO 24
    READ x
    leaf_ang(i)=RAD(x)
NEXT i

DIM integer c%(8)
FOR i=1 TO 8
    READ r%, g%, b%
    c%(i)=RGB(r%,g%,b%)
NEXT i

' *** set up some limits
min_scale%=INT(MIN(MM.HRES, MM.VRES)/8)
max_scale%=INT(MIN(MM.HRES, MM.VRES)/6)
min_angle=-30
max_angle=30
min_x%=min_scale%
min_y%=min_x%
max_x%=MM.HRES - min_x%
max_y%=MM.VRES - min_y%

CLS
TEXT MM.HRES/2, INT(MM.VRES/3), "autumn in canada", "CM"
TEXT MM.HRES/2, INT(MM.VRES/2), "scruss, 2021-11", "CM"
TEXT MM.HRES/2, INT(2*MM.VRES/3), "just watch ...", "CM"

kt%=0
DO
    cx% = min_x% + INT(RND * (max_x% - min_x%))
    cy% = min_y% + INT(RND * (max_y% - min_y%))
    angle = min_angle + RND * (max_angle - min_angle)
    sc% = min_scale% + INT(RND * (max_scale% - min_scale%))
    col% = 1 + INT(RND * 7)
    leaf cx%, cy%, sc%, angle, c%(7), c%(col%)
    kt% = kt% + 1
LOOP UNTIL kt% >= 1024

END

SUB leaf x%, y%, scale%, angle, outline%, fill%
    FOR i=1 TO 24
        px%(i) = INT(x% + scale% * leaf_rad(i) * COS(RAD(angle)+leaf_ang(i)))
        py%(i) = INT(y% - scale% * leaf_rad(i) * SIN(RAD(angle)+leaf_ang(i)))
    NEXT i
    POLYGON 24, px%(), py%(), outline%, fill%
END SUB

' radii
DATA 0.536, 0.744, 0.608, 0.850, 0.719
DATA 0.836, 0.565, 0.589, 0.211, 0.660, 0.515
DATA 0.801, 0.515, 0.660, 0.211, 0.589, 0.565
DATA 0.836, 0.719, 0.850, 0.608, 0.744, 0.536, 1.000
' angles
DATA 270.000, 307.249, 312.110, 353.267, 356.540
DATA 16.530, 18.774, 33.215, 3.497, 60.659, 72.514
DATA 90.000, 107.486, 119.341, 176.503, 146.785, 161.226
DATA 163.470, 183.460, 186.733, 227.890, 232.751, 270.000, 270.000
' leaf colours
DATA 255,0,0, 255,36,0, 255,72,0, 255,109,0
DATA 255,145,0, 255,182,0, 255,218,0, 255,255,0

You could probably use AUTOSAVE and paste the text into the PicoMite REPL. I used an ILI9341 SPI TFT LCD Touch Panel with my Raspberry Pi Pico along with some rather messy breadboard wiring.

Fun fact: the maple leaf polygon points are derived from the official definition of the flag of Canada.