Program to record speech audio into RAM and then play it back, moving Billy Bass's mouth in sync with the speech.

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

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 }