tom dunigan
/
tts
text-to-speech through DAC to audio amp/speaker
text-to-speech TTS
- This program was based on modifying the Arduino/Teensy TTS library https://github.com/manitou48/TTS
- The audio is generated with the ARM's DAC pin.
- TTS.h selects DAC pin based on MBED board. Only tested on K64F
- Teensy discussions https://forum.pjrc.com/threads/44587-TTS-(Text-to-Speech)-Library-Port
TTS.cpp@3:d12c34704b6d, 2017-06-24 (annotated)
- 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?
User | Revision | Line number | New 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 | } |