TalkiePCM
TalkiePCM.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 TalkiePCM {
26  public:
27  TalkiePCM() = default;
28 #ifdef ARDUINO
29  TalkiePCM(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  setPtr(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) { p_print = &out; }
241 #endif
242 
244  void setDataCallback(void (*cb)(int16_t* data, int len)) {
245  data_callback = cb;
246  }
247 
250  void setChannels(uint16_t ch) { channels = ch; }
251 
254  void setVolume(float vol) { volume = vol; }
255 
256  protected:
257 #ifdef ARDUINO
258  Print* p_print = nullptr;
259 #endif
260  uint16_t channels = 1;
261  bool isOutputText = false;
262  const uint8_t* ptrAddr = nullptr;
263  uint8_t ptrBit;
264  uint8_t synthPeriod;
265  int16_t synthK1, synthK2;
266  int8_t synthK3, synthK4, synthK5, synthK6, synthK7, synthK8, synthK9,
267  synthK10;
268  float volume = 1.0f;
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() { calculateSamples(); }
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  // prevent NPE
334  if (ptrAddr == nullptr) return 0;
335  uint8_t value;
336  uint16_t data;
337  data = rev(*(ptrAddr)) << 8;
338  if (ptrBit + bits > 8) {
339  data |= rev(*(ptrAddr + 1));
340  }
341  data <<= ptrBit;
342  value = data >> (16 - bits);
343  ptrBit += bits;
344  if (ptrBit >= 8) {
345  ptrBit -= 8;
346  ptrAddr++;
347  }
348  return value;
349  }
350 
351  int clip(int value, int min, int max) {
352  if (value < min) return min;
353  if (value > max) return max;
354  return value;
355  }
356 
357  void writeSample(int16_t sample) {
358  // scale to 16 bits;
359  int16_t outSample;
360  if (volume == 1.0f)
361  outSample = clip(static_cast<int>(sample) << 6, -32768, 32767);
362  else
363  outSample = clip(volume * (static_cast<int>(sample) << 6), -32768, 32767);
364 
365  // provide data via callback
366  if (data_callback) {
367  int16_t out[channels] = {0};
368  for (int j = 0; j < channels; j++) out[j] = outSample;
369  data_callback(out, channels);
370  }
371 
372 #ifdef ARDUINO
373  // provide data to Arduino Print
374  if (p_print) {
375  ;
376  if (isOutputText) {
377  for (int j = 0; j < channels; j++) {
378  if (j > 0) p_print->print(", ");
379  p_print->print(outSample);
380  }
381  p_print->println();
382  } else {
383  int16_t out[channels] = {0};
384  for (int j = 0; j < channels; j++) out[j] = outSample;
385  p_print->write((uint8_t*)&(out[0]), sizeof(out));
386  }
387  }
388 #endif
389  }
390 
401  void processEnergy(uint16_t synthEnergy) {
402  int16_t u[11] = {0};
403 
404  // original logic: update pwm
405  writeSample(nextSample);
406 
407  if (synthPeriod) {
408  // Voiced source
409  if (periodCounter < synthPeriod) {
410  periodCounter++;
411  } else {
412  periodCounter = 0;
413  }
414  if (periodCounter < CHIRP_SIZE) {
415  u[10] = ((chirp[periodCounter]) * (uint32_t)synthEnergy) >> 8;
416  } else {
417  u[10] = 0;
418  }
419  } else {
420  // Unvoiced source
421  static uint16_t synthRand = 1;
422  synthRand = (synthRand >> 1) ^ ((synthRand & 1) ? 0xB800 : 0);
423  u[10] = (synthRand & 1) ? synthEnergy : -synthEnergy;
424  }
425  // Lattice filter forward path
426  u[9] = u[10] - (((int16_t)synthK10 * x[9]) >> 7);
427  u[8] = u[9] - (((int16_t)synthK9 * x[8]) >> 7);
428  u[7] = u[8] - (((int16_t)synthK8 * x[7]) >> 7);
429  u[6] = u[7] - (((int16_t)synthK7 * x[6]) >> 7);
430  u[5] = u[6] - (((int16_t)synthK6 * x[5]) >> 7);
431  u[4] = u[5] - (((int16_t)synthK5 * x[4]) >> 7);
432  u[3] = u[4] - (((int16_t)synthK4 * x[3]) >> 7);
433  u[2] = u[3] - (((int16_t)synthK3 * x[2]) >> 7);
434  u[1] = u[2] - (((int32_t)synthK2 * x[1]) >> 15);
435  u[0] = u[1] - (((int32_t)synthK1 * x[0]) >> 15);
436 
437  // Output clamp
438  u[0] = clip(u[0], -512, 511);
439 
440  // Lattice filter reverse path
441  x[9] = x[8] + (((int16_t)synthK9 * u[8]) >> 7);
442  x[8] = x[7] + (((int16_t)synthK8 * u[7]) >> 7);
443  x[7] = x[6] + (((int16_t)synthK7 * u[6]) >> 7);
444  x[6] = x[5] + (((int16_t)synthK6 * u[5]) >> 7);
445  x[5] = x[4] + (((int16_t)synthK5 * u[4]) >> 7);
446  x[4] = x[3] + (((int16_t)synthK4 * u[3]) >> 7);
447  x[3] = x[2] + (((int16_t)synthK3 * u[2]) >> 7);
448  x[2] = x[1] + (((int32_t)synthK2 * u[1]) >> 15);
449  x[1] = x[0] + (((int32_t)synthK1 * u[0]) >> 15);
450  x[0] = u[0];
451 
452  // nextPwm = (u[0] >> 2) + 0x80;
453  nextSample = u[0];
454  }
455 
456  void calculateSamples() {
457  uint8_t energy = 0;
458  uint16_t synthEnergy = 0;
459 
460  do {
461  // Read speech data, processing the variable size frames.
462  energy = getBits(4);
463  if (energy == 0) {
464  // Energy = 0: rest frame
465  synthEnergy = 0;
466  } else if (energy == 0xf) {
467  // Energy = 15: stop frame. Silence the synthesiser.
468  synthEnergy = 0;
469  synthK1 = 0;
470  synthK2 = 0;
471  synthK3 = 0;
472  synthK4 = 0;
473  synthK5 = 0;
474  synthK6 = 0;
475  synthK7 = 0;
476  synthK8 = 0;
477  synthK9 = 0;
478  synthK10 = 0;
479  } else {
480  synthEnergy = tmsEnergy[energy];
481  bool repeat = getBits(1);
482  synthPeriod = tmsPeriod[getBits(6)];
483  // A repeat frame uses the last coefficients
484  if (!repeat) {
485  // All frames use the first 4 coefficients
486  synthK1 = tmsK1[getBits(5)];
487  synthK2 = tmsK2[getBits(5)];
488  synthK3 = tmsK3[getBits(4)];
489  synthK4 = tmsK4[getBits(4)];
490  if (synthPeriod) {
491  // Voiced frames use 6 extra coefficients.
492  synthK5 = tmsK5[getBits(4)];
493  synthK6 = tmsK6[getBits(4)];
494  synthK7 = tmsK7[getBits(4)];
495  synthK8 = tmsK8[getBits(3)];
496  synthK9 = tmsK9[getBits(3)];
497  synthK10 = tmsK10[getBits(3)];
498  }
499  }
500  }
501  // original logic: delay 25 ms (=40 hz) -> 8000 hz / 40 hz = 200 cycles
502  for (int j=0;j<200;j++)
503  processEnergy(synthEnergy);
504  } while (energy != 0xf);
505  }
506 };
Talkie is a software implementation of the Texas Instruments speech synthesis architecture (Linear Pr...
Definition: TalkiePCM.h:25
void setOutput(Print &out)
Defines the Arduino data target.
Definition: TalkiePCM.h:240
void silence(uint16_t ms)
outputs silence for the indicated milliseconds
Definition: TalkiePCM.h:44
void setDataCallback(void(*cb)(int16_t *data, int len))
Defines the data callback that receives the generated samples.
Definition: TalkiePCM.h:244
void setOutputAsText(bool flag)
converts samples to csv string format
Definition: TalkiePCM.h:35
void say(const uint8_t *address)
converts the provided word into samples
Definition: TalkiePCM.h:38
void setChannels(uint16_t ch)
Definition: TalkiePCM.h:250
void sayNumber(long aNumber)
say any number between -999,999 and 999,999
Definition: TalkiePCM.h:72
void setVolume(float vol)
Definition: TalkiePCM.h:254