The Big Mouth Billy Bass program

Dependencies:   mbed

Revision:
0:ef6fc1737022
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Jan 17 16:53:37 2011 +0000
@@ -0,0 +1,373 @@
+
+#include "mbed.h"
+#include "SDFileSystem.h"
+
+#define SAMPLE_FREQ 40000
+#define BUF_SIZE (SAMPLE_FREQ/10)
+#define SLICE_BUF_SIZE 1
+
+// include this #define to enable lots of serial output
+//#define VERBOSE
+
+
+
+typedef struct uFMT_STRUCT {
+  short comp_code;
+  short num_channels;
+  unsigned sample_rate;
+  unsigned avg_Bps;
+  short block_align;
+  short sig_bps;
+} FMT_STRUCT;
+
+
+typedef struct uMOV_STRUCT {
+  long sample;
+  unsigned motor;
+  unsigned duty_cycle;
+  unsigned played;
+} MOV_STRUCT;
+
+// global MBED things
+AnalogOut DACout(p18);
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+DigitalOut digout(p8);
+DigitalIn pushbutton(p24);
+PwmOut body(p21);
+PwmOut mouth(p22);
+PwmOut tail(p23);
+Ticker tick;
+SDFileSystem sd(p5, p6, p7, p13, "sd");
+
+// global variables used both by the main program and the ISR
+short DAC_fifo[256];    // FIFO for the DAC
+short DAC_wptr;            // FIFO pointer
+volatile short DAC_rptr;            // FIFO pointer
+long slice;
+unsigned num_movements;
+unsigned current_movement;
+MOV_STRUCT movements[500];
+
+void dac_out(void);
+void play_wave(char *,char *,unsigned);
+void cleanup(char *);
+unsigned process_movement_file (char *mfname, MOV_STRUCT *mv,unsigned samp_rate);
+
+
+int main() {
+        FILE *cmdfile;
+        char cmdline[100];
+        char *movfile,*tmp;
+        char cmdfile_name[]="/sd/fish.txt";
+        unsigned slow_mode=0;
+  if (pushbutton) slow_mode=1;
+  led1=0; wait(.5); led1=1; wait(.5); led1=0;
+  printf("\nHello, world!\n");
+  if (slow_mode) printf("Slooooow mode enabled...\n");
+  printf("Waiting for button push\n");
+  while (!pushbutton);
+  printf("Button pushed\n");
+  while (1) {
+    cmdfile=fopen(cmdfile_name,"rb");
+    if (!cmdfile) {
+      printf("Unable to open command file '%s'\n",cmdfile_name);
+      exit(1);
+    }
+  
+    fgets(cmdline,100,cmdfile);
+    while (!feof(cmdfile)) {
+      printf("Parsing '%s' from command file '%s'\n",cmdline,cmdfile_name);
+      tmp=strchr(cmdline,'#');
+      if (tmp) *tmp=0;
+      movfile=strchr(cmdline,' ');
+      if (movfile) {
+        *movfile=0;
+        movfile++;
+        tmp=strchr(movfile,'\n');
+        cleanup(movfile);
+        if (tmp) *tmp=0;
+
+#ifdef USE_PUSHBUTTON
+        printf("Waiting for button push\n");
+        while (!pushbutton);
+        printf("Button pushed\n");
+#else
+        wait_ms(2000);
+#endif
+        play_wave(cmdline,movfile,slow_mode);
+        printf("Back from play_wave()\n");
+      } else {
+        printf("Unable to parse '%s' from command file '%s'\n",cmdline,cmdfile_name);
+      }
+      fgets(cmdline,100,cmdfile);
+    }
+    fclose(cmdfile);
+    printf("Goodbye, world!\n");
+  }
+}
+  
+void play_wave(char *wavname,char *movname,unsigned slow)
+{
+        unsigned chunk_id,chunk_size,channel;
+        unsigned data,samp_int,i;
+        short dac_data;
+        char *slice_buf;
+        short *data_sptr;
+        unsigned char *data_bptr;
+        int *data_wptr;
+        FMT_STRUCT wav_format;
+        FILE *wavfile;
+        long num_slices;
+        long long slice_value;
+        int verbosity=0;
+  DAC_wptr=0;
+  DAC_rptr=0;
+  for (i=0;i<256;i+=2) {
+    DAC_fifo[i]=0;
+    DAC_fifo[i+1]=3000;
+  }
+  DAC_wptr=4;
+
+  body.period_us(100);
+  mouth.period_us(100);
+  tail.period_us(100);
+  led1=led2=led3=led4=0;
+  
+  printf("Playing wave file '%s', mov file '%s'\n",wavname,movname);
+
+  wavfile=fopen(wavname,"rb");
+  if (!wavfile) {
+    printf("Unable to open wav file '%s'\n",wavname);
+    return;
+  }
+
+
+  fread(&chunk_id,4,1,wavfile);
+  fread(&chunk_size,4,1,wavfile);
+  while (!feof(wavfile)) {
+    printf("Read chunk ID 0x%x, size 0x%x\n",chunk_id,chunk_size);
+    switch (chunk_id) {
+      case 0x46464952:
+        fread(&data,4,1,wavfile);
+        printf("RIFF chunk\n");
+        printf("  chunk size %d (0x%x)\n",chunk_size,chunk_size);
+        printf("  RIFF type 0x%x\n",data);
+        break;
+      case 0x20746d66:
+        fread(&wav_format,sizeof(wav_format),1,wavfile);
+        printf("FORMAT chunk\n");
+        printf("  chunk size %d (0x%x)\n",chunk_size,chunk_size);
+        printf("  compression code %d\n",wav_format.comp_code);
+        printf("  %d channels\n",wav_format.num_channels);
+        printf("  %d samples/sec\n",wav_format.sample_rate);
+        printf("  %d bytes/sec\n",wav_format.avg_Bps);
+        printf("  block align %d\n",wav_format.block_align);
+        printf("  %d bits per sample\n",wav_format.sig_bps);
+        if (chunk_size > sizeof(wav_format))
+          fseek(wavfile,chunk_size-sizeof(wav_format),SEEK_CUR);
+// create a slice buffer large enough to hold multiple slices
+        slice_buf=(char *)malloc(wav_format.block_align*SLICE_BUF_SIZE);
+        if (!slice_buf) {
+          printf("Unable to malloc slice buffer");
+          exit(1);
+        }
+// now that the sample rate is known, process the movement file
+        num_movements=process_movement_file(movname,movements,wav_format.sample_rate);
+        break;
+
+      case 0x61746164:
+        slice_buf=(char *)malloc(wav_format.block_align*SLICE_BUF_SIZE);
+        if (!slice_buf) {
+          printf("Unable to malloc slice buffer");
+          exit(1);
+        }        num_slices=chunk_size/wav_format.block_align;
+        printf("DATA chunk\n");
+        printf("  chunk size %d (0x%x)\n",chunk_size,chunk_size);
+        printf("  %d slices\n",num_slices);
+        printf("  Ideal sample interval=%d\n",(unsigned)(1000000.0/wav_format.sample_rate));
+        samp_int=1000000/(wav_format.sample_rate);
+        if (slow) samp_int*=1.5;
+        printf("  programmed interrupt tick interval=%d\n",samp_int);
+
+// starting up ticker to write samples out -- no printfs until tick.detach is called
+        current_movement=0;
+        tick.attach_us(&dac_out, samp_int); 
+        led2=1;
+        for (slice=0;slice<num_slices;slice+=SLICE_BUF_SIZE) {
+          fread(slice_buf,wav_format.block_align*SLICE_BUF_SIZE,1,wavfile);
+          if (feof(wavfile)) {
+            printf("Oops -- not enough slices in the wave file\n");
+            exit(1);
+          }
+          data_sptr=(short *)slice_buf;
+          data_bptr=(unsigned char *)slice_buf;
+          data_wptr=(int *)slice_buf;
+          slice_value=0;
+          for (i=0;i<SLICE_BUF_SIZE;i++) {
+            for (channel=0;channel<wav_format.num_channels;channel++) {
+              switch (wav_format.sig_bps) {
+              case 16:
+                if (verbosity)
+                  printf("16 bit channel %d data=%d ",channel,data_sptr[channel]);
+                slice_value+=data_sptr[channel];
+                break;
+              case 32:
+                if (verbosity)
+                  printf("32 bit channel %d data=%d ",channel,data_wptr[channel]);
+                slice_value+=data_wptr[channel];
+                break;
+              case 8:
+                if (verbosity)
+                  printf("8 bit channel %d data=%d ",channel,(int)data_bptr[channel]);
+                slice_value+=data_bptr[channel];
+                break;
+              }
+            }
+            slice_value/=wav_format.num_channels;
+
+// slice_value is now averaged.  Next it needs to be scaled to an unsigned 16 bit value
+// with DC offset so it can be written to the DAC.
+            switch (wav_format.sig_bps) {
+              case 8:     slice_value<<=8;
+                          break;
+              case 16:    slice_value+=32768;
+                          break;
+              case 32:    slice_value>>=16;
+                          slice_value+=32768;
+                          break;
+            }
+            dac_data=(short unsigned )slice_value;
+            if (verbosity)
+              printf("sample %d wptr %d slice_value %d dac_data %u\n",slice,DAC_wptr,(int)slice_value,dac_data);
+
+// finally stick it in the DAC FIFO.  If the write pointer wraps around and meets the read pointer
+// the wait until the read pointer moves.
+            DAC_fifo[DAC_wptr]=dac_data;
+            DAC_wptr=(DAC_wptr+1) & 0xff;
+            while (DAC_wptr==DAC_rptr) {
+            }
+          }
+        }
+        led2=0;
+// wait for ISR to drain FIFO
+        wait_us(300);
+        tick.detach();
+        printf("Ticker detached\n");
+        led3=1;
+        free(slice_buf);
+        break;
+      case 0x5453494c:
+        printf("INFO chunk, size %d\n",chunk_size);
+        fseek(wavfile,chunk_size,SEEK_CUR);
+        break;
+      default:
+        printf("unknown chunk type 0x%x, size %d\n",chunk_id,chunk_size);
+        data=fseek(wavfile,chunk_size,SEEK_CUR);
+        break;
+    }
+    fread(&chunk_id,4,1,wavfile);
+    fread(&chunk_size,4,1,wavfile);
+  }
+  printf("Done with wave file\n");
+  fclose(wavfile);
+  led1=0;
+  body.pulsewidth_us(0);
+  mouth.pulsewidth_us(0);
+  tail.pulsewidth_us(0);
+}
+
+
+void dac_out() {
+        int value;
+    digout=1;
+    if (!movements[current_movement].played) {
+        if (movements[current_movement].sample<=slice) {
+            if (movements[current_movement].motor==0) body.pulsewidth_us(movements[current_movement].duty_cycle);
+            if (movements[current_movement].motor==1) mouth.pulsewidth_us(movements[current_movement].duty_cycle);
+            if (movements[current_movement].motor==2) tail.pulsewidth_us(movements[current_movement].duty_cycle);
+            movements[current_movement].played=1;
+            current_movement++;
+        }
+    }
+    DACout.write_u16(DAC_fifo[DAC_rptr]);
+    DAC_rptr=(DAC_rptr+1) & 0xff;
+    digout=0;
+}
+
+
+void cleanup(char *s)
+{
+        char *t;
+  t=strchr(s,'\n');
+  if (t) *t=0;
+  t=strchr(s,'\r');
+  if (t) *t=0;
+}        
+
+unsigned process_movement_file (char *mfname, MOV_STRUCT *mv,unsigned samp_rate)
+{
+        FILE *movfile;
+        char line[100],*tmp;
+        unsigned num_movements,i,j,x;
+  movfile=fopen(mfname,"rb");
+  if (!movfile) {
+    printf("Unable to open mov file '%s'\n",mfname);
+    return 0;
+  }
+  
+  fgets(line,100,movfile);
+  num_movements=0;
+#ifdef VERBOSE
+  printf("Motor report...\n");
+#endif
+  while (!feof(movfile)) {
+    if (line[0]!='#') {
+      tmp=line;
+// first thing on line is time in ms
+      movements[num_movements].sample=(atol(tmp)*samp_rate)/1000;
+// skip digits (non whitespace)
+      tmp=line;
+      while (*tmp!=' ' && *tmp!='\t' && *tmp!=0)
+        tmp++;
+// skip whitespace
+      while ((*tmp==' ' | *tmp=='\t') && *tmp!=0)
+        tmp++;
+      if (strstr(tmp,"body"))
+        movements[num_movements].motor=0;
+      if (strstr(tmp,"mouth"))
+        movements[num_movements].motor=1;
+      if (strstr(tmp,"tail"))
+        movements[num_movements].motor=2;
+// skip letters (non whitespace)
+      while (*tmp!=' ' && *tmp!='\t')
+        tmp++;
+// skip whitespace
+      while (*tmp==' ' | *tmp=='\t')
+        tmp++;
+      if (tmp)
+        movements[num_movements].duty_cycle=atoi(tmp);
+        movements[num_movements].played=0;
+#ifdef VERBOSE
+      printf("  moving motor %d at sample %ld with duty cycle %d\n",movements[num_movements].motor,movements[num_movements].sample,movements[num_movements].duty_cycle);
+#endif
+      num_movements++;
+    }
+    fgets(line,100,movfile);
+  }
+  printf("  %d movements read\n",num_movements);
+  printf("  sorting movements...");
+  for (i=0;i<num_movements;i++) {
+    for (j=i;j<num_movements;j++) {
+      if (movements[j].sample < movements[i].sample) {
+        x=movements[i].sample;    movements[i].sample=movements[j].sample;        movements[j].sample=x;
+        x=movements[i].motor ;    movements[i].motor =movements[j].motor ;        movements[j].motor =x;
+        x=movements[i].duty_cycle;movements[i].duty_cycle=movements[j].duty_cycle;movements[j].duty_cycle=x;
+      }
+    }
+  }
+  printf("done\n");
+  return num_movements;
+}