/ TalkieTrellis / TalkieTrellis.ino
TalkieTrellis.ino
  1  // SPDX-FileCopyrightText: 2019 Phillip Burgess for Adafruit Industries
  2  //
  3  // SPDX-License-Identifier: MIT
  4  
  5  // Speak & Spell sound board for the Adafruit NeoTrellis M4.
  6  // Requires the following libraries, install with Arduino Library Manager:
  7  //  - Adafruit_NeoTrellisM4
  8  //  - Adafruit_Keypad
  9  //  - Adafruit_NeoPixel
 10  // Also requires the Adafruit fork of the Talkie library, install manually:
 11  //  - https://github.com/adafruit/Talkie
 12  //
 13  // Connect headphones or speaker to the Trellis audio output.
 14  // Tap buttons to hear letters, numbers, words and phrases. Hold one of the
 15  // bottom row buttons to enable one of eight sets of 31 sounds (plus the 24
 16  // sounds when no bottom-row button is held).
 17  // What you're hearing are not simply WAV files. Instead, the Talkie library
 18  // emulates the TMS5100 speech chip used in the original Speak & Spell.
 19  // File words.h contains a selection of speech data exactly as it was stored
 20  // in the original ROM in a highly-compressed lossy format called LPC-10.
 21  
 22  #include <Adafruit_NeoTrellisM4.h>
 23  #include <talkie.h>
 24  #include "words.h"
 25  
 26  Adafruit_NeoTrellisM4 trellis;
 27  Talkie                voice;
 28  
 29  // These tables assign sounds to each button. These are just pointers to
 30  // sound data in the file words.h, or NULL where no sound is assigned.
 31  // There are 9 goups of 32 buttons, the first is with none of the 8
 32  // bottom-row "shift" buttons held, the other 8 groups are for each of
 33  // the 8 shifted states. 
 34  const uint8_t *sound[9][32] = {
 35    { sp0 , sp1 , sp2 , sp3 , sp4 , sp5 , sp6 , sp7 ,
 36      sp8 , sp9 , spTEN, spABOVE, spACHIEVE, spAGAINST, spALMOST, spALREADY, 
 37      spANCIENT, spANOTHER, spANSWER, spANYTHING, spAPPROVE, spBEAUTY, spBELIEVE, spBOULDER,
 38      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, // "Shift" keys, leave this row NULL
 39    { spBROTHER, spBUILT, spBULLET, spBULLETIN, spBUREAU, spBUSHEL, spBUSINESS, spCARAVAN,
 40      spCARRY, spCHALK, spCHILD, spCIRCUIT, spCLEANSER, spCOLOR, spCOMFORT, spCOMING,
 41      spCONQUER, spCORRECT, spCOULDNT, spCOUNTRY, spCOUPLE, spCOURAGE, spCOUSIN, spDISCOVER,
 42      NULL, spDOES, spDOZEN, spDUNGEON, spEARLY, spEARNEST, spEARTH, spECHO },
 43    { spEGG, spENOUGH, spERROR, spEVERY, spEVERYONE, spEXTRA, spEYEBROW, spFEATHER,
 44      spFIELD, spFINGER, spFIRED, spFLOOD, spFLOOR, spFREIGHT, spFRONT, spGARAGE,
 45      spGLACIER, spGLOVE, spGREATER, spGUARD, spGUESS, spGUIDE, spHALF, spHASTE,
 46      spHEALTH, NULL, spHEALTHY, spHEAVY, spHEROES, spHONEY, spHONOR, spHOSTESS },
 47    { spHYGIENE, spIMPROVE, spINSTEAD, spIRON, spIS, spISLAND, spISLE, spJOURNEY,
 48      spKEY, spLANGUAGE, spLAUGH, spLAUGHTER, spLEARN, spLEATHER, spLEISURE, spLETTUCE,
 49      spLIBRARY, spLINGER, spLOSE, spMACHINE, spMEADOW, spMEANING, spMEASURE, spMECHANIC,
 50      spMILD, spMINUTE, NULL, spMIRROR, spMISTAKE, spMONEY, spMOSQUITO, spMOST },
 51    { spMOTHER, spMOVIE, spMUSTACHE, spNARROW, spNEIGHBOR, spNUISANCE, spOCEAN, spONCE,
 52      spONION, spOTHER, spOUTDOOR, spOVEN, spPERIOD, spPIANOS, spPIERCE, spPINT,
 53      spPLAGUE, spPLEASANT, spPLEASURE, spPLUNGER, spPLURAL, spPOSTAGE, spPOULTRY, spPRETTY,
 54      spPROMISE, spPULL, spPUSH, NULL, spQUESTION, spQUIET, spQUOTIENT, spRANGE },
 55    { spRANGER, spREADY, spREINDEER, spRELIEF, spRELIEVE, spREMOVE, spRHYTHM, spROCK,
 56      spRURAL, spSAYS, spSCHEDULE, spSCHOOL, spSCISSORS, spSEARCH, spSERIOUS, spSHIELD,
 57      spSHOULD, spSHOULDER, spSHOVEL, spSIGN, spSKI, spSOMEONE, spSOMETIME, spSOURCE,
 58      spSPELL, spSPONGE, spSPREAD, spSQUAD, NULL, spSQUASH, spSQUAT, spSTATUE },
 59    { spSTOMACH, spSTRANGER, spSUGAR, spSURE, spSURGEON, spSWAMP, spSWAN, spSWAP,
 60      spSWEAT, spSWEATER, spTALK, spTODAY, spTOMORROW, spTON, spTONGUE, spTOUCH,
 61      spTOUGH, spTOWARD, spTREASURE, spTRY, spUNCOVER, spUNION, spUSUAL, spVIEW,
 62      spWALK, spWARM, spWAS, spWASH, spWATCH, NULL, spWATER, spWEALTH },
 63    { spWEIRD, spWELCOME, spWILD, spWOLVES, spWOMAN, spWONDER, spWORD, spWORLD,
 64      spWORTH, spWRONG, spYIELD, spYOLK, spYOUNG, spYOURSELF, spZEROS, spHERE_IS_YOUR_SCORE,
 65      spI_WIN, spNEXT_SPELL, spNOW_SPELL, spNOW_TRY, spPERFECT_SCORE, spSAY_IT, spTHAT_IS_CORRECT, spTHAT_IS_INCORRECT,
 66      spTHAT_IS_RIGHT, spTHE_CORRECT_SPELLING_OF, spTO_WED, spTRY_AGAIN, spYOU_ARE_CORRECT, spYOU_ARE_RIGHT, NULL, spYOU_WIN },
 67    { spA , spB , spC , spD , spE , spF , spG , spH ,
 68      spI , spJ , spK , spL , spM , spN , spO , spP ,
 69      spQ , spR , spS , spT , spU , spV , spW , spX ,
 70      spY , spZ , spAPOSTROPHE , spBEEPS_1 , spBEEPS_2 , spBEEPS_3 , spBEEPS_4, NULL } };
 71  
 72  uint8_t shiftmode = 0; // Which of 8 shift buttons is held (0 = none)
 73  
 74  // Lights the bottom-most row of buttons in the "ready for input" pattern
 75  void idle(void) {
 76    trellis.fill(0);
 77    for(int i=0; i<8; i++) {
 78      trellis.setPixelColor(24 + i, trellis.ColorHSV(i * 65536 / 8, 255, 6));
 79    }
 80  }
 81  
 82  // SETUP FUNCTION - RUNS ONCE AT STARTUP ***********************************
 83  
 84  void setup() {
 85    trellis.begin();
 86  
 87    // You MUST call this function before issuing Speak & Spell data to the
 88    // Talkie library. By default, that library emulates the TMS5220, a later
 89    // speech chip (used in the TI-99/4A and others) that uses a slightly
 90    // different data format. This puts it in TMS5100-compatible mode:
 91    voice.mode(TALKIE_TMS5100);
 92  
 93    idle(); // Light Trellis buttons in the "ready for input" pattern
 94  }
 95  
 96  // LOOP FUNCTION - AFTER SETUP, RUNS REPEATEDLY ****************************
 97  
 98  void loop() {
 99    trellis.tick();                                 // Generate button events
100    while(trellis.available()) {                    // Process each event...
101      keypadEvent e = trellis.read();
102      if(e.bit.EVENT == KEY_JUST_PRESSED) {         // Button press?
103        if(shiftmode || (e.bit.KEY < 24)) {         // If not a shift-button
104          if(sound[shiftmode][e.bit.KEY] != NULL) { // and has word assigned,
105            voice.say(sound[shiftmode][e.bit.KEY], false); // Say it!
106          }
107        } else {                                    // Is a shift-button
108          shiftmode = e.bit.KEY - 23;               // shiftmode = 1 to 8
109          trellis.fill(trellis.ColorHSV((shiftmode-1) * 65536 / 8, 255, 2));
110        }
111      } else if(e.bit.EVENT == KEY_JUST_RELEASED) { // Button release?
112        if(e.bit.KEY == (shiftmode + 23)) {         // Shift mode button?
113          shiftmode = 0;                            // Un-shift
114          idle();                                   // Restore 'ready' lights
115        }
116      }
117    }
118  }