text-to-speech through DAC to audio amp/speaker

Dependencies:   mbed

text-to-speech TTS

Committer:
manitou
Date:
Sat Jun 24 19:41:42 2017 +0000
Revision:
3:d12c34704b6d
Parent:
2:eceeac07154b
format/indent

Who changed what in which revision?

UserRevisionLine numberNew contents of line
manitou 3:d12c34704b6d 1 /**
manitou 3:d12c34704b6d 2 * Text To Speech synthesis library
manitou 0:bcd16e4a0207 3 * Copyright (c) 2008 Clive Webster. All rights reserved.
manitou 0:bcd16e4a0207 4 *
manitou 0:bcd16e4a0207 5 * Nov. 29th 2009 - Modified to work with Arduino by Gabriel Petrut:
manitou 0:bcd16e4a0207 6 * The Text To Speech library uses Timer1 to generate the PWM
manitou 0:bcd16e4a0207 7 * output on digital pin 10. The output signal needs to be fed
manitou 0:bcd16e4a0207 8 * to an RC filter then through an amplifier to the speaker.
manitou 0:bcd16e4a0207 9 * http://www.tehnorama.ro/minieric-modulul-de-control-si-sinteza-vocala/
manitou 3:d12c34704b6d 10 *
manitou 0:bcd16e4a0207 11 * Modified to allow use of different PWM pins by Stephen Crane.
manitou 0:bcd16e4a0207 12 * Modified for Timer5 on Arduino Mega2560 by Peter Dambrowsky.
manitou 0:bcd16e4a0207 13 */
manitou 0:bcd16e4a0207 14
manitou 0:bcd16e4a0207 15 #include "TTS.h"
manitou 0:bcd16e4a0207 16
manitou 0:bcd16e4a0207 17 AnalogOut dac(DACpin);
manitou 0:bcd16e4a0207 18
manitou 0:bcd16e4a0207 19 // Random number seed
manitou 0:bcd16e4a0207 20 static byte seed0;
manitou 0:bcd16e4a0207 21 static byte seed1;
manitou 0:bcd16e4a0207 22 static byte seed2;
manitou 0:bcd16e4a0207 23
manitou 0:bcd16e4a0207 24 static char phonemes[128];
manitou 0:bcd16e4a0207 25 static char modifier[128]; // must be same size as 'phonemes'
manitou 0:bcd16e4a0207 26 static char g_text[128];
manitou 0:bcd16e4a0207 27
manitou 0:bcd16e4a0207 28 static byte defaultPitch = 7;
manitou 0:bcd16e4a0207 29
manitou 0:bcd16e4a0207 30 // Lookup user specified pitch changes
manitou 0:bcd16e4a0207 31 static const byte PROGMEM PitchesP[] = { 1, 2, 4, 6, 8, 10, 13, 16 };
manitou 0:bcd16e4a0207 32
manitou 0:bcd16e4a0207 33 /**
manitou 0:bcd16e4a0207 34 * Find the single character 'token' in 'vocab'
manitou 0:bcd16e4a0207 35 * and append its phonemes to dest[x]
manitou 0:bcd16e4a0207 36 */
manitou 0:bcd16e4a0207 37 static int copyToken(char token, char *dest, int x, const VOCAB * vocab)
manitou 0:bcd16e4a0207 38 {
manitou 0:bcd16e4a0207 39 for (unsigned int ph = 0; ph < numVocab; ph++) {
manitou 3:d12c34704b6d 40 const char *txt = (const char *) pgm_read_word(&vocab[ph].txt);
manitou 3:d12c34704b6d 41 if (pgm_read_byte(&txt[0]) == token && pgm_read_byte(&txt[1]) == 0) {
manitou 3:d12c34704b6d 42 const char *src =
manitou 3:d12c34704b6d 43 (const char *) pgm_read_word(&vocab[ph].phoneme);
manitou 3:d12c34704b6d 44 while (pgm_read_byte(src)) {
manitou 3:d12c34704b6d 45 dest[x++] = pgm_read_byte(src);
manitou 3:d12c34704b6d 46 src++;
manitou 3:d12c34704b6d 47 }
manitou 3:d12c34704b6d 48 break;
manitou 0:bcd16e4a0207 49 }
manitou 0:bcd16e4a0207 50 }
manitou 0:bcd16e4a0207 51 return x;
manitou 0:bcd16e4a0207 52 }
manitou 0:bcd16e4a0207 53
manitou 0:bcd16e4a0207 54 static byte whitespace(char c)
manitou 0:bcd16e4a0207 55 {
manitou 0:bcd16e4a0207 56 return (c == 0 || c == ' ' || c == ',' || c == '.' || c == '?'
manitou 3:d12c34704b6d 57 || c == '\'' || c == '!' || c == ':' || c == '/');
manitou 0:bcd16e4a0207 58 }
manitou 0:bcd16e4a0207 59
manitou 0:bcd16e4a0207 60 /**
manitou 0:bcd16e4a0207 61 * Enter:
manitou 0:bcd16e4a0207 62 * src => English text in upper case
manitou 0:bcd16e4a0207 63 * vocab => VOCAB array
manitou 0:bcd16e4a0207 64 * dest => address to return result
manitou 0:bcd16e4a0207 65 * return 1 if ok, or 0 if error
manitou 0:bcd16e4a0207 66 */
manitou 0:bcd16e4a0207 67 static int textToPhonemes(const char *src, const VOCAB * vocab, char *dest)
manitou 0:bcd16e4a0207 68 {
manitou 0:bcd16e4a0207 69 int outIndex = 0; // Current offset into dest
manitou 0:bcd16e4a0207 70 int inIndex = -1; // Starts at -1 so that a leading space is assumed
manitou 0:bcd16e4a0207 71
manitou 0:bcd16e4a0207 72 while (inIndex == -1 || src[inIndex]) { // until end of text
manitou 3:d12c34704b6d 73 int maxMatch = 0; // Max chars matched on input text
manitou 3:d12c34704b6d 74 int numOut = 0; // Number of characters copied to output stream for the best match
manitou 3:d12c34704b6d 75 boolean endsInWhiteSpace = FALSE;
manitou 3:d12c34704b6d 76 int maxWildcardPos = 0;
manitou 0:bcd16e4a0207 77
manitou 3:d12c34704b6d 78 // Get next phoneme, P2
manitou 3:d12c34704b6d 79 for (unsigned int ph = 0; ph < numVocab; ph++) {
manitou 3:d12c34704b6d 80 int y, x;
manitou 3:d12c34704b6d 81 char wildcard = 0; // modifier
manitou 3:d12c34704b6d 82 int wildcardInPos = 0;
manitou 3:d12c34704b6d 83 boolean hasWhiteSpace = FALSE;
manitou 3:d12c34704b6d 84 const char *text =
manitou 3:d12c34704b6d 85 (const char *) pgm_read_word(&vocab[ph].txt);
manitou 3:d12c34704b6d 86 const char *phon =
manitou 3:d12c34704b6d 87 (const char *) pgm_read_word(&vocab[ph].phoneme);
manitou 0:bcd16e4a0207 88
manitou 3:d12c34704b6d 89 for (y = 0;; y++) {
manitou 3:d12c34704b6d 90 char nextVocabChar = pgm_read_byte(&text[y]);
manitou 3:d12c34704b6d 91 char nextCharIn =
manitou 3:d12c34704b6d 92 (y + inIndex == -1) ? ' ' : src[y + inIndex];
manitou 3:d12c34704b6d 93 if (nextCharIn >= 'a' && nextCharIn <= 'z')
manitou 3:d12c34704b6d 94 nextCharIn = nextCharIn - 'a' + 'A';
manitou 0:bcd16e4a0207 95
manitou 3:d12c34704b6d 96 if (nextVocabChar == '#' && nextCharIn >= 'A'
manitou 3:d12c34704b6d 97 && nextCharIn <= 'Z') {
manitou 3:d12c34704b6d 98 wildcard = nextCharIn; // The character equivalent to the '#'
manitou 3:d12c34704b6d 99 wildcardInPos = y;
manitou 3:d12c34704b6d 100 continue;
manitou 3:d12c34704b6d 101 }
manitou 0:bcd16e4a0207 102
manitou 3:d12c34704b6d 103 if (nextVocabChar == '_') {
manitou 3:d12c34704b6d 104 // try to match against a white space
manitou 3:d12c34704b6d 105 hasWhiteSpace = TRUE;
manitou 3:d12c34704b6d 106 if (whitespace(nextCharIn))
manitou 3:d12c34704b6d 107 continue;
manitou 3:d12c34704b6d 108 y--;
manitou 3:d12c34704b6d 109 break;
manitou 3:d12c34704b6d 110 }
manitou 3:d12c34704b6d 111 // check for end of either string
manitou 3:d12c34704b6d 112 if (nextVocabChar == 0 || nextCharIn == 0)
manitou 3:d12c34704b6d 113 break;
manitou 0:bcd16e4a0207 114
manitou 3:d12c34704b6d 115 if (nextVocabChar != nextCharIn)
manitou 3:d12c34704b6d 116 break;
manitou 3:d12c34704b6d 117 }
manitou 0:bcd16e4a0207 118
manitou 3:d12c34704b6d 119 // See if its the longest complete match so far
manitou 3:d12c34704b6d 120 if (y <= maxMatch || pgm_read_byte(&text[y]))
manitou 3:d12c34704b6d 121 continue;
manitou 0:bcd16e4a0207 122
manitou 3:d12c34704b6d 123 // This is the longest complete match
manitou 3:d12c34704b6d 124 maxMatch = y;
manitou 3:d12c34704b6d 125 maxWildcardPos = 0;
manitou 3:d12c34704b6d 126 x = outIndex; // offset into phoneme return data
manitou 0:bcd16e4a0207 127
manitou 3:d12c34704b6d 128 // Copy the matching phrase changing any '#' to the phoneme for the wildcard
manitou 3:d12c34704b6d 129 for (y = 0;; y++) {
manitou 3:d12c34704b6d 130 char c = pgm_read_byte(&phon[y]);
manitou 3:d12c34704b6d 131 if (c == 0)
manitou 3:d12c34704b6d 132 break;
manitou 3:d12c34704b6d 133 if (c == '#') {
manitou 3:d12c34704b6d 134 if (pgm_read_byte(&phon[y + 1]) == 0) {
manitou 3:d12c34704b6d 135 // replacement ends in wildcard
manitou 3:d12c34704b6d 136 maxWildcardPos = wildcardInPos;
manitou 3:d12c34704b6d 137 } else {
manitou 3:d12c34704b6d 138 x = copyToken(wildcard, dest, x, vocab); // Copy the phonemes for the wildcard character
manitou 3:d12c34704b6d 139 }
manitou 3:d12c34704b6d 140 } else {
manitou 3:d12c34704b6d 141 dest[x++] = c;
manitou 3:d12c34704b6d 142 }
manitou 0:bcd16e4a0207 143 }
manitou 3:d12c34704b6d 144 dest[x] = 0;
manitou 3:d12c34704b6d 145 endsInWhiteSpace = hasWhiteSpace;
manitou 3:d12c34704b6d 146
manitou 3:d12c34704b6d 147 // 14
manitou 3:d12c34704b6d 148 numOut = x - outIndex; // The number of bytes added
manitou 0:bcd16e4a0207 149 }
manitou 3:d12c34704b6d 150 // 15 - end of vocab table
manitou 0:bcd16e4a0207 151
manitou 3:d12c34704b6d 152 // 16
manitou 3:d12c34704b6d 153 if (endsInWhiteSpace)
manitou 3:d12c34704b6d 154 maxMatch--;
manitou 0:bcd16e4a0207 155
manitou 3:d12c34704b6d 156 // 17
manitou 3:d12c34704b6d 157 if (maxMatch == 0) {
manitou 3:d12c34704b6d 158 //loggerP(PSTR("Mistake in SAY, no token for "));
manitou 3:d12c34704b6d 159 //logger(&src[inIndex]);
manitou 3:d12c34704b6d 160 //loggerCRLF();
manitou 3:d12c34704b6d 161 return 0;
manitou 3:d12c34704b6d 162 }
manitou 3:d12c34704b6d 163 // 20
manitou 3:d12c34704b6d 164 outIndex += numOut;
manitou 3:d12c34704b6d 165 if (outIndex > 128 - 16) {
manitou 3:d12c34704b6d 166 //loggerP(PSTR("Mistake in SAY, text too long\n"));
manitou 3:d12c34704b6d 167 return 0;
manitou 3:d12c34704b6d 168 }
manitou 3:d12c34704b6d 169 // 21
manitou 3:d12c34704b6d 170 inIndex += (maxWildcardPos > 0) ? maxWildcardPos : maxMatch;
manitou 0:bcd16e4a0207 171 }
manitou 0:bcd16e4a0207 172 return 1;
manitou 0:bcd16e4a0207 173 }
manitou 0:bcd16e4a0207 174
manitou 0:bcd16e4a0207 175 /**
manitou 0:bcd16e4a0207 176 * Convert phonemes to data string
manitou 0:bcd16e4a0207 177 * Enter: textp = phonemes string
manitou 0:bcd16e4a0207 178 * Return: phonemes = string of sound data
manitou 0:bcd16e4a0207 179 * modifier = 2 bytes per sound data
manitou 0:bcd16e4a0207 180 */
manitou 0:bcd16e4a0207 181 static int phonemesToData(const char *textp, const PHONEME * phoneme)
manitou 0:bcd16e4a0207 182 {
manitou 0:bcd16e4a0207 183 unsigned int phonemeOut = 0; // offset into the phonemes array
manitou 0:bcd16e4a0207 184 unsigned int modifierOut = 0; // offset into the modifiers array
manitou 0:bcd16e4a0207 185 unsigned int L81 = 0; // attenuate
manitou 0:bcd16e4a0207 186 unsigned int L80 = 16;
manitou 0:bcd16e4a0207 187
manitou 0:bcd16e4a0207 188 while (*textp) {
manitou 3:d12c34704b6d 189 // P20: Get next phoneme
manitou 3:d12c34704b6d 190 boolean anyMatch = FALSE;
manitou 3:d12c34704b6d 191 int longestMatch = 0;
manitou 3:d12c34704b6d 192 int numOut = 0; // The number of bytes copied to the output for the longest match
manitou 0:bcd16e4a0207 193
manitou 3:d12c34704b6d 194 // Get next phoneme, P2
manitou 3:d12c34704b6d 195 for (unsigned int ph = 0; ph < numPhoneme; ph++) {
manitou 3:d12c34704b6d 196 int numChars;
manitou 0:bcd16e4a0207 197
manitou 3:d12c34704b6d 198 // Locate start of next phoneme
manitou 3:d12c34704b6d 199 const char *ph_text =
manitou 3:d12c34704b6d 200 (const char *) pgm_read_word(&phoneme[ph].txt);
manitou 0:bcd16e4a0207 201
manitou 3:d12c34704b6d 202 // Set 'numChars' to the number of characters
manitou 3:d12c34704b6d 203 // that we match against this phoneme
manitou 3:d12c34704b6d 204 for (numChars = 0; textp[numChars]; numChars++) {
manitou 0:bcd16e4a0207 205
manitou 3:d12c34704b6d 206 // get next input character and make lower case
manitou 3:d12c34704b6d 207 char nextChar = textp[numChars];
manitou 3:d12c34704b6d 208 if (nextChar >= 'A' && nextChar <= 'Z')
manitou 3:d12c34704b6d 209 nextChar = nextChar - 'A' + 'a';
manitou 0:bcd16e4a0207 210
manitou 3:d12c34704b6d 211 if (nextChar != pgm_read_byte(&ph_text[numChars]))
manitou 3:d12c34704b6d 212 break;
manitou 3:d12c34704b6d 213 }
manitou 0:bcd16e4a0207 214
manitou 3:d12c34704b6d 215 // if not the longest match so far then ignore
manitou 3:d12c34704b6d 216 if (numChars <= longestMatch)
manitou 3:d12c34704b6d 217 continue;
manitou 0:bcd16e4a0207 218
manitou 3:d12c34704b6d 219 // partial phoneme match
manitou 3:d12c34704b6d 220 if (pgm_read_byte(&ph_text[numChars]))
manitou 3:d12c34704b6d 221 continue;
manitou 0:bcd16e4a0207 222
manitou 3:d12c34704b6d 223 // P7: we have matched the whole phoneme
manitou 3:d12c34704b6d 224 longestMatch = numChars;
manitou 0:bcd16e4a0207 225
manitou 3:d12c34704b6d 226 // Copy phoneme data to 'phonemes'
manitou 3:d12c34704b6d 227 const char *ph_ph =
manitou 3:d12c34704b6d 228 (const char *) pgm_read_word(&phoneme[ph].phoneme);
manitou 3:d12c34704b6d 229 for (numOut = 0; pgm_read_byte(&ph_ph[numOut]); numOut++)
manitou 3:d12c34704b6d 230 phonemes[phonemeOut + numOut] =
manitou 3:d12c34704b6d 231 pgm_read_byte(&ph_ph[numOut]);
manitou 0:bcd16e4a0207 232
manitou 3:d12c34704b6d 233 L81 = pgm_read_byte(&phoneme[ph].attenuate) + '0';
manitou 3:d12c34704b6d 234 anyMatch = TRUE; // phoneme match found
manitou 0:bcd16e4a0207 235
manitou 3:d12c34704b6d 236 modifier[modifierOut] = -1;
manitou 3:d12c34704b6d 237 modifier[modifierOut + 1] = 0;
manitou 0:bcd16e4a0207 238
manitou 3:d12c34704b6d 239 // Get char from text after the phoneme and test if it is a numeric
manitou 3:d12c34704b6d 240 if (textp[longestMatch] >= '0' && textp[longestMatch] <= '9') {
manitou 3:d12c34704b6d 241 // Pitch change requested
manitou 3:d12c34704b6d 242 modifier[modifierOut] =
manitou 3:d12c34704b6d 243 pgm_read_byte(&PitchesP[textp[longestMatch] - '1']);
manitou 3:d12c34704b6d 244 modifier[modifierOut + 1] = L81;
manitou 3:d12c34704b6d 245 longestMatch++;
manitou 3:d12c34704b6d 246 }
manitou 3:d12c34704b6d 247 // P10
manitou 3:d12c34704b6d 248 if (L81 != '0' && L81 != L80 && modifier[modifierOut] >= 0) {
manitou 3:d12c34704b6d 249 modifier[modifierOut - 2] = modifier[modifierOut];
manitou 3:d12c34704b6d 250 modifier[modifierOut - 1] = '0';
manitou 3:d12c34704b6d 251 continue;
manitou 3:d12c34704b6d 252 }
manitou 3:d12c34704b6d 253 // P11
manitou 3:d12c34704b6d 254 if ((textp[longestMatch - 1] | 0x20) == 0x20) {
manitou 3:d12c34704b6d 255 // end of input string or a space
manitou 3:d12c34704b6d 256 modifier[modifierOut] =
manitou 3:d12c34704b6d 257 (modifierOut == 0) ? 16 : modifier[modifierOut - 2];
manitou 3:d12c34704b6d 258 }
manitou 3:d12c34704b6d 259 } // next phoneme
manitou 0:bcd16e4a0207 260
manitou 3:d12c34704b6d 261 // p13
manitou 3:d12c34704b6d 262 L80 = L81;
manitou 3:d12c34704b6d 263 if (longestMatch == 0 && !anyMatch) {
manitou 3:d12c34704b6d 264 //loggerP(PSTR("Mistake in speech at "));
manitou 3:d12c34704b6d 265 //logger(textp);
manitou 3:d12c34704b6d 266 //loggerCRLF();
manitou 3:d12c34704b6d 267 return 0;
manitou 3:d12c34704b6d 268 }
manitou 3:d12c34704b6d 269 // Move over the bytes we have copied to the output
manitou 3:d12c34704b6d 270 phonemeOut += numOut;
manitou 0:bcd16e4a0207 271
manitou 3:d12c34704b6d 272 if (phonemeOut > sizeof(phonemes) - 16) {
manitou 3:d12c34704b6d 273 //loggerP(PSTR("Line too long\n"));
manitou 3:d12c34704b6d 274 return 0;
manitou 3:d12c34704b6d 275 }
manitou 3:d12c34704b6d 276 // P16
manitou 0:bcd16e4a0207 277
manitou 3:d12c34704b6d 278 // Copy the modifier setting to each sound data element for this phoneme
manitou 3:d12c34704b6d 279 if (numOut > 2)
manitou 3:d12c34704b6d 280 for (int count = 0; count != numOut; count += 2) {
manitou 3:d12c34704b6d 281 modifier[modifierOut + count + 2] =
manitou 3:d12c34704b6d 282 modifier[modifierOut + count];
manitou 3:d12c34704b6d 283 modifier[modifierOut + count + 3] = 0;
manitou 3:d12c34704b6d 284 }
manitou 3:d12c34704b6d 285 modifierOut += numOut;
manitou 0:bcd16e4a0207 286
manitou 3:d12c34704b6d 287 //p21
manitou 3:d12c34704b6d 288 textp += longestMatch;
manitou 0:bcd16e4a0207 289 }
manitou 0:bcd16e4a0207 290
manitou 0:bcd16e4a0207 291 phonemes[phonemeOut++] = 'z';
manitou 0:bcd16e4a0207 292 phonemes[phonemeOut++] = 'z';
manitou 0:bcd16e4a0207 293 phonemes[phonemeOut++] = 'z';
manitou 0:bcd16e4a0207 294 phonemes[phonemeOut++] = 'z';
manitou 0:bcd16e4a0207 295
manitou 0:bcd16e4a0207 296 while (phonemeOut < sizeof(phonemes))
manitou 3:d12c34704b6d 297 phonemes[phonemeOut++] = 0;
manitou 0:bcd16e4a0207 298
manitou 0:bcd16e4a0207 299 while (modifierOut < sizeof(modifier)) {
manitou 3:d12c34704b6d 300 modifier[modifierOut++] = -1;
manitou 3:d12c34704b6d 301 modifier[modifierOut++] = 0;
manitou 0:bcd16e4a0207 302 }
manitou 0:bcd16e4a0207 303
manitou 0:bcd16e4a0207 304 return 1;
manitou 0:bcd16e4a0207 305 }
manitou 0:bcd16e4a0207 306
manitou 0:bcd16e4a0207 307 /*
manitou 0:bcd16e4a0207 308 * A delay loop that doesn't change with different optimisation settings
manitou 0:bcd16e4a0207 309 */
manitou 0:bcd16e4a0207 310
manitou 0:bcd16e4a0207 311
manitou 0:bcd16e4a0207 312 static void pause(byte delays)
manitou 0:bcd16e4a0207 313 {
manitou 0:bcd16e4a0207 314 wait_us(delays*6);
manitou 0:bcd16e4a0207 315 }
manitou 0:bcd16e4a0207 316
manitou 0:bcd16e4a0207 317 static void delay2(byte d)
manitou 0:bcd16e4a0207 318 {
manitou 0:bcd16e4a0207 319 wait_us(d*3127);
manitou 0:bcd16e4a0207 320 }
manitou 0:bcd16e4a0207 321
manitou 0:bcd16e4a0207 322 /*
manitou 0:bcd16e4a0207 323 * Generate a random number
manitou 0:bcd16e4a0207 324 */
manitou 0:bcd16e4a0207 325 static byte random2(void)
manitou 0:bcd16e4a0207 326 {
manitou 0:bcd16e4a0207 327 byte tmp = (seed0 & 0x48) + 0x38;
manitou 0:bcd16e4a0207 328 seed0 <<= 1;
manitou 0:bcd16e4a0207 329 if (seed1 & 0x80)
manitou 3:d12c34704b6d 330 seed0++;
manitou 0:bcd16e4a0207 331 seed1 <<= 1;
manitou 0:bcd16e4a0207 332 if (seed2 & 0x80)
manitou 3:d12c34704b6d 333 seed1++;
manitou 0:bcd16e4a0207 334 seed2 <<= 1;
manitou 0:bcd16e4a0207 335 if (tmp & 0x40)
manitou 3:d12c34704b6d 336 seed2++;
manitou 0:bcd16e4a0207 337 return seed0;
manitou 0:bcd16e4a0207 338 }
manitou 0:bcd16e4a0207 339
manitou 0:bcd16e4a0207 340 static int pin;
manitou 0:bcd16e4a0207 341
manitou 0:bcd16e4a0207 342 static void soundOff(void)
manitou 0:bcd16e4a0207 343 {
manitou 2:eceeac07154b 344 //dac.write(0);
manitou 0:bcd16e4a0207 345 }
manitou 0:bcd16e4a0207 346
manitou 0:bcd16e4a0207 347 #define PWM_TOP (1200/2)
manitou 0:bcd16e4a0207 348
manitou 0:bcd16e4a0207 349 //https://sites.google.com/site/qeewiki/books/avr-guide/pwm-on-the-atmega328
manitou 0:bcd16e4a0207 350 static void soundOn(void)
manitou 0:bcd16e4a0207 351 {
manitou 3:d12c34704b6d 352 // dac.write(0);
manitou 0:bcd16e4a0207 353
manitou 0:bcd16e4a0207 354 // initialise random number seed
manitou 0:bcd16e4a0207 355 seed0 = 0xecu;
manitou 0:bcd16e4a0207 356 seed1 = 7;
manitou 0:bcd16e4a0207 357 seed2 = 0xcfu;
manitou 0:bcd16e4a0207 358 }
manitou 0:bcd16e4a0207 359
manitou 0:bcd16e4a0207 360 // Logarithmic scale
manitou 0:bcd16e4a0207 361 //static const int16_t PROGMEM Volume[8] =
manitou 3:d12c34704b6d 362 //{ 0, PWM_TOP * 0.01, PWM_TOP * 0.02, PWM_TOP * 0.03, PWM_TOP * 0.06,
manitou 0:bcd16e4a0207 363 //PWM_TOP * 0.12, PWM_TOP * 0.25, PWM_TOP * 0.5 };
manitou 0:bcd16e4a0207 364
manitou 0:bcd16e4a0207 365 // Linear scale
manitou 3:d12c34704b6d 366 static const int16_t PROGMEM Volume[8] = {
manitou 3:d12c34704b6d 367 0, (uint16_t)(PWM_TOP * 0.07), (uint16_t)(PWM_TOP * 0.14), (uint16_t)(PWM_TOP * 0.21), (uint16_t)(PWM_TOP * 0.29),
manitou 0:bcd16e4a0207 368 (uint16_t)(PWM_TOP * 0.36), (uint16_t)(PWM_TOP * 0.43), (uint16_t)(PWM_TOP * 0.5)
manitou 0:bcd16e4a0207 369 };
manitou 0:bcd16e4a0207 370
manitou 0:bcd16e4a0207 371 static void sound(byte b)
manitou 0:bcd16e4a0207 372 {
manitou 3:d12c34704b6d 373 // Update PWM volume
manitou 0:bcd16e4a0207 374 b = (b & 15);
manitou 0:bcd16e4a0207 375 dac.write(0.5*b/16.);
manitou 0:bcd16e4a0207 376 }
manitou 0:bcd16e4a0207 377
manitou 0:bcd16e4a0207 378 static byte playTone(byte soundNum, byte soundPos, char pitch1,
manitou 3:d12c34704b6d 379 char pitch2, byte count, byte volume)
manitou 0:bcd16e4a0207 380 {
manitou 0:bcd16e4a0207 381 const byte *soundData = &SoundData[soundNum * 0x40];
manitou 0:bcd16e4a0207 382 while (count-- > 0) {
manitou 3:d12c34704b6d 383 byte s = pgm_read_byte(&soundData[soundPos & 0x3fu]);
manitou 3:d12c34704b6d 384 sound((byte) (s & volume));
manitou 3:d12c34704b6d 385 pause(pitch1);
manitou 3:d12c34704b6d 386 sound((byte) ((s >> 4) & volume));
manitou 3:d12c34704b6d 387 pause(pitch2);
manitou 0:bcd16e4a0207 388
manitou 3:d12c34704b6d 389 soundPos++;
manitou 0:bcd16e4a0207 390 }
manitou 0:bcd16e4a0207 391 return soundPos & 0x3fu;
manitou 0:bcd16e4a0207 392 }
manitou 0:bcd16e4a0207 393
manitou 0:bcd16e4a0207 394 static void play(byte duration, byte soundNumber)
manitou 0:bcd16e4a0207 395 {
manitou 0:bcd16e4a0207 396 while (duration--)
manitou 3:d12c34704b6d 397 playTone(soundNumber, random2(), 7, 7, 10, 15);
manitou 0:bcd16e4a0207 398 }
manitou 0:bcd16e4a0207 399
manitou 0:bcd16e4a0207 400 /******************************************************************************
manitou 0:bcd16e4a0207 401 * User API
manitou 0:bcd16e4a0207 402 ******************************************************************************/
manitou 0:bcd16e4a0207 403 TTS::TTS()
manitou 0:bcd16e4a0207 404 {
manitou 0:bcd16e4a0207 405 }
manitou 0:bcd16e4a0207 406
manitou 0:bcd16e4a0207 407 void TTS::setPitch(byte pitch)
manitou 0:bcd16e4a0207 408 {
manitou 0:bcd16e4a0207 409 defaultPitch = pitch;
manitou 0:bcd16e4a0207 410 }
manitou 0:bcd16e4a0207 411
manitou 0:bcd16e4a0207 412 byte TTS::getPitch(void)
manitou 0:bcd16e4a0207 413 {
manitou 0:bcd16e4a0207 414 return defaultPitch;
manitou 0:bcd16e4a0207 415 }
manitou 0:bcd16e4a0207 416
manitou 0:bcd16e4a0207 417 /*
manitou 0:bcd16e4a0207 418 * Speak a string of phonemes
manitou 0:bcd16e4a0207 419 */
manitou 0:bcd16e4a0207 420 void TTS::sayPhonemes(const char *textp)
manitou 0:bcd16e4a0207 421 {
manitou 0:bcd16e4a0207 422 byte phonemeIn, // offset into text
manitou 3:d12c34704b6d 423 byte2, modifierIn, // offset into stuff in modifier
manitou 3:d12c34704b6d 424 punctuationPitchDelta; // change in pitch due to fullstop or question mark
manitou 0:bcd16e4a0207 425 int8_t byte1;
manitou 0:bcd16e4a0207 426 char phoneme;
manitou 0:bcd16e4a0207 427 const SOUND_INDEX *soundIndex;
manitou 0:bcd16e4a0207 428 byte sound1Num; // Sound data for the current phoneme
manitou 0:bcd16e4a0207 429 byte sound2Num; // Sound data for the next phoneme
manitou 0:bcd16e4a0207 430 byte sound2Stop; // Where the second sound should stop
manitou 0:bcd16e4a0207 431 char pitch1; // pitch for the first sound
manitou 0:bcd16e4a0207 432 char pitch2; // pitch for the second sound
manitou 0:bcd16e4a0207 433 short i;
manitou 0:bcd16e4a0207 434 byte sound1Duration; // the duration for sound 1
manitou 0:bcd16e4a0207 435
manitou 0:bcd16e4a0207 436 if (phonemesToData(textp, s_phonemes)) {
manitou 3:d12c34704b6d 437 // phonemes has list of sound bytes
manitou 3:d12c34704b6d 438 soundOn();
manitou 0:bcd16e4a0207 439
manitou 3:d12c34704b6d 440 // _630C
manitou 3:d12c34704b6d 441 byte1 = 0;
manitou 3:d12c34704b6d 442 punctuationPitchDelta = 0;
manitou 0:bcd16e4a0207 443
manitou 3:d12c34704b6d 444 // Q19
manitou 3:d12c34704b6d 445 for (phonemeIn = 0, modifierIn = 0; phonemes[phonemeIn];
manitou 3:d12c34704b6d 446 phonemeIn += 2, modifierIn += 2) {
manitou 3:d12c34704b6d 447 byte duration; // duration from text line
manitou 3:d12c34704b6d 448 byte SoundPos; // offset into sound data
manitou 3:d12c34704b6d 449 byte fadeSpeed = 0;
manitou 0:bcd16e4a0207 450
manitou 3:d12c34704b6d 451 phoneme = phonemes[phonemeIn];
manitou 3:d12c34704b6d 452 if (phoneme == 'z') {
manitou 3:d12c34704b6d 453 delay2(15);
manitou 3:d12c34704b6d 454 continue;
manitou 3:d12c34704b6d 455 } else if (phoneme == '#') {
manitou 3:d12c34704b6d 456 continue;
manitou 3:d12c34704b6d 457 } else {
manitou 0:bcd16e4a0207 458
manitou 3:d12c34704b6d 459 // Collect info on sound 1
manitou 3:d12c34704b6d 460 soundIndex = &SoundIndex[phoneme - 'A'];
manitou 3:d12c34704b6d 461 sound1Num = pgm_read_byte(&soundIndex->SoundNumber);
manitou 3:d12c34704b6d 462 byte1 = pgm_read_byte(&soundIndex->byte1);
manitou 3:d12c34704b6d 463 byte2 = pgm_read_byte(&soundIndex->byte2);
manitou 0:bcd16e4a0207 464
manitou 3:d12c34704b6d 465 duration = phonemes[phonemeIn + 1] - '0'; // Get duration from the input line
manitou 3:d12c34704b6d 466 if (duration != 1)
manitou 3:d12c34704b6d 467 duration <<= 1;
manitou 0:bcd16e4a0207 468
manitou 3:d12c34704b6d 469 duration += 6; // scaled duration from the input line (at least 6)
manitou 3:d12c34704b6d 470 sound2Stop = 0x40 >> 1;
manitou 0:bcd16e4a0207 471
manitou 3:d12c34704b6d 472 pitch1 = modifier[modifierIn];
manitou 3:d12c34704b6d 473 if (modifier[modifierIn + 1] == 0 || pitch1 == -1) {
manitou 3:d12c34704b6d 474 pitch1 = 10;
manitou 3:d12c34704b6d 475 duration -= 6;
manitou 3:d12c34704b6d 476 } else if (modifier[modifierIn + 1] == '0'
manitou 3:d12c34704b6d 477 || duration == 6) {
manitou 3:d12c34704b6d 478 duration -= 6;
manitou 3:d12c34704b6d 479 }
manitou 3:d12c34704b6d 480 // q8
manitou 3:d12c34704b6d 481 pitch2 = modifier[modifierIn + 2];
manitou 3:d12c34704b6d 482 if (modifier[modifierIn + 3] == 0 || pitch2 == -1)
manitou 3:d12c34704b6d 483 pitch2 = 10;
manitou 0:bcd16e4a0207 484
manitou 3:d12c34704b6d 485 // q10
manitou 3:d12c34704b6d 486 if (byte1 < 0) {
manitou 3:d12c34704b6d 487 sound1Num = 0;
manitou 3:d12c34704b6d 488 random2();
manitou 3:d12c34704b6d 489 sound2Stop = (0x40 >> 1) + 2;
manitou 3:d12c34704b6d 490 } else {
manitou 3:d12c34704b6d 491 // is positive
manitou 3:d12c34704b6d 492 if (byte1 == 2) {
manitou 3:d12c34704b6d 493 // 64A4
manitou 3:d12c34704b6d 494 // Make a white noise sound !
manitou 3:d12c34704b6d 495 byte volume = (duration == 6) ? 15 : 1; // volume mask
manitou 3:d12c34704b6d 496 for (duration <<= 2; duration > 0; duration--) {
manitou 3:d12c34704b6d 497 playTone(sound1Num, random2(), 8, 12, 11,
manitou 3:d12c34704b6d 498 volume);
manitou 3:d12c34704b6d 499 // Increase the volume
manitou 3:d12c34704b6d 500 if (++volume == 16)
manitou 3:d12c34704b6d 501 volume = 15; // full volume from now on
manitou 3:d12c34704b6d 502 }
manitou 3:d12c34704b6d 503 continue;
manitou 0:bcd16e4a0207 504
manitou 3:d12c34704b6d 505 } else {
manitou 3:d12c34704b6d 506 // q11
manitou 3:d12c34704b6d 507 if (byte1)
manitou 3:d12c34704b6d 508 delay2(25);
manitou 3:d12c34704b6d 509 }
manitou 3:d12c34704b6d 510 }
manitou 0:bcd16e4a0207 511 }
manitou 0:bcd16e4a0207 512
manitou 3:d12c34704b6d 513 // 6186
manitou 3:d12c34704b6d 514 pitch1 += defaultPitch + punctuationPitchDelta;
manitou 3:d12c34704b6d 515 if (pitch1 < 1)
manitou 3:d12c34704b6d 516 pitch1 = 1;
manitou 0:bcd16e4a0207 517
manitou 3:d12c34704b6d 518 pitch2 += defaultPitch + punctuationPitchDelta;
manitou 3:d12c34704b6d 519 if (pitch2 < 1)
manitou 3:d12c34704b6d 520 pitch2 = 1;
manitou 0:bcd16e4a0207 521
manitou 3:d12c34704b6d 522 // get next phoneme
manitou 3:d12c34704b6d 523 phoneme = phonemes[phonemeIn + 2];
manitou 0:bcd16e4a0207 524
manitou 3:d12c34704b6d 525 if (phoneme == 0 || phoneme == 'z') {
manitou 3:d12c34704b6d 526 if (duration == 1)
manitou 3:d12c34704b6d 527 delay2(60);
manitou 3:d12c34704b6d 528 phoneme = 'a'; // change to a pause
manitou 3:d12c34704b6d 529 } else {
manitou 3:d12c34704b6d 530 // s6
manitou 3:d12c34704b6d 531 if (byte2 != 1)
manitou 3:d12c34704b6d 532 byte2 =
manitou 3:d12c34704b6d 533 (byte2 +
manitou 3:d12c34704b6d 534 pgm_read_byte(&SoundIndex[phoneme - 'A'].byte2))
manitou 3:d12c34704b6d 535 >> 1;
manitou 0:bcd16e4a0207 536
manitou 3:d12c34704b6d 537 if (byte1 < 0
manitou 3:d12c34704b6d 538 || pgm_read_byte(&SoundIndex[phoneme - 'A'].byte1))
manitou 3:d12c34704b6d 539 phoneme = 'a'; // change to a pause
manitou 3:d12c34704b6d 540 }
manitou 0:bcd16e4a0207 541
manitou 3:d12c34704b6d 542 // S10
manitou 3:d12c34704b6d 543 sound2Num =
manitou 3:d12c34704b6d 544 pgm_read_byte(&SoundIndex[phoneme - 'A'].SoundNumber);
manitou 0:bcd16e4a0207 545
manitou 3:d12c34704b6d 546 sound1Duration = 0x80; // play half of sound 1
manitou 3:d12c34704b6d 547 if (sound2Num == sound1Num)
manitou 3:d12c34704b6d 548 byte2 = duration;
manitou 0:bcd16e4a0207 549
manitou 3:d12c34704b6d 550 // S11
manitou 3:d12c34704b6d 551 if ((byte2 >> 1) == 0) {
manitou 3:d12c34704b6d 552 sound1Duration = 0xff; // play all of sound 1
manitou 3:d12c34704b6d 553 } else {
manitou 3:d12c34704b6d 554 // The fade speed between the two sounds
manitou 3:d12c34704b6d 555 fadeSpeed = (sound1Duration + (byte2 >> 1)) / byte2;
manitou 0:bcd16e4a0207 556
manitou 3:d12c34704b6d 557 if (duration == 1) {
manitou 3:d12c34704b6d 558 sound2Stop = 0x40; // dont play sound2
manitou 3:d12c34704b6d 559 sound1Duration = 0xff; // play all of sound 1
manitou 3:d12c34704b6d 560 pitch1 = 12;
manitou 3:d12c34704b6d 561 }
manitou 3:d12c34704b6d 562 }
manitou 0:bcd16e4a0207 563
manitou 3:d12c34704b6d 564 SoundPos = 0;
manitou 3:d12c34704b6d 565 do {
manitou 3:d12c34704b6d 566 byte sound1Stop = (sound1Duration >> 2) & 0x3fu;
manitou 3:d12c34704b6d 567 byte sound1End = sound1Stop;
manitou 3:d12c34704b6d 568 if (sound2Stop < sound1End) sound1End = sound2Stop; // min
manitou 0:bcd16e4a0207 569
manitou 3:d12c34704b6d 570 if (sound1Stop)
manitou 3:d12c34704b6d 571 SoundPos =
manitou 3:d12c34704b6d 572 playTone(sound1Num, SoundPos, pitch1, pitch1,
manitou 3:d12c34704b6d 573 sound1End, 15);
manitou 0:bcd16e4a0207 574
manitou 3:d12c34704b6d 575 // s18
manitou 3:d12c34704b6d 576 if (sound2Stop != 0x40) {
manitou 3:d12c34704b6d 577 SoundPos =
manitou 3:d12c34704b6d 578 playTone(sound2Num, SoundPos, pitch2, pitch2,
manitou 3:d12c34704b6d 579 (byte) (sound2Stop - sound1End), 15);
manitou 3:d12c34704b6d 580 }
manitou 3:d12c34704b6d 581 // s23
manitou 3:d12c34704b6d 582 if (sound1Duration != 0xff && duration < byte2) {
manitou 3:d12c34704b6d 583 // Fade sound1 out
manitou 3:d12c34704b6d 584 sound1Duration -= fadeSpeed;
manitou 3:d12c34704b6d 585 if (sound1Duration >= (byte) 0xC8)
manitou 3:d12c34704b6d 586 sound1Duration = 0; // stop playing sound 1
manitou 3:d12c34704b6d 587 }
manitou 3:d12c34704b6d 588 // Call any additional sound
manitou 3:d12c34704b6d 589 if (byte1 == -1)
manitou 3:d12c34704b6d 590 play(3, 30); // make an 'f' sound
manitou 3:d12c34704b6d 591 else if (byte1 == -2)
manitou 3:d12c34704b6d 592 play(3, 29); // make an 's' sound
manitou 3:d12c34704b6d 593 else if (byte1 == -3)
manitou 3:d12c34704b6d 594 play(3, 33); // make a 'th' sound
manitou 3:d12c34704b6d 595 else if (byte1 == -4)
manitou 3:d12c34704b6d 596 play(3, 27); // make a 'sh' sound
manitou 0:bcd16e4a0207 597
manitou 3:d12c34704b6d 598 } while (--duration);
manitou 0:bcd16e4a0207 599
manitou 3:d12c34704b6d 600 // Scan ahead to find a '.' or a '?' as this will change the pitch
manitou 3:d12c34704b6d 601 punctuationPitchDelta = 0;
manitou 3:d12c34704b6d 602 for (i = 6; i > 0; i--) {
manitou 3:d12c34704b6d 603 char next = phonemes[phonemeIn + (i * 2)];
manitou 3:d12c34704b6d 604 if (next == 'i')
manitou 3:d12c34704b6d 605 // found a full stop
manitou 3:d12c34704b6d 606 punctuationPitchDelta = 6 - i; // Lower the pitch
manitou 3:d12c34704b6d 607 else if (next == 'h')
manitou 3:d12c34704b6d 608 // found a question mark
manitou 3:d12c34704b6d 609 punctuationPitchDelta = i - 6; // Raise the pitch
manitou 3:d12c34704b6d 610 }
manitou 0:bcd16e4a0207 611
manitou 3:d12c34704b6d 612 if (byte1 == 1)
manitou 3:d12c34704b6d 613 delay2(25);
manitou 3:d12c34704b6d 614 } // next phoneme
manitou 0:bcd16e4a0207 615 }
manitou 0:bcd16e4a0207 616 soundOff();
manitou 0:bcd16e4a0207 617 }
manitou 0:bcd16e4a0207 618
manitou 0:bcd16e4a0207 619 /*
manitou 0:bcd16e4a0207 620 * Speak an English command line of text
manitou 0:bcd16e4a0207 621 */
manitou 0:bcd16e4a0207 622 void TTS::sayText(const char *original)
manitou 0:bcd16e4a0207 623 {
manitou 0:bcd16e4a0207 624 unsigned int i;
manitou 3:d12c34704b6d 625 if (textToPhonemes(original, s_vocab, g_text)) {
manitou 3:d12c34704b6d 626 sayPhonemes(g_text);
manitou 0:bcd16e4a0207 627 }
manitou 0:bcd16e4a0207 628 }