
The final project of Embedde class.
Dependencies: C12832 LM75B ESP-call MMA7660
Talkie/Talkie.cpp@2:37756b51ccdb, 2021-06-03 (annotated)
- Committer:
- pkr7098
- Date:
- Thu Jun 03 07:09:55 2021 +0000
- Revision:
- 2:37756b51ccdb
- Parent:
- 1:ed1c6618f739
The final project of Embedde class.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
pkr7098 | 1:ed1c6618f739 | 1 | // Talkie library |
pkr7098 | 1:ed1c6618f739 | 2 | // Copyright 2011 Peter Knight |
pkr7098 | 1:ed1c6618f739 | 3 | // This code is released under GPLv2 license. |
pkr7098 | 1:ed1c6618f739 | 4 | |
pkr7098 | 1:ed1c6618f739 | 5 | #include "Talkie.h" |
pkr7098 | 1:ed1c6618f739 | 6 | |
pkr7098 | 1:ed1c6618f739 | 7 | Timeout timeout; |
pkr7098 | 1:ed1c6618f739 | 8 | PwmOut speaker(D6); |
pkr7098 | 1:ed1c6618f739 | 9 | |
pkr7098 | 1:ed1c6618f739 | 10 | #define FS 8000 // Speech engine sample rate |
pkr7098 | 1:ed1c6618f739 | 11 | |
pkr7098 | 1:ed1c6618f739 | 12 | uint8_t synthPeriod; |
pkr7098 | 1:ed1c6618f739 | 13 | uint16_t synthEnergy; |
pkr7098 | 1:ed1c6618f739 | 14 | int16_t synthK1,synthK2; |
pkr7098 | 1:ed1c6618f739 | 15 | int8_t synthK3,synthK4,synthK5,synthK6,synthK7,synthK8,synthK9,synthK10; |
pkr7098 | 1:ed1c6618f739 | 16 | |
pkr7098 | 1:ed1c6618f739 | 17 | uint8_t tmsEnergy[0x10] = {0x00,0x02,0x03,0x04,0x05,0x07,0x0a,0x0f,0x14,0x20,0x29,0x39,0x51,0x72,0xa1,0xff}; |
pkr7098 | 1:ed1c6618f739 | 18 | uint8_t tmsPeriod[0x40] = {0x00,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2D,0x2F,0x31,0x33,0x35,0x36,0x39,0x3B,0x3D,0x3F,0x42,0x45,0x47,0x49,0x4D,0x4F,0x51,0x55,0x57,0x5C,0x5F,0x63,0x66,0x6A,0x6E,0x73,0x77,0x7B,0x80,0x85,0x8A,0x8F,0x95,0x9A,0xA0}; |
pkr7098 | 1:ed1c6618f739 | 19 | int16_t tmsK1[0x20] = {-32064, -31872, -31808, -31680, -31552, -31424, -31232, -30848, -30592, -30336, -30016, -29696, -29376, -28928, -28480, -27968, -26368, -24256, -21632, -18368, -14528, -10048, -5184, 0, 5184, 10048, 14528, 18368, 21632, 24256, 26368, 27968}; |
pkr7098 | 1:ed1c6618f739 | 20 | int16_t tmsK2[0x20] = {-20992, -19328, -17536, -15552, -13440, -11200, -8768, -6272, -3712, -1088, 1536, 4160, 6720, 9216, 11584, 13824, 15936, 17856, 19648, 21248, 22656, 24000, 25152, 26176, 27072, 27840, 28544, 29120, 29632, 30080, 30464, 32384}; |
pkr7098 | 1:ed1c6618f739 | 21 | int8_t tmsK3[0x10] = {-110, -97, -83, -70, -56, -43, -29, -16, -2, 11, 25, 38, 52, 65, 79, 92}; |
pkr7098 | 1:ed1c6618f739 | 22 | int8_t tmsK4[0x10] = {-82, -68, -54, -40, -26, -12, 1, 15}; |
pkr7098 | 1:ed1c6618f739 | 23 | int8_t tmsK5[0x10] = {-82, -70, -59, -47, -35, -24, -12, -1, 11, 23, 34, 46, 57, 69, 81, 92}; |
pkr7098 | 1:ed1c6618f739 | 24 | int8_t tmsK6[0x10] = {-64, -53, -42, -31, -20, -9, 3, 14, 25, 36, 47, 58, 69, 80, 91, 102}; |
pkr7098 | 1:ed1c6618f739 | 25 | int8_t tmsK7[0x10] = {-77, -65, -53, -41, -29, -17, -5, 7, 19, 31, 43, 55, 67, 79, 90, 102}; |
pkr7098 | 1:ed1c6618f739 | 26 | int8_t tmsK8[0x08] = {-64, -40, -16, 7, 31, 55, 79, 102}; |
pkr7098 | 1:ed1c6618f739 | 27 | int8_t tmsK9[0x08] = {-64, -44, -24, -4, 16, 37, 57, 77}; |
pkr7098 | 1:ed1c6618f739 | 28 | int8_t tmsK10[0x08] = {-51, -33, -15, 4, 22, 32, 59, 77}; |
pkr7098 | 1:ed1c6618f739 | 29 | |
pkr7098 | 1:ed1c6618f739 | 30 | void TIMEOUT(void); |
pkr7098 | 1:ed1c6618f739 | 31 | |
pkr7098 | 1:ed1c6618f739 | 32 | void Talkie::setPtr(uint8_t* addr) |
pkr7098 | 1:ed1c6618f739 | 33 | { |
pkr7098 | 1:ed1c6618f739 | 34 | ptrAddr = addr; |
pkr7098 | 1:ed1c6618f739 | 35 | ptrBit = 0; |
pkr7098 | 1:ed1c6618f739 | 36 | } |
pkr7098 | 1:ed1c6618f739 | 37 | |
pkr7098 | 1:ed1c6618f739 | 38 | // The ROMs used with the TI speech were serial, not byte wide. |
pkr7098 | 1:ed1c6618f739 | 39 | // Here's a handy routine to flip ROM data which is usually reversed. |
pkr7098 | 1:ed1c6618f739 | 40 | uint8_t Talkie::rev(uint8_t a) |
pkr7098 | 1:ed1c6618f739 | 41 | { |
pkr7098 | 1:ed1c6618f739 | 42 | // 76543210 |
pkr7098 | 1:ed1c6618f739 | 43 | a = (a>>4) | (a<<4); // Swap in groups of 4 |
pkr7098 | 1:ed1c6618f739 | 44 | // 32107654 |
pkr7098 | 1:ed1c6618f739 | 45 | a = ((a & 0xcc)>>2) | ((a & 0x33)<<2); // Swap in groups of 2 |
pkr7098 | 1:ed1c6618f739 | 46 | // 10325476 |
pkr7098 | 1:ed1c6618f739 | 47 | a = ((a & 0xaa)>>1) | ((a & 0x55)<<1); // Swap bit pairs |
pkr7098 | 1:ed1c6618f739 | 48 | // 01234567 |
pkr7098 | 1:ed1c6618f739 | 49 | return a; |
pkr7098 | 1:ed1c6618f739 | 50 | } |
pkr7098 | 1:ed1c6618f739 | 51 | uint8_t Talkie::getBits(uint8_t bits) |
pkr7098 | 1:ed1c6618f739 | 52 | { |
pkr7098 | 1:ed1c6618f739 | 53 | uint8_t value; |
pkr7098 | 1:ed1c6618f739 | 54 | uint16_t data; |
pkr7098 | 1:ed1c6618f739 | 55 | data = rev((*ptrAddr))<<8; |
pkr7098 | 1:ed1c6618f739 | 56 | if (ptrBit+bits > 8) { |
pkr7098 | 1:ed1c6618f739 | 57 | data |= rev((*(ptrAddr+1))); |
pkr7098 | 1:ed1c6618f739 | 58 | } |
pkr7098 | 1:ed1c6618f739 | 59 | data <<= ptrBit; |
pkr7098 | 1:ed1c6618f739 | 60 | value = data >> (16-bits); |
pkr7098 | 1:ed1c6618f739 | 61 | ptrBit += bits; |
pkr7098 | 1:ed1c6618f739 | 62 | if (ptrBit >= 8) { |
pkr7098 | 1:ed1c6618f739 | 63 | ptrBit -= 8; |
pkr7098 | 1:ed1c6618f739 | 64 | ptrAddr++; |
pkr7098 | 1:ed1c6618f739 | 65 | } |
pkr7098 | 1:ed1c6618f739 | 66 | return value; |
pkr7098 | 1:ed1c6618f739 | 67 | } |
pkr7098 | 1:ed1c6618f739 | 68 | void Talkie::say(uint8_t* addr) |
pkr7098 | 1:ed1c6618f739 | 69 | { |
pkr7098 | 1:ed1c6618f739 | 70 | uint8_t energy; |
pkr7098 | 1:ed1c6618f739 | 71 | |
pkr7098 | 1:ed1c6618f739 | 72 | if (!setup) { |
pkr7098 | 1:ed1c6618f739 | 73 | // Auto-setup. |
pkr7098 | 1:ed1c6618f739 | 74 | // |
pkr7098 | 1:ed1c6618f739 | 75 | // Enable the speech system whenever say() is called. |
pkr7098 | 1:ed1c6618f739 | 76 | |
pkr7098 | 1:ed1c6618f739 | 77 | // Timer 2 set up as a 62500Hz PWM. |
pkr7098 | 1:ed1c6618f739 | 78 | // |
pkr7098 | 1:ed1c6618f739 | 79 | // The PWM 'buzz' is well above human hearing range and is |
pkr7098 | 1:ed1c6618f739 | 80 | // very easy to filter out. |
pkr7098 | 1:ed1c6618f739 | 81 | // |
pkr7098 | 1:ed1c6618f739 | 82 | speaker.period_us(16); |
pkr7098 | 1:ed1c6618f739 | 83 | |
pkr7098 | 1:ed1c6618f739 | 84 | // Unfortunately we can't calculate the next sample every PWM cycle |
pkr7098 | 1:ed1c6618f739 | 85 | // as the routine is too slow. So use Timer 1 to trigger that. |
pkr7098 | 1:ed1c6618f739 | 86 | |
pkr7098 | 1:ed1c6618f739 | 87 | // Timer 1 set up as a 8000Hz sample interrupt |
pkr7098 | 1:ed1c6618f739 | 88 | timeout.attach_us(TIMEOUT, 125); |
pkr7098 | 1:ed1c6618f739 | 89 | |
pkr7098 | 1:ed1c6618f739 | 90 | setup = 1; |
pkr7098 | 1:ed1c6618f739 | 91 | } |
pkr7098 | 1:ed1c6618f739 | 92 | |
pkr7098 | 1:ed1c6618f739 | 93 | setPtr(addr); |
pkr7098 | 1:ed1c6618f739 | 94 | do { |
pkr7098 | 1:ed1c6618f739 | 95 | uint8_t repeat; |
pkr7098 | 1:ed1c6618f739 | 96 | |
pkr7098 | 1:ed1c6618f739 | 97 | // Read speech data, processing the variable size frames. |
pkr7098 | 1:ed1c6618f739 | 98 | |
pkr7098 | 1:ed1c6618f739 | 99 | energy = getBits(4); |
pkr7098 | 1:ed1c6618f739 | 100 | if (energy == 0) { |
pkr7098 | 1:ed1c6618f739 | 101 | // Energy = 0: rest frame |
pkr7098 | 1:ed1c6618f739 | 102 | synthEnergy = 0; |
pkr7098 | 1:ed1c6618f739 | 103 | } else if (energy == 0xf) { |
pkr7098 | 1:ed1c6618f739 | 104 | // Energy = 15: stop frame. Silence the synthesiser. |
pkr7098 | 1:ed1c6618f739 | 105 | synthEnergy = 0; |
pkr7098 | 1:ed1c6618f739 | 106 | synthK1 = 0; |
pkr7098 | 1:ed1c6618f739 | 107 | synthK2 = 0; |
pkr7098 | 1:ed1c6618f739 | 108 | synthK3 = 0; |
pkr7098 | 1:ed1c6618f739 | 109 | synthK4 = 0; |
pkr7098 | 1:ed1c6618f739 | 110 | synthK5 = 0; |
pkr7098 | 1:ed1c6618f739 | 111 | synthK6 = 0; |
pkr7098 | 1:ed1c6618f739 | 112 | synthK7 = 0; |
pkr7098 | 1:ed1c6618f739 | 113 | synthK8 = 0; |
pkr7098 | 1:ed1c6618f739 | 114 | synthK9 = 0; |
pkr7098 | 1:ed1c6618f739 | 115 | synthK10 = 0; |
pkr7098 | 1:ed1c6618f739 | 116 | } else { |
pkr7098 | 1:ed1c6618f739 | 117 | synthEnergy = tmsEnergy[energy]; |
pkr7098 | 1:ed1c6618f739 | 118 | repeat = getBits(1); |
pkr7098 | 1:ed1c6618f739 | 119 | synthPeriod = tmsPeriod[getBits(6)]; |
pkr7098 | 1:ed1c6618f739 | 120 | // A repeat frame uses the last coefficients |
pkr7098 | 1:ed1c6618f739 | 121 | if (!repeat) { |
pkr7098 | 1:ed1c6618f739 | 122 | // All frames use the first 4 coefficients |
pkr7098 | 1:ed1c6618f739 | 123 | synthK1 = tmsK1[getBits(5)]; |
pkr7098 | 1:ed1c6618f739 | 124 | synthK2 = tmsK2[getBits(5)]; |
pkr7098 | 1:ed1c6618f739 | 125 | synthK3 = tmsK3[getBits(4)]; |
pkr7098 | 1:ed1c6618f739 | 126 | synthK4 = tmsK4[getBits(4)]; |
pkr7098 | 1:ed1c6618f739 | 127 | if (synthPeriod) { |
pkr7098 | 1:ed1c6618f739 | 128 | // Voiced frames use 6 extra coefficients. |
pkr7098 | 1:ed1c6618f739 | 129 | synthK5 = tmsK5[getBits(4)]; |
pkr7098 | 1:ed1c6618f739 | 130 | synthK6 = tmsK6[getBits(4)]; |
pkr7098 | 1:ed1c6618f739 | 131 | synthK7 = tmsK7[getBits(4)]; |
pkr7098 | 1:ed1c6618f739 | 132 | synthK8 = tmsK8[getBits(3)]; |
pkr7098 | 1:ed1c6618f739 | 133 | synthK9 = tmsK9[getBits(3)]; |
pkr7098 | 1:ed1c6618f739 | 134 | synthK10 = tmsK10[getBits(3)]; |
pkr7098 | 1:ed1c6618f739 | 135 | } |
pkr7098 | 1:ed1c6618f739 | 136 | } |
pkr7098 | 1:ed1c6618f739 | 137 | } |
pkr7098 | 1:ed1c6618f739 | 138 | thread_sleep_for(25); |
pkr7098 | 1:ed1c6618f739 | 139 | } while (energy != 0xf); |
pkr7098 | 1:ed1c6618f739 | 140 | } |
pkr7098 | 1:ed1c6618f739 | 141 | |
pkr7098 | 1:ed1c6618f739 | 142 | #define CHIRP_SIZE 41 |
pkr7098 | 1:ed1c6618f739 | 143 | int8_t chirp[CHIRP_SIZE] = {0, 42, -44, 50, -78, 18, 37, 20, 2, -31, -59, 2, 95, 90, 5, 15, 38, -4, -91, -91, -42, -35, -36, -4, 37, 43, 34, 33, 15, -1, -8, -18, -19, -17, -9, -10, -6, 0, 3, 2, 1}; |
pkr7098 | 1:ed1c6618f739 | 144 | void TIMEOUT(void) |
pkr7098 | 1:ed1c6618f739 | 145 | { |
pkr7098 | 1:ed1c6618f739 | 146 | static uint8_t nextPwm; |
pkr7098 | 1:ed1c6618f739 | 147 | static uint8_t periodCounter; |
pkr7098 | 1:ed1c6618f739 | 148 | static int16_t x0,x1,x2,x3,x4,x5,x6,x7,x8,x9,x10; |
pkr7098 | 1:ed1c6618f739 | 149 | int16_t u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10; |
pkr7098 | 1:ed1c6618f739 | 150 | |
pkr7098 | 1:ed1c6618f739 | 151 | speaker = float(nextPwm) * 0.392; |
pkr7098 | 1:ed1c6618f739 | 152 | // sei(); |
pkr7098 | 1:ed1c6618f739 | 153 | if (synthPeriod) { |
pkr7098 | 1:ed1c6618f739 | 154 | // Voiced source |
pkr7098 | 1:ed1c6618f739 | 155 | if (periodCounter < synthPeriod) { |
pkr7098 | 1:ed1c6618f739 | 156 | periodCounter++; |
pkr7098 | 1:ed1c6618f739 | 157 | } else { |
pkr7098 | 1:ed1c6618f739 | 158 | periodCounter = 0; |
pkr7098 | 1:ed1c6618f739 | 159 | } |
pkr7098 | 1:ed1c6618f739 | 160 | if (periodCounter < CHIRP_SIZE) { |
pkr7098 | 1:ed1c6618f739 | 161 | u10 = ((chirp[periodCounter]) * (uint32_t) synthEnergy) >> 8; |
pkr7098 | 1:ed1c6618f739 | 162 | } else { |
pkr7098 | 1:ed1c6618f739 | 163 | u10 = 0; |
pkr7098 | 1:ed1c6618f739 | 164 | } |
pkr7098 | 1:ed1c6618f739 | 165 | } else { |
pkr7098 | 1:ed1c6618f739 | 166 | // Unvoiced source |
pkr7098 | 1:ed1c6618f739 | 167 | static uint16_t synthRand = 1; |
pkr7098 | 1:ed1c6618f739 | 168 | synthRand = (synthRand >> 1) ^ ((synthRand & 1) ? 0xB800 : 0); |
pkr7098 | 1:ed1c6618f739 | 169 | u10 = (synthRand & 1) ? synthEnergy : -synthEnergy; |
pkr7098 | 1:ed1c6618f739 | 170 | } |
pkr7098 | 1:ed1c6618f739 | 171 | // Lattice filter forward path |
pkr7098 | 1:ed1c6618f739 | 172 | u9 = u10 - (((int16_t)synthK10*x9) >> 7); |
pkr7098 | 1:ed1c6618f739 | 173 | u8 = u9 - (((int16_t)synthK9*x8) >> 7); |
pkr7098 | 1:ed1c6618f739 | 174 | u7 = u8 - (((int16_t)synthK8*x7) >> 7); |
pkr7098 | 1:ed1c6618f739 | 175 | u6 = u7 - (((int16_t)synthK7*x6) >> 7); |
pkr7098 | 1:ed1c6618f739 | 176 | u5 = u6 - (((int16_t)synthK6*x5) >> 7); |
pkr7098 | 1:ed1c6618f739 | 177 | u4 = u5 - (((int16_t)synthK5*x4) >> 7); |
pkr7098 | 1:ed1c6618f739 | 178 | u3 = u4 - (((int16_t)synthK4*x3) >> 7); |
pkr7098 | 1:ed1c6618f739 | 179 | u2 = u3 - (((int16_t)synthK3*x2) >> 7); |
pkr7098 | 1:ed1c6618f739 | 180 | u1 = u2 - (((int32_t)synthK2*x1) >> 15); |
pkr7098 | 1:ed1c6618f739 | 181 | u0 = u1 - (((int32_t)synthK1*x0) >> 15); |
pkr7098 | 1:ed1c6618f739 | 182 | |
pkr7098 | 1:ed1c6618f739 | 183 | // Output clamp |
pkr7098 | 1:ed1c6618f739 | 184 | if (u0 > 511) u0 = 511; |
pkr7098 | 1:ed1c6618f739 | 185 | if (u0 < -512) u0 = -512; |
pkr7098 | 1:ed1c6618f739 | 186 | |
pkr7098 | 1:ed1c6618f739 | 187 | // Lattice filter reverse path |
pkr7098 | 1:ed1c6618f739 | 188 | x9 = x8 + (((int16_t)synthK9*u8) >> 7); |
pkr7098 | 1:ed1c6618f739 | 189 | x8 = x7 + (((int16_t)synthK8*u7) >> 7); |
pkr7098 | 1:ed1c6618f739 | 190 | x7 = x6 + (((int16_t)synthK7*u6) >> 7); |
pkr7098 | 1:ed1c6618f739 | 191 | x6 = x5 + (((int16_t)synthK6*u5) >> 7); |
pkr7098 | 1:ed1c6618f739 | 192 | x5 = x4 + (((int16_t)synthK5*u4) >> 7); |
pkr7098 | 1:ed1c6618f739 | 193 | x4 = x3 + (((int16_t)synthK4*u3) >> 7); |
pkr7098 | 1:ed1c6618f739 | 194 | x3 = x2 + (((int16_t)synthK3*u2) >> 7); |
pkr7098 | 1:ed1c6618f739 | 195 | x2 = x1 + (((int32_t)synthK2*u1) >> 15); |
pkr7098 | 1:ed1c6618f739 | 196 | x1 = x0 + (((int32_t)synthK1*u0) >> 15); |
pkr7098 | 1:ed1c6618f739 | 197 | x0 = u0; |
pkr7098 | 1:ed1c6618f739 | 198 | |
pkr7098 | 1:ed1c6618f739 | 199 | nextPwm = (u0>>2)+0x80; |
pkr7098 | 1:ed1c6618f739 | 200 | } |