/*
 * $Id: main.c,v 1.13 2007/08/02 01:25:46 bsd Exp $
 *
 */

/*
 * Author : Brian Dean, bsd@bdmicro.com
 * Date   : 2 March, 2004
 */

/*
 * Magnevation SpeakJet Speech and Sound Sythesizer Module
 * 
 * This directory contains a complete test program for demonstrating the
 * use of the Magnevation SpeakJet speach chip with the BDMICRO MAVRIC
 * and MAVRIC-II microcontroller boards.  Feel free to copy the relevent
 * function definitions into your own applications and to customize them
 * for your needs.
 * 
 * This program is interactive and can be used with a terminal emulator
 * to issue control commands and play sequences of phonemes via a
 * command line interface.  Thanks to the large FLASH memory of the
 * ATmega128 on the MAVRIC / MAVRIC-II boards, the SpeakJet's full
 * phoneme table with codes, mnuemonics, and even text descriptions for
 * each of them is stored internally and can be displayed for easy
 * reference using the 'list' and 'listv' commands.
 * 
 * The code is written in C using the GNU GCC C Compiler.  A Makefile is
 * provided which is known to work correctly on the FreeBSD Unix
 * operating system.
 * 
 * Configuration:
 *  
 *   Hook up the SpeakJet according to its data sheet.  Attach the
 *   SpeakJet output to an amplifier.  Attach your MAVRIC or MAVRIC-II
 *   PORTD3 to SpeakJet pin 11 (Serial TTL Data).  It is important to use
 *   PORTD3 and not the MAVRIC/MAVRIC-II UART1 TX pin because the UART1
 *   TX pin is a level shifted RS232 voltage, while the SpeakJet utilizes
 *   just the TTL level.  PORTD3 on the MAVRIC/MAVRIC-II is the TTL level
 *   RS232 output pin which is the one to use with the SpeakJet.
 * 
 *   Next, hook up a serial terminal program to the UART0 of your MAVRIC
 *   / MAVRIC-II board.  A simple command line program is running on the
 *   MAVRIC which allows you to issue sequences of phoneme codes to the
 *   SpeakJet in order to experiment with putting together words,
 *   phrases, and sound effects.
 * 
 *   When the MAVRIC/MAVRIC-II comes out of reset, it will speak the
 *   phrase: "Welcome to the BDMICRO MAVRIC SpeakJet Test Program".  It
 *   then displays a brief help message and finally issues a '>>>' prompt
 *   on UART0 which indicates that is is ready to accept input for
 *   interacting with the SpeakJet.
 * 
 *   At the command prompt, you can enter phonemes and phoneme-codes
 *   which are then handed off to the SpeakJet to play.  For example, to
 *   say "ready", try the following:
 * 
 *     >>> rr eh de iy
 * 
 *   Use the 'list' command to list out all phoneme codes.  Use the
 *   'listv' command to get a verbose listing which includes not only the
 *   phonemes and codes, but also a brief description of each.
 * 
 *   Next, try a few sound effects using the raw phoneme codes:
 * 
 *     >>> b4
 * 
 *   String multiple sound effects and phonemes together like this:
 * 
 *     >>> b4 b7 b7 b7 b8 b8
 * 
 *   If you prefer to use the raw codes, simply list the numeric
 *   equivalents of the phoneme mnuemonics:
 * 
 *     >>> 224 227 227 227 228 228
 * 
 *   Phoneme mneumonics and codes should be seperated by a space or a
 *   comma.  A leading backslash is also acceptable, and they are not
 *   case sensitive.
 * 
 *   Use the '?' command to print a brief help message.  The startup
 *   announcement message can be played by using the 'announce' command.
 * 
 * For more information about the MAVRIC and MAVRIC-II microcontroller
 * boards, see:
 * 
 *   http://www.bdmicro.com/
 * 
 * For more information about the Magnevation SpeakJet Speech chip, see:
 * 
 *   http://www.speakjet.com/
 * 
 *
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#include "ringbuf.h"


#define BAUD     9600L
#define DEF_UART 0
#define AUX_UART 1


/*
 * UART data buffer
 */
#define UART_BUF_LEN 32
static uint8_t uart_buffer[UART_BUF_LEN];
RINGBUF8 uartbuf;

FILE * aux_uart;


const char s_annc[]    PROGMEM = "\n\n\nWELCOME TO THE BDMICRO MAVRIC "
                                 "SPEAKJET TEST PROGRAM\n";
const char s_prompt[]  PROGMEM = ">>> ";
const char s_invalid[] PROGMEM = "INVALID\n";
const char s_bksp[]    PROGMEM = "\b \b";

const char s_help[]    PROGMEM =
"\n"
"Commands:\n"
"\n"
"  ?                  - this help message\n"
"  announce           - play announce message\n"
"  list               - list phoneme mneumonics and codes\n"
"  listv              - list phoneme mneumonics, codes and descriptions\n"
"                         (verbose listing)\n"
"  scp <ascii-text>   - issue SCP control messages to the SpeakJet\n"
"  wid <screen-width> - set the screen width; used by the 'list' and 'listv'\n"
"                          commands to determine how much information can\n"
"                          fit on one line.\n"
"  <phoneme|code> ... - play the specified sequence\n"
"\n";

#define WP 5
uint8_t s_announce[] PROGMEM = {
/* welcome  */ 145, 131, 145, 4, 195, 136, 140, WP,
/* to       */ 192, 162, WP,
/* the      */ 169, 134, WP,
/* bdmicro  */ 170, 128, 0, 174, 128, 0, 140, 155, 194, 148, 137, WP,
/* mavric   */ 140, 154, 166, 148, 129, 194, WP,
/* speakjet */ 187, 198, 128, 194, 4, 165, 131, 191, WP,
/* test     */ 191, 131, 187, 191, WP,
/* program  */ 199, 148, 137, 179, 148, 132, 140, 
/* the end  */ 0
};
#define N_ANNC sizeof(s_announce)

/*
 * simple data structure for representing phoneme information
 */
typedef struct {
  const uint8_t code;
  const char * phoneme;
  const char * desc;
} PHONEME;


/*
 * List of phoneme mnuemonics.  These need to be seperate from the
 * phoneme initializers below to ensure that their data is placed into
 * FLASH memory and not copied over the SRAM.  
 */
const char s_p0[] PROGMEM = "p0";
const char s_p1[] PROGMEM = "p1";
const char s_p2[] PROGMEM = "p2";
const char s_p3[] PROGMEM = "p3";
const char s_p4[] PROGMEM = "p4";
const char s_p5[] PROGMEM = "p5";
const char s_p6[] PROGMEM = "p6";
const char s_iy[] PROGMEM = "iy";
const char s_ih[] PROGMEM = "ih";
const char s_ey[] PROGMEM = "ey";
const char s_eh[] PROGMEM = "eh";
const char s_ay[] PROGMEM = "ay";
const char s_ax[] PROGMEM = "ax";
const char s_ux[] PROGMEM = "ux";
const char s_oh[] PROGMEM = "oh";
const char s_aw[] PROGMEM = "aw";
const char s_ow[] PROGMEM = "ow";
const char s_uh[] PROGMEM = "uh";
const char s_uw[] PROGMEM = "uw";
const char s_mm[] PROGMEM = "mm";
const char s_ne[] PROGMEM = "ne";
const char s_no[] PROGMEM = "no";
const char s_nge[] PROGMEM = "nge";
const char s_ngo[] PROGMEM = "ngo";
const char s_le[] PROGMEM = "le";
const char s_lo[] PROGMEM = "lo";
const char s_ww[] PROGMEM = "ww";
const char s_rr[] PROGMEM = "rr";
const char s_iyrr[] PROGMEM = "iyrr";
const char s_eyrr[] PROGMEM = "eyrr";
const char s_axrr[] PROGMEM = "axrr";
const char s_awrr[] PROGMEM = "awrr";
const char s_owrr[] PROGMEM = "owrr";
const char s_eyiy[] PROGMEM = "eyiy";
const char s_ohiy[] PROGMEM = "ohiy";
const char s_owiy[] PROGMEM = "owiy";
const char s_ohih[] PROGMEM = "ohih";
const char s_iyeh[] PROGMEM = "iyeh";
const char s_ehll[] PROGMEM = "ehll";
const char s_iyuw[] PROGMEM = "iyuw";
const char s_axuw[] PROGMEM = "axuw";
const char s_ihww[] PROGMEM = "ihww";
const char s_ayww[] PROGMEM = "ayww";
const char s_owww[] PROGMEM = "owww";
const char s_jh[] PROGMEM = "jh";
const char s_vv[] PROGMEM = "vv";
const char s_zz[] PROGMEM = "zz";
const char s_zh[] PROGMEM = "zh";
const char s_dh[] PROGMEM = "dh";
const char s_be[] PROGMEM = "be";
const char s_bo[] PROGMEM = "bo";
const char s_eb[] PROGMEM = "eb";
const char s_ob[] PROGMEM = "ob";
const char s_de[] PROGMEM = "de";
const char s_do[] PROGMEM = "do";
const char s_ed[] PROGMEM = "ed";
const char s_od[] PROGMEM = "od";
const char s_ge[] PROGMEM = "ge";
const char s_go[] PROGMEM = "go";
const char s_eg[] PROGMEM = "eg";
const char s_og[] PROGMEM = "og";
const char s_ch[] PROGMEM = "ch";
const char s_he[] PROGMEM = "he";
const char s_ho[] PROGMEM = "ho";
const char s_wh[] PROGMEM = "wh";
const char s_ff[] PROGMEM = "ff";
const char s_se[] PROGMEM = "se";
const char s_so[] PROGMEM = "so";
const char s_sh[] PROGMEM = "sh";
const char s_th[] PROGMEM = "th";
const char s_tt[] PROGMEM = "tt";
const char s_tu[] PROGMEM = "tu";
const char s_ts[] PROGMEM = "ts";
const char s_ke[] PROGMEM = "ke";
const char s_ko[] PROGMEM = "ko";
const char s_ek[] PROGMEM = "ek";
const char s_ok[] PROGMEM = "ok";
const char s_pe[] PROGMEM = "pe";
const char s_po[] PROGMEM = "po";
const char s_r0[] PROGMEM = "r0";
const char s_r1[] PROGMEM = "r1";
const char s_r2[] PROGMEM = "r2";
const char s_r3[] PROGMEM = "r3";
const char s_r4[] PROGMEM = "r4";
const char s_r5[] PROGMEM = "r5";
const char s_r6[] PROGMEM = "r6";
const char s_r7[] PROGMEM = "r7";
const char s_r8[] PROGMEM = "r8";
const char s_r9[] PROGMEM = "r9";
const char s_a0[] PROGMEM = "a0";
const char s_a1[] PROGMEM = "a1";
const char s_a2[] PROGMEM = "a2";
const char s_a3[] PROGMEM = "a3";
const char s_a4[] PROGMEM = "a4";
const char s_a5[] PROGMEM = "a5";
const char s_a6[] PROGMEM = "a6";
const char s_a7[] PROGMEM = "a7";
const char s_a8[] PROGMEM = "a8";
const char s_a9[] PROGMEM = "a9";
const char s_b0[] PROGMEM = "b0";
const char s_b1[] PROGMEM = "b1";
const char s_b2[] PROGMEM = "b2";
const char s_b3[] PROGMEM = "b3";
const char s_b4[] PROGMEM = "b4";
const char s_b5[] PROGMEM = "b5";
const char s_b6[] PROGMEM = "b6";
const char s_b7[] PROGMEM = "b7";
const char s_b8[] PROGMEM = "b8";
const char s_b9[] PROGMEM = "b9";
const char s_c0[] PROGMEM = "c0";
const char s_c1[] PROGMEM = "c1";
const char s_c2[] PROGMEM = "c2";
const char s_c3[] PROGMEM = "c3";
const char s_c4[] PROGMEM = "c4";
const char s_c5[] PROGMEM = "c5";
const char s_c6[] PROGMEM = "c6";
const char s_c7[] PROGMEM = "c7";
const char s_c8[] PROGMEM = "c8";
const char s_c9[] PROGMEM = "c9";
const char s_d0[] PROGMEM = "d0";
const char s_d1[] PROGMEM = "d1";
const char s_d2[] PROGMEM = "d2";
const char s_d3[] PROGMEM = "d3";
const char s_d4[] PROGMEM = "d4";
const char s_d5[] PROGMEM = "d5";
const char s_d6[] PROGMEM = "d6";
const char s_d7[] PROGMEM = "d7";
const char s_d8[] PROGMEM = "d8";
const char s_d9[] PROGMEM = "d9";
const char s_d10[] PROGMEM = "d10";
const char s_d11[] PROGMEM = "d11";
const char s_m0[] PROGMEM = "m0";
const char s_m1[] PROGMEM = "m1";
const char s_m2[] PROGMEM = "m2";
const char s_ns[] PROGMEM = "ns";

const char s_fast[]   PROGMEM = "fast";
const char s_slow[]   PROGMEM = "slow";
const char s_stress[] PROGMEM = "stress";
const char s_relax[]  PROGMEM = "relax";
const char s_wait[]   PROGMEM = "wait";
const char s_soft[]   PROGMEM = "soft";
const char s_volume[] PROGMEM = "volume";
const char s_speed[]  PROGMEM = "speed";
const char s_pitch[]  PROGMEM = "pitch";
const char s_bend[]   PROGMEM = "bend";
const char s_repeat[] PROGMEM = "repeat";
const char s_delay[]  PROGMEM = "delay";
const char s_reset[]  PROGMEM = "reset";

/*
 * descriptions
 */
const char s_p0_d[]   PROGMEM = "0ms silence";
const char s_p1_d[]   PROGMEM = "100ms silence";
const char s_p2_d[]   PROGMEM = "200ms silence";
const char s_p3_d[]   PROGMEM = "700ms silence";
const char s_p4_d[]   PROGMEM = "10ms silence /w trans";
const char s_p5_d[]   PROGMEM = "30ms silence /w trans";
const char s_p6_d[]   PROGMEM = "60ms silence /w trans";
const char s_iy_d[]   PROGMEM = "see, even, feed";
const char s_ih_d[]   PROGMEM = "sit, fix, pin";
const char s_ey_d[]   PROGMEM = "hair, gate, beige";
const char s_eh_d[]   PROGMEM = "met, check, red";
const char s_ay_d[]   PROGMEM = "hat, fast, fan";
const char s_ax_d[]   PROGMEM = "cotten";
const char s_ux_d[]   PROGMEM = "luck, up, uncle";
const char s_oh_d[]   PROGMEM = "hot, clock, fox";
const char s_aw_d[]   PROGMEM = "father, fall";
const char s_ow_d[]   PROGMEM = "comb, over, hold";
const char s_uh_d[]   PROGMEM = "book, could, should";
const char s_uw_d[]   PROGMEM = "food, june";
const char s_mm_d[]   PROGMEM = "milk, famous";
const char s_ne_d[]   PROGMEM = "nip, danger, thin";
const char s_no_d[]   PROGMEM = "no, snow, on";
const char s_nge_d[]  PROGMEM = "think, ping";
const char s_ngo_d[]  PROGMEM = "hung, song";
const char s_le_d[]   PROGMEM = "lake, alarm, lapel";
const char s_lo_d[]   PROGMEM = "clock, plus, hello";
const char s_ww_d[]   PROGMEM = "wool, sweat";
const char s_rr_d[]   PROGMEM = "ray, brain, over";
const char s_iyrr_d[] PROGMEM = "clear, hear, year";
const char s_eyrr_d[] PROGMEM = "hair, stair, repair";
const char s_axrr_d[] PROGMEM = "fir, bird, burn";
const char s_awrr_d[] PROGMEM = "part, farm, yarn";
const char s_owrr_d[] PROGMEM = "corn, four, your";
const char s_eyiy_d[] PROGMEM = "gate, ate, ray";
const char s_ohiy_d[] PROGMEM = "mice, fight, white";
const char s_owiy_d[] PROGMEM = "boy, toy, voice";
const char s_ohih_d[] PROGMEM = "ski, five, I";
const char s_iyeh_d[] PROGMEM = "yes, yarn, million";
const char s_ehll_d[] PROGMEM = "saddle, angle, spell";
const char s_iyuw_d[] PROGMEM = "cute, few";
const char s_axuw_d[] PROGMEM = "brown, clown, thousand";
const char s_ihww_d[] PROGMEM = "two, new, zoo";
const char s_ayww_d[] PROGMEM = "our, ouch, owl";
const char s_owww_d[] PROGMEM = "go, hello, snow";
const char s_jh_d[]   PROGMEM = "dodge, jet, savage";
const char s_vv_d[]   PROGMEM = "vest, even";
const char s_zz_d[]   PROGMEM = "zoo, zap";
const char s_zh_d[]   PROGMEM = "azure, treasure";
const char s_dh_d[]   PROGMEM = "there, that, this";
const char s_be_d[]   PROGMEM = "bear, bird, beed";
const char s_bo_d[]   PROGMEM = "bone, book, brown";
const char s_eb_d[]   PROGMEM = "cab, crib, web";
const char s_ob_d[]   PROGMEM = "bob, sub, tub";
const char s_de_d[]   PROGMEM = "deep, date, divide";
const char s_do_d[]   PROGMEM = "do, dust, dog";
const char s_ed_d[]   PROGMEM = "could, bird";
const char s_od_d[]   PROGMEM = "bud, food";
const char s_ge_d[]   PROGMEM = "get, gate, guest";
const char s_go_d[]   PROGMEM = "got, glue, goo";
const char s_eg_d[]   PROGMEM = "peg, wig";
const char s_og_d[]   PROGMEM = "dog, peg";
const char s_ch_d[]   PROGMEM = "church, feature, march";
const char s_he_d[]   PROGMEM = "help, hand, hair";
const char s_ho_d[]   PROGMEM = "hoe, hot, hug";
const char s_wh_d[]   PROGMEM = "who, whale, white";
const char s_ff_d[]   PROGMEM = "food, effort, off";
const char s_se_d[]   PROGMEM = "see, vest, plus";
const char s_so_d[]   PROGMEM = "so, sweat";
const char s_sh_d[]   PROGMEM = "ship, fiction, leash";
const char s_th_d[]   PROGMEM = "thin, month";
const char s_tt_d[]   PROGMEM = "part, little, sit";
const char s_tu_d[]   PROGMEM = "to, talk, ten";
const char s_ts_d[]   PROGMEM = "parts, costs, robots";
const char s_ke_d[]   PROGMEM = "can't, clown, key";
const char s_ko_d[]   PROGMEM = "comb, quick, fox";
const char s_ek_d[]   PROGMEM = "speak, task";
const char s_ok_d[]   PROGMEM = "book, took, october";
const char s_pe_d[]   PROGMEM = "people, computer";
const char s_po_d[]   PROGMEM = "pow, copy";
const char s_r0_d[]   PROGMEM = "robot";
const char s_r1_d[]   PROGMEM = "robot";
const char s_r2_d[]   PROGMEM = "robot";
const char s_r3_d[]   PROGMEM = "robot";
const char s_r4_d[]   PROGMEM = "robot";
const char s_r5_d[]   PROGMEM = "robot";
const char s_r6_d[]   PROGMEM = "robot";
const char s_r7_d[]   PROGMEM = "robot";
const char s_r8_d[]   PROGMEM = "robot";
const char s_r9_d[]   PROGMEM = "robot";
const char s_a0_d[]   PROGMEM = "alarm";
const char s_a1_d[]   PROGMEM = "alarm";
const char s_a2_d[]   PROGMEM = "alarm";
const char s_a3_d[]   PROGMEM = "alarm";
const char s_a4_d[]   PROGMEM = "alarm";
const char s_a5_d[]   PROGMEM = "alarm";
const char s_a6_d[]   PROGMEM = "alarm";
const char s_a7_d[]   PROGMEM = "alarm";
const char s_a8_d[]   PROGMEM = "alarm";
const char s_a9_d[]   PROGMEM = "alarm";
const char s_b0_d[]   PROGMEM = "beeps";
const char s_b1_d[]   PROGMEM = "beeps";
const char s_b2_d[]   PROGMEM = "beeps";
const char s_b3_d[]   PROGMEM = "beeps";
const char s_b4_d[]   PROGMEM = "beeps";
const char s_b5_d[]   PROGMEM = "beeps";
const char s_b6_d[]   PROGMEM = "beeps";
const char s_b7_d[]   PROGMEM = "beeps";
const char s_b8_d[]   PROGMEM = "beeps";
const char s_b9_d[]   PROGMEM = "beeps";
const char s_c0_d[]   PROGMEM = "biological";
const char s_c1_d[]   PROGMEM = "biological";
const char s_c2_d[]   PROGMEM = "biological";
const char s_c3_d[]   PROGMEM = "biological";
const char s_c4_d[]   PROGMEM = "biological";
const char s_c5_d[]   PROGMEM = "biological";
const char s_c6_d[]   PROGMEM = "biological";
const char s_c7_d[]   PROGMEM = "biological";
const char s_c8_d[]   PROGMEM = "biological";
const char s_c9_d[]   PROGMEM = "biological";
const char s_d0_d[]   PROGMEM = "dtmf";
const char s_d1_d[]   PROGMEM = "dtmf";
const char s_d2_d[]   PROGMEM = "dtmf";
const char s_d3_d[]   PROGMEM = "dtmf";
const char s_d4_d[]   PROGMEM = "dtmf";
const char s_d5_d[]   PROGMEM = "dtmf";
const char s_d6_d[]   PROGMEM = "dtmf";
const char s_d7_d[]   PROGMEM = "dtmf";
const char s_d8_d[]   PROGMEM = "dtmf";
const char s_d9_d[]   PROGMEM = "dtmf";
const char s_d10_d[]  PROGMEM = "dtmf";
const char s_d11_d[]  PROGMEM = "dtmf";
const char s_m0_d[]   PROGMEM = "sonar ping";
const char s_m1_d[]   PROGMEM = "pistol shot";
const char s_m2_d[]   PROGMEM = "wow";
const char s_ns_d[]   PROGMEM = "no sound";

const char s_fast_d[]   PROGMEM = ".5 time";
const char s_slow_d[]   PROGMEM = "1.5 time";
const char s_stress_d[] PROGMEM = "stress voice";
const char s_relax_d[]  PROGMEM = "relax voice";
const char s_wait_d[]   PROGMEM = "stop";
const char s_soft_d[]   PROGMEM = "reduce volume 50 pct";
const char s_volume_d[] PROGMEM = "0-127/96";
const char s_speed_d[]  PROGMEM = "0-127/114";
const char s_pitch_d[]  PROGMEM = "0-255/88";
const char s_bend_d[]   PROGMEM = "0-15/5";
const char s_repeat_d[] PROGMEM = "0-255";
const char s_delay_d[]  PROGMEM = "X*10ms";
const char s_reset_d[]  PROGMEM = "reset parms";



/*
 * Phoneme data.
 */
PHONEME phoneme[] PROGMEM = {
  {   0, s_p0, s_p0_d },
  {   1, s_p1, s_p1_d },
  {   2, s_p2, s_p2_d },
  {   3, s_p3, s_p3_d },
  {   4, s_p4, s_p4_d },
  {   5, s_p5, s_p5_d },
  {   6, s_p6, s_p6_d },
  {   7, s_fast, s_fast_d },
  {   8, s_slow, s_slow_d },
  {  14, s_stress, s_stress_d },
  {  15, s_relax, s_relax_d },
  {  16, s_wait, s_wait_d },
  {  18, s_soft, s_soft_d },
  {  20, s_volume, s_volume_d },
  {  21, s_speed, s_speed_d },
  {  22, s_pitch, s_pitch_d },
  {  23, s_bend, s_bend_d },
  {  26, s_repeat, s_repeat_d },
  {  30, s_delay, s_delay_d },
  {  31, s_reset, s_reset_d },
  { 128, s_iy, s_iy_d },
  { 129, s_ih, s_ih_d },
  { 130, s_ey, s_ey_d },
  { 131, s_eh, s_eh_d },
  { 132, s_ay, s_ay_d },
  { 133, s_ax, s_ax_d },
  { 134, s_ux, s_ux_d },
  { 135, s_oh, s_oh_d },
  { 136, s_aw, s_aw_d },
  { 137, s_ow, s_ow_d },
  { 138, s_uh, s_uh_d },
  { 139, s_uw, s_uw_d },
  { 140, s_mm, s_mm_d },
  { 141, s_ne, s_ne_d },
  { 142, s_no, s_no_d },
  { 143, s_nge, s_nge_d },
  { 144, s_ngo, s_ngo_d },
  { 145, s_le, s_le_d },
  { 146, s_lo, s_lo_d },
  { 147, s_ww, s_ww_d },
  { 148, s_rr, s_rr_d },
  { 149, s_iyrr, s_iyrr_d },
  { 150, s_eyrr, s_eyrr_d },
  { 151, s_axrr, s_axrr_d },
  { 152, s_awrr, s_awrr_d },
  { 153, s_owrr, s_owrr_d },
  { 154, s_eyiy, s_eyiy_d },
  { 155, s_ohiy, s_ohiy_d },
  { 156, s_owiy, s_owiy_d },
  { 157, s_ohih, s_ohih_d },
  { 158, s_iyeh, s_iyeh_d },
  { 159, s_ehll, s_ehll_d },
  { 160, s_iyuw, s_iyuw_d },
  { 161, s_axuw, s_axuw_d },
  { 162, s_ihww, s_ihww_d },
  { 163, s_ayww, s_ayww_d },
  { 164, s_owww, s_owww_d },
  { 165, s_jh, s_jh_d },
  { 166, s_vv, s_vv_d },
  { 167, s_zz, s_zz_d },
  { 168, s_zh, s_zh_d },
  { 169, s_dh, s_dh_d },
  { 170, s_be, s_be_d },
  { 171, s_bo, s_bo_d },
  { 172, s_eb, s_eb_d },
  { 173, s_ob, s_ob_d },
  { 174, s_de, s_de_d },
  { 175, s_do, s_do_d },
  { 176, s_ed, s_ed_d },
  { 177, s_od, s_od_d },
  { 178, s_ge, s_ge_d },
  { 179, s_go, s_go_d },
  { 180, s_eg, s_eg_d },
  { 181, s_og, s_og_d },
  { 182, s_ch, s_ch_d },
  { 183, s_he, s_he_d },
  { 184, s_ho, s_ho_d },
  { 185, s_wh, s_wh_d },
  { 186, s_ff, s_ff_d },
  { 187, s_se, s_se_d },
  { 188, s_so, s_so_d },
  { 189, s_sh, s_sh_d },
  { 190, s_th, s_th_d },
  { 191, s_tt, s_tt_d },
  { 192, s_tu, s_tu_d },
  { 193, s_ts, s_ts_d },
  { 194, s_ke, s_ke_d },
  { 195, s_ko, s_ko_d },
  { 196, s_ek, s_ek_d },
  { 197, s_ok, s_ok_d },
  { 198, s_pe, s_pe_d },
  { 199, s_po, s_po_d },
  { 200, s_r0, s_r0_d },
  { 201, s_r1, s_r1_d },
  { 202, s_r2, s_r2_d },
  { 203, s_r3, s_r3_d },
  { 204, s_r4, s_r4_d },
  { 205, s_r5, s_r5_d },
  { 206, s_r6, s_r6_d },
  { 207, s_r7, s_r7_d },
  { 208, s_r8, s_r8_d },
  { 209, s_r9, s_r9_d },
  { 210, s_a0, s_a0_d },
  { 211, s_a1, s_a1_d },
  { 212, s_a2, s_a2_d },
  { 213, s_a3, s_a3_d },
  { 214, s_a4, s_a4_d },
  { 215, s_a5, s_a5_d },
  { 216, s_a6, s_a6_d },
  { 217, s_a7, s_a7_d },
  { 218, s_a8, s_a8_d },
  { 219, s_a9, s_a9_d },
  { 220, s_b0, s_b0_d },
  { 221, s_b1, s_b1_d },
  { 222, s_b2, s_b2_d },
  { 223, s_b3, s_b3_d },
  { 224, s_b4, s_b4_d },
  { 225, s_b5, s_b5_d },
  { 226, s_b6, s_b6_d },
  { 227, s_b7, s_b7_d },
  { 228, s_b8, s_b8_d },
  { 229, s_b9, s_b9_d },
  { 230, s_c0, s_c0_d },
  { 231, s_c1, s_c1_d },
  { 232, s_c2, s_c2_d },
  { 233, s_c3, s_c3_d },
  { 234, s_c4, s_c4_d },
  { 235, s_c5, s_c5_d },
  { 236, s_c6, s_c6_d },
  { 237, s_c7, s_c7_d },
  { 238, s_c8, s_c8_d },
  { 239, s_c9, s_c9_d },
  { 240, s_d0, s_d0_d },
  { 241, s_d1, s_d1_d },
  { 242, s_d2, s_d2_d },
  { 243, s_d3, s_d3_d },
  { 244, s_d4, s_d4_d },
  { 245, s_d5, s_d5_d },
  { 246, s_d6, s_d6_d },
  { 247, s_d7, s_d7_d },
  { 248, s_d8, s_d8_d },
  { 249, s_d9, s_d9_d },
  { 250, s_d10, s_d10_d },
  { 251, s_d11, s_d11_d },
  { 252, s_m0, s_m0_d },
  { 253, s_m1, s_m1_d },
  { 254, s_m2, s_m2_d },
  { 255, s_ns, s_ns_d }
};
#define N_PHONEMES (sizeof(phoneme)/sizeof(PHONEME))



/*
 * Bits that are set inside interrupt routines, and watched outside in
 * the program's main loop.
 */
volatile struct {
  uint8_t rx_int            : 1;
} event;




//#define CPU_FREQ 16000000L  /* set to clock frequency in Hz */
#define CPU_FREQ 14745600L  /* set to clock frequency in Hz */

#if CPU_FREQ == 16000000L
#define OCR_1MS 125
#elif CPU_FREQ == 14745600L
#define OCR_1MS 115
#endif


volatile uint16_t ms_count;

/*
 * ms_sleep() - delay for specified number of milliseconds
 */
void ms_sleep(uint16_t ms)
{
  TCNT0  = 0;
  ms_count = 0;
  while (ms_count != ms)
    ;
}


/* 
 * millisecond counter interrupt vector 
 */
SIGNAL(SIG_OUTPUT_COMPARE0)
{
  ms_count++;
}


/*
 * Initialize timer0 to use the main crystal clock and the output
 * compare interrupt feature to generate an interrupt approximately
 * once per millisecond to use as a general purpose time base.
 */
void init_timer0(void)
{
  TCCR0 = 0;
  TIFR  |= _BV(OCIE0)|_BV(TOIE0);
  TIMSK |= _BV(TOIE0)|_BV(OCIE0);         /* enable output compare interrupt */
  TCCR0  = _BV(WGM01)|_BV(CS02)|_BV(CS00); /* CTC, prescale = 128 */
  TCNT0  = 0;
  OCR0   = OCR_1MS;                     /* match in aprox 1 ms */
}



/*
 * UART RX interrupt vector
 */
SIGNAL(SIG_UART0_RECV)
{
  uint8_t c;

  c = UDR0;
  if (bit_is_clear(UCSR0A, FE)) {
    ringbuf8_put(&uartbuf, c);
    event.rx_int = 1;
  }
}


void uart0_init(uint16_t baud)
{
  uint16_t brr;

  brr = (long)(CPU_FREQ / ((long)(16L*(long)baud)) - 1);

  UBRR0H = brr >> 8;;
  UBRR0L = brr & 0x00ff;

  UCSR0B = _BV(RXEN)|_BV(TXEN)|_BV(RXCIE);
}


void uart1_init(uint16_t baud)
{
  uint16_t brr;

  brr = CPU_FREQ / (16L*(long)baud) - 1;

  UBRR1H = brr >> 8;;
  UBRR1L = brr & 0x00ff;
  UCSR1B = _BV(TXEN);
}


void uart_putc(uint8_t uart, char c)
{
  if (uart == 0) {
    while ((UCSR0A & _BV(UDRE)) == 0)
      ;
    UDR0 = c;
  }
  else if (uart == 1) {
    while ((UCSR1A & _BV(UDRE)) == 0)
      ;
    UDR1 = c;
  }
}


int def_putc(char ch)
{
  uart_putc(DEF_UART, ch);
  return 0;
}


int uputc(char ch, FILE * f)
{
  return def_putc(ch);
}


int aux_putc(char ch)
{
  uart_putc(AUX_UART, ch);
  return 0;
}


int auxputc(char ch, FILE * f)
{
  return aux_putc(ch);
}





/*
 * Return the 8-bit phoneme code that matches the given string.  If
 * not found, return 0xff.  
 */
uint8_t get_phoneme(char * s)
{
  uint8_t i;
  uint16_t ph;

  for (i=0; i<N_PHONEMES; i++) {
    ph   = pgm_read_word(&phoneme[i].phoneme);
    if (strcasecmp_P(s, (const char *)ph) == 0)
      return pgm_read_byte(&phoneme[i].code);
  }

  return 255;
}


/*
 * List out all the phoneme data.  List in columns, as many as will
 * fit, ascending down, then starting again with the next column.
 */
#define MAX_PH_LEN (7)  /* max phoneme length (including ending space) */
#define MAX_DE_LEN (23) /* max desc length (including ending space) */
char buf[60];
char tbuf1[16];
char tbuf2[30];
uint8_t max_wid=80; /* width of the screen for display */
void list_phonemes(uint8_t verbose)
{
  int l, i, j;
  int col_wid, n_cols, sep, n_lines;
  uint16_t ph, de;
  uint8_t code;

  if (verbose) {
    col_wid = 3 + 1 + MAX_PH_LEN + 2 + MAX_DE_LEN;
  }
  else {
    col_wid = 3 + 1 + MAX_PH_LEN;
  }

  n_cols  = max_wid / col_wid;
  n_lines = N_PHONEMES / n_cols;
  if (N_PHONEMES % n_cols)
    n_lines++;
  sep = n_lines;

  //printf("N_PHONEMES=%d, col_wid=%d, n_cols=%d, n_lines=%d, sep=%d\n\n",
  //        N_PHONEMES, col_wid, n_cols, n_lines, sep);

  for (l=0; l<n_lines; l++) {
    for (j=0; j<n_cols; j++) {
      i = l + j*sep;
      if (i<N_PHONEMES) {
        code = pgm_read_byte(&phoneme[i].code);
        ph   = pgm_read_word(&phoneme[i].phoneme);
        de   = pgm_read_word(&phoneme[i].desc);

        sprintf_P(tbuf1, (const char *)ph);
        sprintf_P(tbuf2, (const char *)de);
        
        if (verbose) {
          sprintf(buf, "%3u:%-6s : %-22s", code, tbuf1, tbuf2);
        }
        else {
          sprintf(buf, "%3u:%-6s", code, tbuf1);
        }
        
        printf(buf);
        printf(" ");
      }
    }
    printf("\n");
  }
}




/* 
 * define a few helper macros for parsing command line information
 */
#define over(x) while (*x && !((*x == ' ')||(*x == '\\')||(*x == ','))) x++
#define skip(x) while (*x && ((*x == ' ')||(*x == '\\')||(*x == ','))) x++


/*
 * decode and execute simple commands
 */
static char do_cmdbuf[20];
static __inline__ void do_cmd(char * s)
{
  static char * cmd = do_cmdbuf;
  char        * args, * p, * e, * q;
  uint8_t       i, code, save;

  if (*s == 0)
    return;

  p = s;
  skip(p);
  q = p;
  over(q);
  if (q == p)
    return;
  save = *q;
  *q = 0;

  strcpy(cmd, p);
  *q = save;

  args = q;
  skip(args);
  if (*args == 0)
    args = NULL;

  if (strcmp(cmd, "?")==0) {
    printf_P(s_help);
  }
  else if (strcmp(cmd, "announce")==0) {
    printf("speaking ");
    for (i=0; i<N_ANNC; i++) {
      printf("%u ", pgm_read_byte(&s_announce[i]));
      uart_putc(1, pgm_read_byte(&s_announce[i]));
    }
    printf("\n");
  }
  else if (strcmp(cmd, "list")==0) {
    printf("\n");
    list_phonemes(0);
    printf("\n");
  }
  else if (strcmp(cmd, "listv")==0) {
    printf("\n");
    list_phonemes(1);
    printf("\n");
  }
  else if (strcmp(cmd, "scp")==0) {
    p = s;
    skip(p);
    while (p && *p != 0) {
      uart_putc(1, *p);
      p++;
    }
  }
  else if (strcmp(cmd, "wid")==0) {
    if (args) {
      p = args;
      skip(p);
      code = strtol(p, &e, 0);
      if (e && e == p) {
        printf("can't read width '%s'\n", p);
        return;
      }
      max_wid = code;
    }
    else {
      printf("wid = %u\n", max_wid);
    }      
  }
  else {
    printf("speaking ");
    p = s;
    skip(p);
    while (p && *p != 0) {
      q = p;
      over(q);
      save = *q;
      *q = 0;
      printf("%s ", p);
      if (isdigit(*p)) {
        code = strtol(p, &e, 0);
        if (e && e == p) {
          code = 255;
        }
      }
      else {
        code = get_phoneme(p);
      }
      printf("%u ", code);
      if (code != 255)
        uart_putc(1, code);
      *q = save;
      p = q;
      skip(p);
    }
    printf("\n");
  }
}



/*
 * accept characters and build a command line, when return is pressed,
 * pass the command to 'do_cmd()' 
 */
#define CMD_BUF_LEN 256
static char recv_input_cmdbuf[CMD_BUF_LEN];
void recv_input(uint8_t ch)
{
  static int idx=0;

  if ((ch == '\r')||(ch == '\n')) {
    def_putc('\n');
    recv_input_cmdbuf[idx] = 0;
    do_cmd(recv_input_cmdbuf);
    printf_P(s_prompt);
    idx = 0;
  }
  else if (ch == '\b') {
    if (idx) {
      printf_P(s_bksp);
      idx--;
      recv_input_cmdbuf[idx] = 0;
    }
  }
  else {
    def_putc(ch);
    recv_input_cmdbuf[idx++] = ch;
    if (idx == CMD_BUF_LEN) {
      idx = 0;
      recv_input_cmdbuf[idx] = 0;
      printf_P(s_invalid);
      printf_P(s_prompt);
    }
  }
}



int main(void)
{
  uint8_t ch, i;

  init_timer0();

  ringbuf8_init(&uartbuf, uart_buffer, sizeof(uart_buffer));

  uart0_init(BAUD);
  uart1_init(BAUD);

  /* enable interrupts */
  sei();

  fdevopen(uputc, NULL);

  aux_uart = fdevopen(auxputc, NULL);

  ms_sleep(250);

  printf_P(s_annc);

  /* speak the announcement message */
  for (i=0; i<N_ANNC; i++)
    uart_putc(1, pgm_read_byte(&s_announce[i]));

  printf_P(s_help);
  printf_P(s_prompt);

  /*
   * event loop - process command input from UART0
   */
  while (1) {
    if (event.rx_int) {
      cli();
      event.rx_int = 0;
      sei();
      while (uartbuf.bufcnt != 0) {
        ch = ringbuf8_get(&uartbuf);
        recv_input(ch);
      }
    }
  }
}

