Talkie
Talkie.h
1 // Talkie library
2 // Copyright 2011 Peter Knight
3 // This code is released under GPLv2 license.
4 
5 #pragma once
6 
7 #include <inttypes.h>
8 #ifdef ARDUINO
9 #include "Print.h"
10 #endif
11 #include "Vocab_Special.h"
12 #include "Vocab_US_Large.h"
13 
14 #define CHIRP_SIZE 41
15 #define FS 8000 // Speech engine sample rate
16 #define LENGT_OF_FLOAT_STRING 14
17 
25 class Talkie {
26  public:
27  Talkie() = default;
28 #ifdef ARDUINO
29  Talkie(Print& out, int channelCount = 1) {
30  setOutput(out);
31  channels = channelCount;
32  }
33 #endif
35  void setOutputAsText(bool flag) { isOutputText = flag; }
36 
38  void say(const uint8_t* address) {
39  say1(address);
40  wait();
41  }
42 
44  void silence(uint16_t ms) {
45  int samples = 8000 * ms / 1000;
46  for (int j = 0; j < samples; j++) {
47  writeSample(0);
48  }
49  }
50 
51  void sayPause() { say(spPAUSE1); }
52 
53  void sayDigit(char aDigit) { return sayNumber(aDigit - '0'); }
54 
55  void sayVoltageMilliVolts(long aMilliVolt) {
56  sayNumber(aMilliVolt);
57  say(sp2_MILLI);
58  say(sp2_VOLTS);
59  }
60 
61  void sayVoltageVolts(float aVolt) {
62  sayFloat(aVolt, 2, true, true);
63  say(sp2_VOLTS);
64  }
65 
66  void sayTimeout() {
67  say(sp2_TIME);
68  say(sp2_OUT);
69  }
70 
72  void sayNumber(long aNumber) {
73  if (aNumber < 0) {
74  say(sp2_MINUS);
75  sayNumber(-aNumber);
76  } else if (aNumber == 0) {
77  say(sp2_ZERO);
78  } else {
79  if (aNumber >= 1000) {
80  int thousands = aNumber / 1000;
81  sayNumber(thousands);
82  say(sp2_THOUSAND);
83  aNumber %= 1000;
84  if ((aNumber > 0) && (aNumber < 100)) say(sp2_AND);
85  }
86  if (aNumber >= 100) {
87  int hundreds = aNumber / 100;
88  sayNumber(hundreds);
89  say(sp2_HUNDRED);
90  aNumber %= 100;
91  if (aNumber > 0) say(sp2_AND);
92  }
93  if (aNumber > 19) {
94  int tens = aNumber / 10;
95  switch (tens) {
96  case 2:
97  say(sp2_TWENTY);
98  break;
99  case 3:
100  say(sp2_THIR_);
101  say(sp2_T);
102  break;
103  case 4:
104  say(sp2_FOUR);
105  say(sp2_T);
106  break;
107  case 5:
108  say(sp2_FIF_);
109  say(sp2_T);
110  break;
111  case 6:
112  say(sp2_SIX);
113  say(sp2_T);
114  break;
115  case 7:
116  say(sp2_SEVEN);
117  say(sp2_T);
118  break;
119  case 8:
120  say(sp2_EIGHT);
121  say(sp2_T);
122  break;
123  case 9:
124  say(sp2_NINE);
125  say(sp2_T);
126  break;
127  }
128  aNumber %= 10;
129  }
130  switch (aNumber) {
131  case 1:
132  say(sp2_ONE);
133  break;
134  case 2:
135  say(sp2_TWO);
136  break;
137  case 3:
138  say(sp2_THREE);
139  break;
140  case 4:
141  say(sp2_FOUR);
142  break;
143  case 5:
144  say(sp2_FIVE);
145  break;
146  case 6:
147  say(sp2_SIX);
148  break;
149  case 7:
150  say(sp2_SEVEN);
151  break;
152  case 8:
153  say(sp2_EIGHT);
154  break;
155  case 9:
156  say(sp2_NINE);
157  break;
158  case 10:
159  say(sp2_TEN);
160  break;
161  case 11:
162  say(sp2_ELEVEN);
163  break;
164  case 12:
165  say(sp2_TWELVE);
166  break;
167  case 13:
168  say(sp2_THIR_);
169  say(sp2__TEEN);
170  break;
171  case 14:
172  say(sp2_FOUR);
173  say(sp2__TEEN);
174  break;
175  case 15:
176  say(sp2_FIF_);
177  say(sp2__TEEN);
178  break;
179  case 16:
180  say(sp2_SIX);
181  say(sp2__TEEN);
182  break;
183  case 17:
184  say(sp2_SEVEN);
185  say(sp2__TEEN);
186  break;
187  case 18:
188  say(sp2_EIGHT);
189  say(sp2__TEEN);
190  break;
191  case 19:
192  say(sp2_NINE);
193  say(sp2__TEEN);
194  break;
195  }
196  }
197  }
198 
199  void sayFloat(float aFloat, int aDecimalPlaces, bool aSuppressLeadingZero,
200  bool aSuppressTrailingZero) {
201  // First the integer part
202  long tIntegerPart = aFloat;
203  if (tIntegerPart != 0 || !aSuppressLeadingZero) {
204  sayNumber(tIntegerPart);
205  }
206  if (aDecimalPlaces > 0) {
207  // convert to string, this avoids rounding errors like 0.654 * 10 =
208  // 6,5399
209  char tFloatString[LENGT_OF_FLOAT_STRING];
210  dtostrf(aFloat, 8, aDecimalPlaces, tFloatString);
211  int i;
212  // find decimal point in string
213  for (i = 0; i < (LENGT_OF_FLOAT_STRING - 1); ++i) {
214  if (tFloatString[i] == '.') {
215  i++;
216  break;
217  }
218  }
219  // output decimal places digits if available
220  if (i < LENGT_OF_FLOAT_STRING - 2) {
221  say(sp2_POINT);
222  for (int j = 0; j < aDecimalPlaces; ++j) {
223  // suppress zero at last position
224  if (!(tFloatString[i] == '0' && aSuppressTrailingZero &&
225  j == aDecimalPlaces - 1)) {
226  sayNumber(tFloatString[i] - '0');
227  // check for end of string
228  i++;
229  if (tFloatString[i] == '\0') {
230  break;
231  }
232  }
233  }
234  }
235  }
236  }
237 
238 #ifdef ARDUINO
240  void setOutput(Print &out){
241  p_print = &out;
242  }
243 #endif
244 
246  void setDataCallback(void (*cb)(int16_t* data, int len)) {
247  data_callback = cb;
248  }
249 
251  void setChannels(uint16_t ch){
252  channels = ch;
253  }
254 
255  protected:
256 #ifdef ARDUINO
257  Print* p_print = nullptr;
258 #endif
259  uint16_t channels = 1;
260  bool isInit = false;
261  bool isOutputText = true;
262  const uint8_t* ptrAddr = nullptr;
263  uint8_t ptrBit;
264  uint8_t synthPeriod;
265  uint16_t synthEnergy;
266  int16_t synthK1, synthK2;
267  int8_t synthK3, synthK4, synthK5, synthK6, synthK7, synthK8, synthK9,
268  synthK10;
269  void (*data_callback)(int16_t* data, int len) = nullptr;
270 
271  uint8_t tmsEnergy[0x10] = {0x00, 0x02, 0x03, 0x04, 0x05, 0x07, 0x0a, 0x0f,
272  0x14, 0x20, 0x29, 0x39, 0x51, 0x72, 0xa1, 0xff};
273  uint8_t tmsPeriod[0x40] = {
274  0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
275  0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24,
276  0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2D, 0x2F, 0x31, 0x33,
277  0x35, 0x36, 0x39, 0x3B, 0x3D, 0x3F, 0x42, 0x45, 0x47, 0x49, 0x4D,
278  0x4F, 0x51, 0x55, 0x57, 0x5C, 0x5F, 0x63, 0x66, 0x6A, 0x6E, 0x73,
279  0x77, 0x7B, 0x80, 0x85, 0x8A, 0x8F, 0x95, 0x9A, 0xA0};
280  uint16_t tmsK1[0x20] = {
281  0x82C0, 0x8380, 0x83C0, 0x8440, 0x84C0, 0x8540, 0x8600, 0x8780,
282  0x8880, 0x8980, 0x8AC0, 0x8C00, 0x8D40, 0x8F00, 0x90C0, 0x92C0,
283  0x9900, 0xA140, 0xAB80, 0xB840, 0xC740, 0xD8C0, 0xEBC0, 0x0000,
284  0x1440, 0x2740, 0x38C0, 0x47C0, 0x5480, 0x5EC0, 0x6700, 0x6D40};
285  uint16_t tmsK2[0x20] = {
286  0xAE00, 0xB480, 0xBB80, 0xC340, 0xCB80, 0xD440, 0xDDC0, 0xE780,
287  0xF180, 0xFBC0, 0x0600, 0x1040, 0x1A40, 0x2400, 0x2D40, 0x3600,
288  0x3E40, 0x45C0, 0x4CC0, 0x5300, 0x5880, 0x5DC0, 0x6240, 0x6640,
289  0x69C0, 0x6CC0, 0x6F80, 0x71C0, 0x73C0, 0x7580, 0x7700, 0x7E80};
290  uint8_t tmsK3[0x10] = {0x92, 0x9F, 0xAD, 0xBA, 0xC8, 0xD5, 0xE3, 0xF0,
291  0xFE, 0x0B, 0x19, 0x26, 0x34, 0x41, 0x4F, 0x5C};
292  uint8_t tmsK4[0x10] = {0xAE, 0xBC, 0xCA, 0xD8, 0xE6, 0xF4, 0x01, 0x0F,
293  0x1D, 0x2B, 0x39, 0x47, 0x55, 0x63, 0x71, 0x7E};
294  uint8_t tmsK5[0x10] = {0xAE, 0xBA, 0xC5, 0xD1, 0xDD, 0xE8, 0xF4, 0xFF,
295  0x0B, 0x17, 0x22, 0x2E, 0x39, 0x45, 0x51, 0x5C};
296  uint8_t tmsK6[0x10] = {0xC0, 0xCB, 0xD6, 0xE1, 0xEC, 0xF7, 0x03, 0x0E,
297  0x19, 0x24, 0x2F, 0x3A, 0x45, 0x50, 0x5B, 0x66};
298  uint8_t tmsK7[0x10] = {0xB3, 0xBF, 0xCB, 0xD7, 0xE3, 0xEF, 0xFB, 0x07,
299  0x13, 0x1F, 0x2B, 0x37, 0x43, 0x4F, 0x5A, 0x66};
300  uint8_t tmsK8[0x08] = {0xC0, 0xD8, 0xF0, 0x07, 0x1F, 0x37, 0x4F, 0x66};
301  uint8_t tmsK9[0x08] = {0xC0, 0xD4, 0xE8, 0xFC, 0x10, 0x25, 0x39, 0x4D};
302  uint8_t tmsK10[0x08] = {0xCD, 0xDF, 0xF1, 0x04, 0x16, 0x20, 0x3B, 0x4D};
303  uint8_t chirp[CHIRP_SIZE] = {
304  0x00, 0x2a, 0xd4, 0x32, 0xb2, 0x12, 0x25, 0x14, 0x02, 0xe1, 0xc5,
305  0x02, 0x5f, 0x5a, 0x05, 0x0f, 0x26, 0xfc, 0xa5, 0xa5, 0xd6, 0xdd,
306  0xdc, 0xfc, 0x25, 0x2b, 0x22, 0x21, 0x0f, 0xff, 0xf8, 0xee, 0xed,
307  0xef, 0xf7, 0xf6, 0xfa, 0x00, 0x03, 0x02, 0x01};
308  int16_t nextSample = 0;
309  uint8_t periodCounter = 0;
310  int16_t x[10] = {0};
311 
312  void wait() { while (process()); }
313 
314  void setPtr(const uint8_t* addr) {
315  ptrAddr = addr;
316  ptrBit = 0;
317  }
318 
319  // The ROMs used with the TI speech were serial, not byte wide.
320  // Here's a handy routine to flip ROM data which is usually reversed.
321  uint8_t rev(uint8_t a) {
322  // 76543210
323  a = (a >> 4) | (a << 4); // Swap in groups of 4
324  // 32107654
325  a = ((a & 0xcc) >> 2) | ((a & 0x33) << 2); // Swap in groups of 2
326  // 10325476
327  a = ((a & 0xaa) >> 1) | ((a & 0x55) << 1); // Swap bit pairs
328  // 01234567
329  return a;
330  }
331 
332  uint8_t getBits(uint8_t bits) {
333  uint8_t value;
334  uint16_t data;
335  data = rev(*(ptrAddr)) << 8;
336  if (ptrBit + bits > 8) {
337  data |= rev(*(ptrAddr + 1));
338  }
339  data <<= ptrBit;
340  value = data >> (16 - bits);
341  ptrBit += bits;
342  if (ptrBit >= 8) {
343  ptrBit -= 8;
344  ptrAddr++;
345  }
346  return value;
347  }
348 
349  void say1(const uint8_t* addr) {
350  uint8_t energy = 0;
351  if (!isInit) {
352  for (int j = 0; j < 100; j++) wait();
353  isInit = true;
354  }
355 
356  setPtr(addr);
357  }
358 
359  int clip(int value, int min, int max) {
360  if (value < min) return min;
361  if (value > max) return max;
362  return value;
363  }
364 
365  void writeSample(int16_t sample) {
366  // 12 bit to 16 bits;
367  int16_t outSample = clip(static_cast<int>(sample), -32768, 32767);
368 
369  // provide data via callback
370  if (data_callback){
371  int16_t out[channels] = {outSample};
372  data_callback(out, channels);
373  }
374 
375 #ifdef ARDUINO
376  // provide data to Arduino Print
377  if (p_print) {;
378  if (isOutputText) {
379  for (int j = 0; j < channels; j++) {
380  if (j > 0) p_print->print(", ");
381  p_print->print(outSample);
382  }
383  p_print->println();
384  } else {
385  int16_t out[channels] = {outSample};
386  p_print->write((uint8_t*)out, sizeof(out));
387  }
388  }
389 #endif
390  }
391 
392  bool process() {
393  int16_t u[11] = {0};
394 
395  bool isActive = calculateSample();
396 
397  writeSample(nextSample);
398 
399  if (synthPeriod) {
400  // Voiced source
401  if (periodCounter < synthPeriod) {
402  periodCounter++;
403  } else {
404  periodCounter = 0;
405  }
406  if (periodCounter < CHIRP_SIZE) {
407  u[10] = ((chirp[periodCounter]) * (uint32_t)synthEnergy) >> 8;
408  } else {
409  u[10] = 0;
410  }
411  } else {
412  // Unvoiced source
413  static uint16_t synthRand = 1;
414  synthRand = (synthRand >> 1) ^ ((synthRand & 1) ? 0xB800 : 0);
415  u[10] = (synthRand & 1) ? synthEnergy : -synthEnergy;
416  }
417  // Lattice filter forward path
418  u[9] = u[10] - (((int16_t)synthK10 * x[9]) >> 7);
419  u[8] = u[9] - (((int16_t)synthK9 * x[8]) >> 7);
420  u[7] = u[8] - (((int16_t)synthK8 * x[7]) >> 7);
421  u[6] = u[7] - (((int16_t)synthK7 * x[6]) >> 7);
422  u[5] = u[6] - (((int16_t)synthK6 * x[5]) >> 7);
423  u[4] = u[5] - (((int16_t)synthK5 * x[4]) >> 7);
424  u[3] = u[4] - (((int16_t)synthK4 * x[3]) >> 7);
425  u[2] = u[3] - (((int16_t)synthK3 * x[2]) >> 7);
426  u[1] = u[2] - (((int32_t)synthK2 * x[1]) >> 15);
427  u[0] = u[1] - (((int32_t)synthK1 * x[0]) >> 15);
428 
429  // Output clamp
430  u[0] = clip(u[0], -512, 511);
431 
432  // Lattice filter reverse path
433  x[9] = x[8] + (((int16_t)synthK9 * u[8]) >> 7);
434  x[8] = x[7] + (((int16_t)synthK8 * u[7]) >> 7);
435  x[7] = x[6] + (((int16_t)synthK7 * u[6]) >> 7);
436  x[6] = x[5] + (((int16_t)synthK6 * u[5]) >> 7);
437  x[5] = x[4] + (((int16_t)synthK5 * u[4]) >> 7);
438  x[4] = x[3] + (((int16_t)synthK4 * u[3]) >> 7);
439  x[3] = x[2] + (((int16_t)synthK3 * u[2]) >> 7);
440  x[2] = x[1] + (((int32_t)synthK2 * u[1]) >> 15);
441  x[1] = x[0] + (((int32_t)synthK1 * u[0]) >> 15);
442  x[0] = u[0];
443 
444  // nextPwm = (u[0] >> 2) + 0x80;
445  nextSample = u[0];
446 
447  return isActive;
448  }
449 
450  bool calculateSample() {
451  uint8_t repeat;
452  uint8_t energy;
453 
454  // Read speech data, processing the variable size frames.
455  energy = getBits(4);
456  if (energy == 0) {
457  // Energy = 0: rest frame
458  synthEnergy = 0;
459  } else if (energy == 0xf) {
460  // Energy = 15: stop frame. Silence the synthesiser.
461  synthEnergy = 0;
462  synthK1 = 0;
463  synthK2 = 0;
464  synthK3 = 0;
465  synthK4 = 0;
466  synthK5 = 0;
467  synthK6 = 0;
468  synthK7 = 0;
469  synthK8 = 0;
470  synthK9 = 0;
471  synthK10 = 0;
472  } else {
473  synthEnergy = tmsEnergy[energy];
474  repeat = getBits(1);
475  synthPeriod = tmsPeriod[getBits(6)];
476  // A repeat frame uses the last coefficients
477  if (!repeat) {
478  // All frames use the first 4 coefficients
479  synthK1 = tmsK1[getBits(5)];
480  synthK2 = tmsK2[getBits(5)];
481  synthK3 = tmsK3[getBits(4)];
482  synthK4 = tmsK4[getBits(4)];
483  if (synthPeriod) {
484  // Voiced frames use 6 extra coefficients.
485  synthK5 = tmsK5[getBits(4)];
486  synthK6 = tmsK6[getBits(4)];
487  synthK7 = tmsK7[getBits(4)];
488  synthK8 = tmsK8[getBits(3)];
489  synthK9 = tmsK9[getBits(3)];
490  synthK10 = tmsK10[getBits(3)];
491  }
492  }
493  }
494  return energy != 0xf;
495  }
496 };
Talkie is a software implementation of the Texas Instruments speech synthesis architecture (Linear Pr...
Definition: Talkie.h:25
void setDataCallback(void(*cb)(int16_t *data, int len))
Defines the data callback that receives the generated samples.
Definition: Talkie.h:246
void say(const uint8_t *address)
converts the provided word into samples
Definition: Talkie.h:38
void setChannels(uint16_t ch)
Defines the number of generated output channels (2=stereo). Default is 1 = mono.
Definition: Talkie.h:251
void setOutput(Print &out)
Defines the Arduino data target.
Definition: Talkie.h:240
void setOutputAsText(bool flag)
converts samples to csv string format
Definition: Talkie.h:35
void sayNumber(long aNumber)
say any number between -999,999 and 999,999
Definition: Talkie.h:72
void silence(uint16_t ms)
outputs silence for the indicated milliseconds
Definition: Talkie.h:44