Program to record speech audio into RAM and then play it back, moving Billy Bass's mouth in sync with the speech.
main.cpp
00001 #include "mbed.h" 00002 #include "FastAnalogIn.h" 00003 #include "AudioAnalyzer.h" 00004 extern "C" { 00005 #include <math.h> 00006 } 00007 00008 using namespace NK; 00009 00010 // Power: 00011 // Power GND J9/14 00012 // Vin (6V) J9/16 00013 00014 // Digital: 00015 DigitalOut tail(PTA13); // J3/2 00016 DigitalOut mouth(PTC12); // J3/1 00017 DigitalOut head(PTC13); // J3/3 00018 DigitalIn pushbutton(PTD5); // J3/4 00019 00020 PwmOut redLED(LED_RED); 00021 PwmOut greenLED(LED_GREEN); 00022 PwmOut blueLED(LED_BLUE); 00023 00024 // Analog: 00025 // GND J3/14 00026 // VrefH J3/16 00027 FastAnalogIn microphone(PTB0); // J10/2 00028 AnalogOut speaker(PTE30); // J10/11 00029 00030 // Communications: 00031 // Serial uart1(PTC4, PTC3); 00032 Serial pc(USBTX, USBRX); 00033 00034 const unsigned SAMPLE_RATE_HZ = 7889; 00035 const unsigned SAMPLE_PERIOD_US = (1000000U / SAMPLE_RATE_HZ); 00036 const unsigned SAMPLE_BUFFER_SIZE = 14000; 00037 const unsigned CHUNK_DURATION_MS = 80; 00038 const unsigned CHUNK_SIZE = SAMPLE_RATE_HZ * CHUNK_DURATION_MS / 1000; 00039 const unsigned NUM_CHUNKS = SAMPLE_BUFFER_SIZE / CHUNK_SIZE; 00040 00041 Ticker sampleTicker; 00042 Timer timer; 00043 00044 // audio samples 00045 int8_t sampleBuffer[SAMPLE_BUFFER_SIZE]; // 1 second buffer 00046 int8_t * volatile nextSample; 00047 uint16_t volatile samplesRemaining; 00048 00049 // vowel decisions 00050 bool vowels[ NUM_CHUNKS ]; 00051 00052 extern "C" 00053 void ADC0_IRQHandler(void) 00054 { 00055 if (samplesRemaining) { 00056 *nextSample++ = microphone.read_s8_nowait(); 00057 microphone.start_read(); 00058 samplesRemaining--; 00059 } else { 00060 microphone.disable_interrupt(); 00061 microphone.abort_read(); 00062 timer.stop(); 00063 } 00064 } 00065 00066 void playAudioSample() 00067 { 00068 static uint16_t dcBias = 0x4000; 00069 if (samplesRemaining) { 00070 int8_t val = *nextSample++; 00071 uint16_t val16 = dcBias + (val * 256); 00072 speaker.write_u16(val16); 00073 samplesRemaining--; 00074 } else { 00075 sampleTicker.detach(); 00076 timer.stop(); 00077 } 00078 } 00079 00080 void resetSampleBuffer(int8_t *start=sampleBuffer, uint16_t nsamples=SAMPLE_BUFFER_SIZE) 00081 { 00082 nextSample = start; 00083 samplesRemaining = nsamples; 00084 } 00085 00086 void recordAudio() 00087 { 00088 pc.printf("Recording %d samples... ", SAMPLE_BUFFER_SIZE); 00089 blueLED = 0.0; 00090 00091 resetSampleBuffer(); 00092 timer.reset(); 00093 timer.start(); 00094 microphone.enable_interrupt(); 00095 microphone.start_read(); 00096 00097 while (samplesRemaining) { 00098 wait_us(50000); 00099 blueLED.write(1.0 - (1.0 * samplesRemaining / SAMPLE_BUFFER_SIZE)); 00100 } 00101 00102 microphone.abort_read(); 00103 00104 float elapsed = timer.read(); 00105 pc.printf("Done. %u samples in %f usec = %f samples/sec\r\n", SAMPLE_BUFFER_SIZE, elapsed * 1.0e6, SAMPLE_BUFFER_SIZE / elapsed); 00106 } 00107 00108 void playAudio(unsigned duration_ms, int8_t *start=sampleBuffer, uint16_t nsamples=SAMPLE_BUFFER_SIZE) 00109 { 00110 resetSampleBuffer(start, nsamples); 00111 timer.reset(); 00112 timer.start(); 00113 sampleTicker.attach_us(&playAudioSample, duration_ms*1000/nsamples); 00114 while (samplesRemaining) { 00115 wait_us(CHUNK_DURATION_MS * 1000); 00116 } 00117 speaker.write_u16(0); 00118 } 00119 00120 void audioTest() 00121 { 00122 double phase = 0.0; 00123 resetSampleBuffer(); 00124 for (int8_t *p = sampleBuffer; p < sampleBuffer + SAMPLE_BUFFER_SIZE; p++) { 00125 double s = sin(phase) * 125.0; 00126 phase += 2000 * 3.1416 / SAMPLE_BUFFER_SIZE; 00127 *p = static_cast<int8_t>(s); 00128 } 00129 } 00130 00131 // returns true if chunk was louder than minimum 00132 bool analyzeChunk(int8_t *chunkStart, uint16_t CHUNK_SIZE, float powerRef, bool *pisvowel = 0) 00133 { 00134 AudioAnalyzer analyzer(chunkStart, CHUNK_SIZE); 00135 uint32_t power = analyzer.getPower(); 00136 uint16_t zcs = analyzer.getZeroCrossings(); 00137 int8_t min, max; 00138 analyzer.getMinMaxValues(&min, &max); 00139 analyzer.setPowerRef(powerRef); 00140 float logPower = analyzer.getLogPower(); 00141 float zcRatio = analyzer.getZeroCrossingRatioPercent(); 00142 pc.printf("%.2f\t%.2f\t%.2f\t%d\t%d\t%d\t", zcRatio, logPower, zcRatio / (logPower - AudioAnalyzer::VowelXIntercept), min, max, analyzer.isVowel()); 00143 if (pisvowel) 00144 *pisvowel = analyzer.isVowel(); 00145 return analyzer.isVoiced(); 00146 } 00147 00148 void analyze(bool playToo = false) 00149 { 00150 int8_t *chunkStart = sampleBuffer; 00151 AudioAnalyzer analyzer(sampleBuffer, SAMPLE_BUFFER_SIZE); 00152 uint32_t power = analyzer.getPower(); 00153 float powerRef = ::log((double)power); 00154 pc.printf("Reference power = %.2f\r\n", powerRef); 00155 pc.printf("Analyzing %d chunks of %d samples (%.2f seconds):\r\n", NUM_CHUNKS, CHUNK_SIZE, CHUNK_DURATION_MS); 00156 pc.printf("chunk\tstartms\tzcratio\tlogp\tmaxs\tmin\tmax\tisVowel\tvowel\r\n"); 00157 for (uint16_t chunk = 0; chunk < NUM_CHUNKS; chunk++) { 00158 pc.printf("%u\t%u\t", chunk, chunk * CHUNK_DURATION_MS); 00159 bool loudEnough = analyzeChunk(chunkStart, CHUNK_SIZE, powerRef, &vowels[chunk]); 00160 if (loudEnough) { 00161 if (playToo) { 00162 while (! pc.readable()) 00163 playAudio(CHUNK_DURATION_MS, chunkStart, CHUNK_SIZE); 00164 int c = pc.getc(); 00165 pc.putc(c); 00166 } else 00167 pc.puts("-"); 00168 } 00169 pc.puts("\r\n"); 00170 chunkStart += CHUNK_SIZE; 00171 } 00172 } 00173 00174 // assumes that vowels[] has been set by analyze 00175 void playWithBilly() 00176 { 00177 int8_t *chunkStart = sampleBuffer; 00178 head = true; 00179 wait(0.2); 00180 for (uint16_t chunk = 0; chunk < NUM_CHUNKS; chunk++) { 00181 if (vowels[chunk]) { 00182 greenLED = 0.0; 00183 mouth = true; 00184 } else { 00185 greenLED = 1.0; 00186 mouth = false; 00187 } 00188 playAudio(CHUNK_DURATION_MS, chunkStart, CHUNK_SIZE); 00189 chunkStart += CHUNK_SIZE; 00190 } 00191 tail = true; 00192 wait(0.2); 00193 tail = false; 00194 wait(0.1); 00195 head = false; 00196 } 00197 00198 void dumpAudio(int8_t *start=sampleBuffer, uint16_t nsamples=SAMPLE_BUFFER_SIZE) 00199 { 00200 for (int8_t *p = start; p < sampleBuffer + nsamples; p++) { 00201 pc.printf("%d\r\n", *p); 00202 } 00203 } 00204 00205 int main() 00206 { 00207 pc.baud(115200); 00208 pc.printf("\r\n\r\nSample buffer = %u samples; rate = %u Hz; period = %u usec\r\n", SAMPLE_BUFFER_SIZE, SAMPLE_RATE_HZ, SAMPLE_PERIOD_US); 00209 pushbutton.mode(PullUp); 00210 00211 for (;;) { 00212 redLED = 1.0; 00213 greenLED = 1.0; 00214 blueLED = 1.0; 00215 00216 #if 0 00217 pc.puts("ENTER when ready:"); 00218 pc.getc(); 00219 pc.puts("\r\n"); 00220 #endif 00221 #if 0 00222 audioTest(); 00223 playAudio(1000); 00224 analyze(); 00225 #endif 00226 00227 while (pushbutton.read()) 00228 wait(0.1); 00229 00230 recordAudio(); 00231 float duration = timer.read(); 00232 // playAudio(duration * 1000); 00233 float elapsed = timer.read(); 00234 pc.printf("Done. %u samples in %f usec = %f samples/sec", SAMPLE_BUFFER_SIZE, elapsed * 1.0e6, SAMPLE_BUFFER_SIZE / elapsed); 00235 pc.printf(" (Rate %#+0.2f%%)\r\n", (duration-elapsed)*100/duration); 00236 analyze(false); 00237 // dumpAudio(); 00238 00239 playWithBilly(); 00240 } 00241 }
Generated on Wed Jul 13 2022 10:13:19 by 1.7.2