The Big Mouth Billy Bass program

Dependencies:   mbed

main.cpp

Committer:
sravet
Date:
2011-01-17
Revision:
0:ef6fc1737022

File content as of revision 0:ef6fc1737022:


#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;
}