Twittering Billy Bass plays back samples and looks out for and reads twitters!

Dependencies:   NetServices mbed

billy.cpp

Committer:
simon
Date:
2011-04-09
Revision:
1:27b1efbf5a46
Parent:
0:ad2574c88043

File content as of revision 1:27b1efbf5a46:

// Original core billy player code factored from Steve Ravet's Big Mouth Billy Bass
// - See http://mbed.org/cookbook/Big-Mouth-Billy-Bass

#include "billy.h"

#include "mbed.h"

#define SAMPLE_FREQ 40000
#define BUF_SIZE (SAMPLE_FREQ/10)
#define SLICE_BUF_SIZE 1
#define USE_PUSHBUTTON 1

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;

// Billy's i/o
AnalogOut DACout(p18);
PwmOut body(p21);
PwmOut mouth(p22);
PwmOut tail(p23);
Ticker tick;

// 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);
unsigned process_movement_file (char *mfname, MOV_STRUCT *mv,unsigned samp_rate);

void billy_play(char *wavname, char *movname) {
    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);

    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);

                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) {
                        }
                    }
                }

// wait for ISR to drain FIFO
                wait_us(300);
                tick.detach();
                printf("Ticker detached\n");
                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);
    body.pulsewidth_us(0);
    mouth.pulsewidth_us(0);
    tail.pulsewidth_us(0);
}

void dac_out() {
    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;
}

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");
    fclose(movfile);
    return num_movements;
}