Page 1 of 2

Voice synthesis, part 1

Posted: Tue Mar 29, 2011 5:18 am
by sje
Some commercial dedicated chess computers first offered voice output of moves some thirty years ago and this feature can be found on some of today's units.

Naturally, I had to have this feature in my program. But first I had to code a routine that generated the long text form of the move suitable for input to a voice synthesizer.

At the time back in 1987 or so, I bought a dedicated voice synthesizer based on a 2 MHz Mosfet 6502 microprocessor with an 8 KB program. The unit connected via a 9,600 bps serial link to the machine running the chess program. The synthesizer wasn't very sophisticated, so the long text form of a move had to be encoded using phoneme codes instead of pure ASCII English text. Today's synthesizers are much better and the phoneme codes aren't needed for casual work.

Here's the main long text move encoding routine form the new CIL toolkit:

Code: Select all

(defun encode-move-longtext-aux (my-stream my-move)
  "Encode the long text form of a non-null, non-void chess move on a stream."
  (let*
    (
      (frsq   (move-frsq   my-move))
      (tosq   (move-tosq   my-move))
      (frman  (move-frman  my-move))
      (toman  (move-toman  my-move))
      (msc    (move-msc    my-move))
      (good   (move->mover my-move))
      (evil   (other-color good))
    )
    (cond
      ;
      ; Regular move
      ;
      ((is-msc-regular? msc)
        (fmt-strings my-stream "The " (man->longtext frman) " at square " (sq->string frsq))
        (if (is-man-vacant? toman)
          (fmt-strings my-stream " moves to square " (sq->string tosq))
          (fmt-strings my-stream " captures the " (man->longtext toman) " at square " (sq->string tosq))))
      ;
      ; En passant move
      ;
      ((is-msc-en-passant? msc)
        (fmt-strings my-stream
          "The " (man->longtext frman) " at square " (sq->string frsq)
          " moves to square " (sq->string tosq)
          " and captures the " (man->longtext (color->pawn evil))
          " at square " (sq->string (calc-ep-victim-sq good tosq))
          " en passant"))
      ;
      ; Castling move
      ;
      ((is-msc-castling? msc)
        (fmt-strings my-stream (player->string good) " castles " (flank->string (msc->castling-flank msc))))
      ;
      ; Promotion move
      ;
      ((is-msc-promotion? msc)
        (fmt-strings my-stream "The " (man->longtext frman) " at square " (sq->string frsq))
        (if (is-man-vacant? toman)
          (fmt-strings my-stream " moves to square " (sq->string tosq))
          (fmt-strings my-stream " captures the " (man->longtext toman) " at square " (sq->string tosq)))
        (fmt-strings my-stream " and promotes to a " (piece->string (prom->piece (msc->prom msc)))))
      ;
      (t
        (error "encode-move-longtext-aux: bad msc: ~S" msc)))
    ;
    ; Stalemates, checkmates, and checks 
    ;
    (cond
      ((is-move-marked-stalemate? my-move)
        (fmt-string my-stream " giving stalemate"))
      ((is-move-marked-checkmate? my-move)
        (fmt-string my-stream " giving checkmate"))
      ((is-move-marked-check? my-move)
        (fmt-string my-stream " giving check"))
      (t
        nil))
    ;
    ; Final stop
    ;
    (fmt-string my-stream "."))
  (values))
For the position BWTC.0907:
[D]6n1/5P1k/7p/np4b1/3B4/1pP4P/5PP1/1b4K1 w - - 0 1

Code: Select all

* (setf p (string->pos "6n1/5P1k/7p/np4b1/3B4/1pP4P/5PP1/1b4K1 w - - 0 1"))
6n1/5P1k/7p/np4b1/3B4/1pP4P/5PP1/1b4K1 w - - 0 1
* (setf ml (generate-canonical p))
(Ba7 Bb6 Bc5 Be3 Be5 Bf6 Bg7 Bh8 Kf1 Kh1 Kh2 c4 f3 f4 f8=B f8=N# f8=Q f8=R fxg8=B+ fxg8=N fxg8=Q+ fxg8=R g3 g4 h4)
* (dolist (move ml) (format t "~A~%" (move->longtext move)))
The white bishop at square d4 moves to square a7.
The white bishop at square d4 moves to square b6.
The white bishop at square d4 moves to square c5.
The white bishop at square d4 moves to square e3.
The white bishop at square d4 moves to square e5.
The white bishop at square d4 moves to square f6.
The white bishop at square d4 moves to square g7.
The white bishop at square d4 moves to square h8.
The white king at square g1 moves to square f1.
The white king at square g1 moves to square h1.
The white king at square g1 moves to square h2.
The white pawn at square c3 moves to square c4.
The white pawn at square f2 moves to square f3.
The white pawn at square f2 moves to square f4.
The white pawn at square f7 moves to square f8 and promotes to a bishop.
The white pawn at square f7 moves to square f8 and promotes to a knight giving checkmate.
The white pawn at square f7 moves to square f8 and promotes to a queen.
The white pawn at square f7 moves to square f8 and promotes to a rook.
The white pawn at square f7 captures the black knight at square g8 and promotes to a bishop giving check.
The white pawn at square f7 captures the black knight at square g8 and promotes to a knight.
The white pawn at square f7 captures the black knight at square g8 and promotes to a queen giving check.
The white pawn at square f7 captures the black knight at square g8 and promotes to a rook.
The white pawn at square g2 moves to square g3.
The white pawn at square g2 moves to square g4.
The white pawn at square h3 moves to square h4.

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 7:04 am
by bob
sje wrote:Some commercial dedicated chess computers first offered voice output of moves some thirty years ago and this feature can be found on some of today's units.

Naturally, I had to have this feature in my program. But first I had to code a routine that generated the long text form of the move suitable for input to a voice synthesizer.

At the time back in 1987 or so, I bought a dedicated voice synthesizer based on a 2 MHz Mosfet 6502 microprocessor with an 8 KB program. The unit connected via a 9,600 bps serial link to the machine running the chess program. The synthesizer wasn't very sophisticated, so the long text form of a move had to be encoded using phoneme codes instead of pure ASCII English text. Today's synthesizers are much better and the phoneme codes aren't needed for casual work.
Back in the old altair/Imsai days (s100 bus) I had a board made by a company called "computalker". Worked well. You spat a string of phoneme codes (that were actually pretty readable) and it would talk pretty reasonably. I used it for demos with cray blitz every now and then and had it announce mated. The current version of Crafty has been able to speak for years, of course..



Here's the main long text move encoding routine form the new CIL toolkit:

Code: Select all

(defun encode-move-longtext-aux (my-stream my-move)
  "Encode the long text form of a non-null, non-void chess move on a stream."
  (let*
    (
      (frsq   (move-frsq   my-move))
      (tosq   (move-tosq   my-move))
      (frman  (move-frman  my-move))
      (toman  (move-toman  my-move))
      (msc    (move-msc    my-move))
      (good   (move->mover my-move))
      (evil   (other-color good))
    )
    (cond
      ;
      ; Regular move
      ;
      ((is-msc-regular? msc)
        (fmt-strings my-stream "The " (man->longtext frman) " at square " (sq->string frsq))
        (if (is-man-vacant? toman)
          (fmt-strings my-stream " moves to square " (sq->string tosq))
          (fmt-strings my-stream " captures the " (man->longtext toman) " at square " (sq->string tosq))))
      ;
      ; En passant move
      ;
      ((is-msc-en-passant? msc)
        (fmt-strings my-stream
          "The " (man->longtext frman) " at square " (sq->string frsq)
          " moves to square " (sq->string tosq)
          " and captures the " (man->longtext (color->pawn evil))
          " at square " (sq->string (calc-ep-victim-sq good tosq))
          " en passant"))
      ;
      ; Castling move
      ;
      ((is-msc-castling? msc)
        (fmt-strings my-stream (player->string good) " castles " (flank->string (msc->castling-flank msc))))
      ;
      ; Promotion move
      ;
      ((is-msc-promotion? msc)
        (fmt-strings my-stream "The " (man->longtext frman) " at square " (sq->string frsq))
        (if (is-man-vacant? toman)
          (fmt-strings my-stream " moves to square " (sq->string tosq))
          (fmt-strings my-stream " captures the " (man->longtext toman) " at square " (sq->string tosq)))
        (fmt-strings my-stream " and promotes to a " (piece->string (prom->piece (msc->prom msc)))))
      ;
      (t
        (error "encode-move-longtext-aux: bad msc: ~S" msc)))
    ;
    ; Stalemates, checkmates, and checks 
    ;
    (cond
      ((is-move-marked-stalemate? my-move)
        (fmt-string my-stream " giving stalemate"))
      ((is-move-marked-checkmate? my-move)
        (fmt-string my-stream " giving checkmate"))
      ((is-move-marked-check? my-move)
        (fmt-string my-stream " giving check"))
      (t
        nil))
    ;
    ; Final stop
    ;
    (fmt-string my-stream "."))
  (values))
For the position BWTC.0907:
[D]6n1/5P1k/7p/np4b1/3B4/1pP4P/5PP1/1b4K1 w - - 0 1

Code: Select all

* (setf p (string->pos "6n1/5P1k/7p/np4b1/3B4/1pP4P/5PP1/1b4K1 w - - 0 1"))
6n1/5P1k/7p/np4b1/3B4/1pP4P/5PP1/1b4K1 w - - 0 1
* (setf ml (generate-canonical p))
(Ba7 Bb6 Bc5 Be3 Be5 Bf6 Bg7 Bh8 Kf1 Kh1 Kh2 c4 f3 f4 f8=B f8=N# f8=Q f8=R fxg8=B+ fxg8=N fxg8=Q+ fxg8=R g3 g4 h4)
* (dolist (move ml) (format t "~A~%" (move->longtext move)))
The white bishop at square d4 moves to square a7.
The white bishop at square d4 moves to square b6.
The white bishop at square d4 moves to square c5.
The white bishop at square d4 moves to square e3.
The white bishop at square d4 moves to square e5.
The white bishop at square d4 moves to square f6.
The white bishop at square d4 moves to square g7.
The white bishop at square d4 moves to square h8.
The white king at square g1 moves to square f1.
The white king at square g1 moves to square h1.
The white king at square g1 moves to square h2.
The white pawn at square c3 moves to square c4.
The white pawn at square f2 moves to square f3.
The white pawn at square f2 moves to square f4.
The white pawn at square f7 moves to square f8 and promotes to a bishop.
The white pawn at square f7 moves to square f8 and promotes to a knight giving checkmate.
The white pawn at square f7 moves to square f8 and promotes to a queen.
The white pawn at square f7 moves to square f8 and promotes to a rook.
The white pawn at square f7 captures the black knight at square g8 and promotes to a bishop giving check.
The white pawn at square f7 captures the black knight at square g8 and promotes to a knight.
The white pawn at square f7 captures the black knight at square g8 and promotes to a queen giving check.
The white pawn at square f7 captures the black knight at square g8 and promotes to a rook.
The white pawn at square g2 moves to square g3.
The white pawn at square g2 moves to square g4.
The white pawn at square h3 moves to square h4.

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 7:57 am
by sje
I remember the Altair/Imsai days as I worked with a few Altair 8800 machines and I built an Imsai 8080 from parts when the kit was first announced. However, I never got my Imsai to speak as being a dirt poor student I couldn't afford any peripherals or even any boards other than a 4 KB RAM board. I still had a lot of fun with the front panel, though.

I vaguely remember the Computalker board. Didn't it use the voice chip made by Vortax? Vortax themselves also sold a standalone unit.

My 6502 synthesizer was purchased from Steve Ciarcia's Circuit Cellar (they were still around the last time I checked) although I'm sure they dropped it and other kits long, long ago. The software was as I remember the same as the voice software in the Apple II machine series. This made sense as the synthesizer was a stripped down version of an Apple I sans keyboard and display subsystems.

As to how to do voice output today, that's in part 2 of this topic to be written after a bit more testing on my part.

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 9:15 am
by hgm
Seems to me that this is a fuction that logically belongs in the interface. When you are putting it in your engine, it ceases to be an egine, and you are developing it towards a stand-alone Chess program.

WinBoard for JAWS supports voice output in any mode (game viewer, engine or ICS client).

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 11:40 am
by sje
The above code is nearly stand-alone, so it can appear in a toolkit, a chess program, or even an interface.

Not all target platforms have a third party interface available. Some don't have X Windows or perhaps any graphics capability at all. Some don't even have an operating system!

The corresponding SAN encoder

Posted: Tue Mar 29, 2011 11:46 am
by sje
Here is the corresponding SAN encoding routine:

Code: Select all

;; ---------- SAN move encoding

(defun encode-move-aux (my-stream my-move)
  "Encode the SAN form of a non-null, non-void chess move on a stream."
  (let
    (
      (frsq   (move-frsq   my-move))
      (tosq   (move-tosq   my-move))
      (frman  (move-frman  my-move))
      (toman  (move-toman  my-move))
      (msc    (move-msc    my-move))
      (mfbits (move-mfbits my-move))
    )
    ;
    (when (is-mf-set? mfbits mf-bust)
      (fmt-string my-stream "*"))
    ;
    (cond
      ;
      ; Regular move
      ;
      ((is-msc-regular? msc)
        (if (is-man-pawn? frman)
          (progn
            (fmt-string my-stream (file->string (sq->file frsq)))
            (if (is-man-vacant? toman)
              (fmt-string my-stream (rank->string (sq->rank tosq)))
              (fmt-strings my-stream "x" (sq->string tosq))))
          (progn
            (fmt-string my-stream (string (piece->char (man->piece frman))))
            (when (is-mf-set? mfbits mf-andf)
              (fmt-string my-stream (file->string (sq->file frsq))))
            (when (is-mf-set? mfbits mf-andr)
              (fmt-string my-stream (rank->string (sq->rank frsq))))
            (unless (is-man-vacant? toman)
              (fmt-string my-stream "x"))
            (fmt-string my-stream (sq->string tosq)))))
      ;
      ; En passant move
      ;
      ((is-msc-en-passant? msc)
        (fmt-strings my-stream
          (file->string (sq->file frsq)) "x" (sq->string (calc-ep-victim-sq (man->color frman) tosq))))
      ;
      ; Castling move
      ;
      ((is-msc-castling? msc)
        (fmt-string my-stream (flank->castling-string (msc->castling-flank msc))))
      ;
      ; Promotion move
      ;
      ((is-msc-promotion? msc)
        (fmt-string my-stream (file->string (sq->file frsq)))
        (if (is-man-vacant? toman)
          (fmt-string my-stream (rank->string (sq->rank tosq)))
          (fmt-strings my-stream "x" (sq->string tosq)))
        (fmt-strings my-stream "=" (string (piece->char (prom->piece (msc->prom msc))))))
      ;
      (t
        (error "encode-move-aux: bad msc: ~S" msc)))
    ;
    ; Checks and checkmates
    ;
    (when (is-move-marked-check? my-move)
      (fmt-string my-stream (if (is-move-marked-checkmate? my-move) "#" "+"))))
  (values))

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 12:01 pm
by smatovic
I remember the Altair/Imsai days as I worked with a few Altair 8800 machines and I built an Imsai 8080 from parts when the kit was first announced. However, I never got my Imsai to speak as being a dirt poor student I couldn't afford any peripherals or even any boards other than a 4 KB RAM board. I still had a lot of fun with the front panel, though.
Not to forget the Amiga Series from Commodore in the early 90s. I had fun with their onboard "Paula"-Soundchip (8 bit Pulse Code Modulation). Unfortunely only the Atari ST had built in MIDI :-(

--
Srdja

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 1:10 pm
by sje
smatovic wrote:Not to forget the Amiga Series from Commodore in the early 90s. I had fun with their onboard "Paula"-Soundchip (8 bit Pulse Code Modulation).
I have two Amigas (Amigae?). One is an A1000 (and I have no software for it, not a single floppy); the other is an A600. Neither has been powered up in this millennium.

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 6:35 pm
by bob
sje wrote:I remember the Altair/Imsai days as I worked with a few Altair 8800 machines and I built an Imsai 8080 from parts when the kit was first announced. However, I never got my Imsai to speak as being a dirt poor student I couldn't afford any peripherals or even any boards other than a 4 KB RAM board. I still had a lot of fun with the front panel, though.

I vaguely remember the Computalker board. Didn't it use the voice chip made by Vortax? Vortax themselves also sold a standalone unit.
No idea. The entire circuitry on the board was embedded in a block of some sort of polymer so that nothing at all was visible...


[qote]

My 6502 synthesizer was purchased from Steve Ciarcia's Circuit Cellar (they were still around the last time I checked) although I'm sure they dropped it and other kits long, long ago. The software was as I remember the same as the voice software in the Apple II machine series. This made sense as the synthesizer was a stripped down version of an Apple I sans keyboard and display subsystems.

As to how to do voice output today, that's in part 2 of this topic to be written after a bit more testing on my part.[/quote]

Re: Voice synthesis, part 1

Posted: Tue Mar 29, 2011 6:51 pm
by wgarvin
I seem to recall that early SoundBlaster sound cards for PCs had a hardware chip on them for voice synthesis. It came with this sample app called Dr. Sbaitso.

Nowadays, all versions of windows have a speech synthesis API that programs can use. I've never tried it though, but it doesn't look too hard to use.