Twittering Billy Bass plays back samples and looks out for and reads twitters!
Dependencies: NetServices mbed
billy.cpp@1:27b1efbf5a46, 2011-04-09 (annotated)
- Committer:
- simon
- Date:
- Sat Apr 09 08:35:20 2011 +0000
- Revision:
- 1:27b1efbf5a46
- Parent:
- 0:ad2574c88043
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
simon | 1:27b1efbf5a46 | 1 | // Original core billy player code factored from Steve Ravet's Big Mouth Billy Bass |
simon | 1:27b1efbf5a46 | 2 | // - See http://mbed.org/cookbook/Big-Mouth-Billy-Bass |
simon | 1:27b1efbf5a46 | 3 | |
simon | 0:ad2574c88043 | 4 | #include "billy.h" |
simon | 0:ad2574c88043 | 5 | |
simon | 0:ad2574c88043 | 6 | #include "mbed.h" |
simon | 0:ad2574c88043 | 7 | |
simon | 0:ad2574c88043 | 8 | #define SAMPLE_FREQ 40000 |
simon | 0:ad2574c88043 | 9 | #define BUF_SIZE (SAMPLE_FREQ/10) |
simon | 0:ad2574c88043 | 10 | #define SLICE_BUF_SIZE 1 |
simon | 0:ad2574c88043 | 11 | #define USE_PUSHBUTTON 1 |
simon | 0:ad2574c88043 | 12 | |
simon | 0:ad2574c88043 | 13 | typedef struct uFMT_STRUCT { |
simon | 0:ad2574c88043 | 14 | short comp_code; |
simon | 0:ad2574c88043 | 15 | short num_channels; |
simon | 0:ad2574c88043 | 16 | unsigned sample_rate; |
simon | 0:ad2574c88043 | 17 | unsigned avg_Bps; |
simon | 0:ad2574c88043 | 18 | short block_align; |
simon | 0:ad2574c88043 | 19 | short sig_bps; |
simon | 0:ad2574c88043 | 20 | } FMT_STRUCT; |
simon | 0:ad2574c88043 | 21 | |
simon | 0:ad2574c88043 | 22 | typedef struct uMOV_STRUCT { |
simon | 0:ad2574c88043 | 23 | long sample; |
simon | 0:ad2574c88043 | 24 | unsigned motor; |
simon | 0:ad2574c88043 | 25 | unsigned duty_cycle; |
simon | 0:ad2574c88043 | 26 | unsigned played; |
simon | 0:ad2574c88043 | 27 | } MOV_STRUCT; |
simon | 0:ad2574c88043 | 28 | |
simon | 0:ad2574c88043 | 29 | // Billy's i/o |
simon | 0:ad2574c88043 | 30 | AnalogOut DACout(p18); |
simon | 0:ad2574c88043 | 31 | PwmOut body(p21); |
simon | 0:ad2574c88043 | 32 | PwmOut mouth(p22); |
simon | 0:ad2574c88043 | 33 | PwmOut tail(p23); |
simon | 0:ad2574c88043 | 34 | Ticker tick; |
simon | 0:ad2574c88043 | 35 | |
simon | 0:ad2574c88043 | 36 | // global variables used both by the main program and the ISR |
simon | 0:ad2574c88043 | 37 | short DAC_fifo[256]; // FIFO for the DAC |
simon | 0:ad2574c88043 | 38 | short DAC_wptr; // FIFO pointer |
simon | 0:ad2574c88043 | 39 | volatile short DAC_rptr; // FIFO pointer |
simon | 0:ad2574c88043 | 40 | long slice; |
simon | 0:ad2574c88043 | 41 | unsigned num_movements; |
simon | 0:ad2574c88043 | 42 | unsigned current_movement; |
simon | 0:ad2574c88043 | 43 | MOV_STRUCT movements[500]; |
simon | 0:ad2574c88043 | 44 | |
simon | 0:ad2574c88043 | 45 | void dac_out(void); |
simon | 0:ad2574c88043 | 46 | unsigned process_movement_file (char *mfname, MOV_STRUCT *mv,unsigned samp_rate); |
simon | 0:ad2574c88043 | 47 | |
simon | 0:ad2574c88043 | 48 | void billy_play(char *wavname, char *movname) { |
simon | 0:ad2574c88043 | 49 | unsigned chunk_id,chunk_size,channel; |
simon | 0:ad2574c88043 | 50 | unsigned data,samp_int,i; |
simon | 0:ad2574c88043 | 51 | short dac_data; |
simon | 0:ad2574c88043 | 52 | char *slice_buf; |
simon | 0:ad2574c88043 | 53 | short *data_sptr; |
simon | 0:ad2574c88043 | 54 | unsigned char *data_bptr; |
simon | 0:ad2574c88043 | 55 | int *data_wptr; |
simon | 0:ad2574c88043 | 56 | FMT_STRUCT wav_format; |
simon | 0:ad2574c88043 | 57 | FILE *wavfile; |
simon | 0:ad2574c88043 | 58 | long num_slices; |
simon | 0:ad2574c88043 | 59 | long long slice_value; |
simon | 0:ad2574c88043 | 60 | int verbosity=0; |
simon | 0:ad2574c88043 | 61 | DAC_wptr=0; |
simon | 0:ad2574c88043 | 62 | DAC_rptr=0; |
simon | 0:ad2574c88043 | 63 | for (i=0;i<256;i+=2) { |
simon | 0:ad2574c88043 | 64 | DAC_fifo[i]=0; |
simon | 0:ad2574c88043 | 65 | DAC_fifo[i+1]=3000; |
simon | 0:ad2574c88043 | 66 | } |
simon | 0:ad2574c88043 | 67 | DAC_wptr=4; |
simon | 0:ad2574c88043 | 68 | |
simon | 0:ad2574c88043 | 69 | body.period_us(100); |
simon | 0:ad2574c88043 | 70 | mouth.period_us(100); |
simon | 0:ad2574c88043 | 71 | tail.period_us(100); |
simon | 0:ad2574c88043 | 72 | |
simon | 0:ad2574c88043 | 73 | printf("Playing wave file '%s', mov file '%s'\n",wavname, movname); |
simon | 0:ad2574c88043 | 74 | |
simon | 0:ad2574c88043 | 75 | wavfile=fopen(wavname,"rb"); |
simon | 0:ad2574c88043 | 76 | if (!wavfile) { |
simon | 0:ad2574c88043 | 77 | printf("Unable to open wav file '%s'\n",wavname); |
simon | 0:ad2574c88043 | 78 | return; |
simon | 0:ad2574c88043 | 79 | } |
simon | 0:ad2574c88043 | 80 | |
simon | 0:ad2574c88043 | 81 | fread(&chunk_id,4,1,wavfile); |
simon | 0:ad2574c88043 | 82 | fread(&chunk_size,4,1,wavfile); |
simon | 0:ad2574c88043 | 83 | while (!feof(wavfile)) { |
simon | 0:ad2574c88043 | 84 | printf("Read chunk ID 0x%x, size 0x%x\n",chunk_id,chunk_size); |
simon | 0:ad2574c88043 | 85 | switch (chunk_id) { |
simon | 0:ad2574c88043 | 86 | case 0x46464952: |
simon | 0:ad2574c88043 | 87 | fread(&data,4,1,wavfile); |
simon | 0:ad2574c88043 | 88 | printf("RIFF chunk\n"); |
simon | 0:ad2574c88043 | 89 | printf(" chunk size %d (0x%x)\n",chunk_size,chunk_size); |
simon | 0:ad2574c88043 | 90 | printf(" RIFF type 0x%x\n",data); |
simon | 0:ad2574c88043 | 91 | break; |
simon | 0:ad2574c88043 | 92 | case 0x20746d66: |
simon | 0:ad2574c88043 | 93 | fread(&wav_format,sizeof(wav_format),1,wavfile); |
simon | 0:ad2574c88043 | 94 | printf("FORMAT chunk\n"); |
simon | 0:ad2574c88043 | 95 | printf(" chunk size %d (0x%x)\n",chunk_size,chunk_size); |
simon | 0:ad2574c88043 | 96 | printf(" compression code %d\n",wav_format.comp_code); |
simon | 0:ad2574c88043 | 97 | printf(" %d channels\n",wav_format.num_channels); |
simon | 0:ad2574c88043 | 98 | printf(" %d samples/sec\n",wav_format.sample_rate); |
simon | 0:ad2574c88043 | 99 | printf(" %d bytes/sec\n",wav_format.avg_Bps); |
simon | 0:ad2574c88043 | 100 | printf(" block align %d\n",wav_format.block_align); |
simon | 0:ad2574c88043 | 101 | printf(" %d bits per sample\n",wav_format.sig_bps); |
simon | 0:ad2574c88043 | 102 | if (chunk_size > sizeof(wav_format)) |
simon | 0:ad2574c88043 | 103 | fseek(wavfile,chunk_size-sizeof(wav_format),SEEK_CUR); |
simon | 0:ad2574c88043 | 104 | // create a slice buffer large enough to hold multiple slices |
simon | 0:ad2574c88043 | 105 | slice_buf=(char *)malloc(wav_format.block_align*SLICE_BUF_SIZE); |
simon | 0:ad2574c88043 | 106 | if (!slice_buf) { |
simon | 0:ad2574c88043 | 107 | printf("Unable to malloc slice buffer"); |
simon | 0:ad2574c88043 | 108 | exit(1); |
simon | 0:ad2574c88043 | 109 | } |
simon | 0:ad2574c88043 | 110 | // now that the sample rate is known, process the movement file |
simon | 0:ad2574c88043 | 111 | num_movements=process_movement_file(movname,movements,wav_format.sample_rate); |
simon | 0:ad2574c88043 | 112 | break; |
simon | 0:ad2574c88043 | 113 | |
simon | 0:ad2574c88043 | 114 | case 0x61746164: |
simon | 0:ad2574c88043 | 115 | slice_buf=(char *)malloc(wav_format.block_align*SLICE_BUF_SIZE); |
simon | 0:ad2574c88043 | 116 | if (!slice_buf) { |
simon | 0:ad2574c88043 | 117 | printf("Unable to malloc slice buffer"); |
simon | 0:ad2574c88043 | 118 | exit(1); |
simon | 0:ad2574c88043 | 119 | } |
simon | 0:ad2574c88043 | 120 | num_slices=chunk_size/wav_format.block_align; |
simon | 0:ad2574c88043 | 121 | printf("DATA chunk\n"); |
simon | 0:ad2574c88043 | 122 | printf(" chunk size %d (0x%x)\n",chunk_size,chunk_size); |
simon | 0:ad2574c88043 | 123 | printf(" %d slices\n",num_slices); |
simon | 0:ad2574c88043 | 124 | printf(" Ideal sample interval=%d\n",(unsigned)(1000000.0/wav_format.sample_rate)); |
simon | 0:ad2574c88043 | 125 | samp_int=1000000/(wav_format.sample_rate); |
simon | 0:ad2574c88043 | 126 | |
simon | 0:ad2574c88043 | 127 | printf(" programmed interrupt tick interval=%d\n",samp_int); |
simon | 0:ad2574c88043 | 128 | |
simon | 0:ad2574c88043 | 129 | // starting up ticker to write samples out -- no printfs until tick.detach is called |
simon | 0:ad2574c88043 | 130 | current_movement = 0; |
simon | 0:ad2574c88043 | 131 | tick.attach_us(&dac_out, samp_int); |
simon | 0:ad2574c88043 | 132 | //led2=1; |
simon | 0:ad2574c88043 | 133 | for (slice=0;slice<num_slices;slice+=SLICE_BUF_SIZE) { |
simon | 0:ad2574c88043 | 134 | fread(slice_buf,wav_format.block_align*SLICE_BUF_SIZE,1,wavfile); |
simon | 0:ad2574c88043 | 135 | if (feof(wavfile)) { |
simon | 0:ad2574c88043 | 136 | printf("Oops -- not enough slices in the wave file\n"); |
simon | 0:ad2574c88043 | 137 | exit(1); |
simon | 0:ad2574c88043 | 138 | } |
simon | 0:ad2574c88043 | 139 | data_sptr=(short *)slice_buf; |
simon | 0:ad2574c88043 | 140 | data_bptr=(unsigned char *)slice_buf; |
simon | 0:ad2574c88043 | 141 | data_wptr=(int *)slice_buf; |
simon | 0:ad2574c88043 | 142 | slice_value=0; |
simon | 0:ad2574c88043 | 143 | for (i=0;i<SLICE_BUF_SIZE;i++) { |
simon | 0:ad2574c88043 | 144 | for (channel=0;channel<wav_format.num_channels;channel++) { |
simon | 0:ad2574c88043 | 145 | switch (wav_format.sig_bps) { |
simon | 0:ad2574c88043 | 146 | case 16: |
simon | 0:ad2574c88043 | 147 | if (verbosity) |
simon | 0:ad2574c88043 | 148 | printf("16 bit channel %d data=%d ",channel,data_sptr[channel]); |
simon | 0:ad2574c88043 | 149 | slice_value+=data_sptr[channel]; |
simon | 0:ad2574c88043 | 150 | break; |
simon | 0:ad2574c88043 | 151 | case 32: |
simon | 0:ad2574c88043 | 152 | if (verbosity) |
simon | 0:ad2574c88043 | 153 | printf("32 bit channel %d data=%d ",channel,data_wptr[channel]); |
simon | 0:ad2574c88043 | 154 | slice_value+=data_wptr[channel]; |
simon | 0:ad2574c88043 | 155 | break; |
simon | 0:ad2574c88043 | 156 | case 8: |
simon | 0:ad2574c88043 | 157 | if (verbosity) |
simon | 0:ad2574c88043 | 158 | printf("8 bit channel %d data=%d ",channel,(int)data_bptr[channel]); |
simon | 0:ad2574c88043 | 159 | slice_value+=data_bptr[channel]; |
simon | 0:ad2574c88043 | 160 | break; |
simon | 0:ad2574c88043 | 161 | } |
simon | 0:ad2574c88043 | 162 | } |
simon | 0:ad2574c88043 | 163 | slice_value/=wav_format.num_channels; |
simon | 0:ad2574c88043 | 164 | |
simon | 0:ad2574c88043 | 165 | // slice_value is now averaged. Next it needs to be scaled to an unsigned 16 bit value |
simon | 0:ad2574c88043 | 166 | // with DC offset so it can be written to the DAC. |
simon | 0:ad2574c88043 | 167 | switch (wav_format.sig_bps) { |
simon | 0:ad2574c88043 | 168 | case 8: |
simon | 0:ad2574c88043 | 169 | slice_value<<=8; |
simon | 0:ad2574c88043 | 170 | break; |
simon | 0:ad2574c88043 | 171 | case 16: |
simon | 0:ad2574c88043 | 172 | slice_value+=32768; |
simon | 0:ad2574c88043 | 173 | break; |
simon | 0:ad2574c88043 | 174 | case 32: |
simon | 0:ad2574c88043 | 175 | slice_value>>=16; |
simon | 0:ad2574c88043 | 176 | slice_value+=32768; |
simon | 0:ad2574c88043 | 177 | break; |
simon | 0:ad2574c88043 | 178 | } |
simon | 0:ad2574c88043 | 179 | dac_data=(short unsigned )slice_value; |
simon | 0:ad2574c88043 | 180 | if (verbosity) |
simon | 0:ad2574c88043 | 181 | printf("sample %d wptr %d slice_value %d dac_data %u\n",slice,DAC_wptr,(int)slice_value,dac_data); |
simon | 0:ad2574c88043 | 182 | |
simon | 0:ad2574c88043 | 183 | // finally stick it in the DAC FIFO. If the write pointer wraps around and meets the read pointer |
simon | 0:ad2574c88043 | 184 | // the wait until the read pointer moves. |
simon | 0:ad2574c88043 | 185 | DAC_fifo[DAC_wptr]=dac_data; |
simon | 0:ad2574c88043 | 186 | DAC_wptr=(DAC_wptr+1) & 0xff; |
simon | 0:ad2574c88043 | 187 | while (DAC_wptr==DAC_rptr) { |
simon | 0:ad2574c88043 | 188 | } |
simon | 0:ad2574c88043 | 189 | } |
simon | 0:ad2574c88043 | 190 | } |
simon | 0:ad2574c88043 | 191 | |
simon | 0:ad2574c88043 | 192 | // wait for ISR to drain FIFO |
simon | 0:ad2574c88043 | 193 | wait_us(300); |
simon | 0:ad2574c88043 | 194 | tick.detach(); |
simon | 0:ad2574c88043 | 195 | printf("Ticker detached\n"); |
simon | 0:ad2574c88043 | 196 | free(slice_buf); |
simon | 0:ad2574c88043 | 197 | break; |
simon | 0:ad2574c88043 | 198 | case 0x5453494c: |
simon | 0:ad2574c88043 | 199 | printf("INFO chunk, size %d\n",chunk_size); |
simon | 0:ad2574c88043 | 200 | fseek(wavfile,chunk_size,SEEK_CUR); |
simon | 0:ad2574c88043 | 201 | break; |
simon | 0:ad2574c88043 | 202 | default: |
simon | 0:ad2574c88043 | 203 | printf("unknown chunk type 0x%x, size %d\n",chunk_id,chunk_size); |
simon | 0:ad2574c88043 | 204 | data=fseek(wavfile,chunk_size,SEEK_CUR); |
simon | 0:ad2574c88043 | 205 | break; |
simon | 0:ad2574c88043 | 206 | } |
simon | 0:ad2574c88043 | 207 | fread(&chunk_id,4,1,wavfile); |
simon | 0:ad2574c88043 | 208 | fread(&chunk_size,4,1,wavfile); |
simon | 0:ad2574c88043 | 209 | } |
simon | 0:ad2574c88043 | 210 | printf("Done with wave file\n"); |
simon | 0:ad2574c88043 | 211 | fclose(wavfile); |
simon | 0:ad2574c88043 | 212 | body.pulsewidth_us(0); |
simon | 0:ad2574c88043 | 213 | mouth.pulsewidth_us(0); |
simon | 0:ad2574c88043 | 214 | tail.pulsewidth_us(0); |
simon | 0:ad2574c88043 | 215 | } |
simon | 0:ad2574c88043 | 216 | |
simon | 0:ad2574c88043 | 217 | void dac_out() { |
simon | 0:ad2574c88043 | 218 | if (!movements[current_movement].played) { |
simon | 0:ad2574c88043 | 219 | if (movements[current_movement].sample<=slice) { |
simon | 0:ad2574c88043 | 220 | if (movements[current_movement].motor==0) body.pulsewidth_us(movements[current_movement].duty_cycle); |
simon | 0:ad2574c88043 | 221 | if (movements[current_movement].motor==1) mouth.pulsewidth_us(movements[current_movement].duty_cycle); |
simon | 0:ad2574c88043 | 222 | if (movements[current_movement].motor==2) tail.pulsewidth_us(movements[current_movement].duty_cycle); |
simon | 0:ad2574c88043 | 223 | movements[current_movement].played=1; |
simon | 0:ad2574c88043 | 224 | current_movement++; |
simon | 0:ad2574c88043 | 225 | } |
simon | 0:ad2574c88043 | 226 | } |
simon | 0:ad2574c88043 | 227 | DACout.write_u16(DAC_fifo[DAC_rptr]); |
simon | 0:ad2574c88043 | 228 | DAC_rptr=(DAC_rptr+1) & 0xff; |
simon | 0:ad2574c88043 | 229 | } |
simon | 0:ad2574c88043 | 230 | |
simon | 0:ad2574c88043 | 231 | unsigned process_movement_file(char *mfname, MOV_STRUCT *mv,unsigned samp_rate) { |
simon | 0:ad2574c88043 | 232 | FILE *movfile; |
simon | 0:ad2574c88043 | 233 | char line[100],*tmp; |
simon | 0:ad2574c88043 | 234 | unsigned num_movements,i,j,x; |
simon | 0:ad2574c88043 | 235 | movfile=fopen(mfname,"rb"); |
simon | 0:ad2574c88043 | 236 | if (!movfile) { |
simon | 0:ad2574c88043 | 237 | printf("Unable to open mov file '%s'\n",mfname); |
simon | 0:ad2574c88043 | 238 | return 0; |
simon | 0:ad2574c88043 | 239 | } |
simon | 0:ad2574c88043 | 240 | |
simon | 0:ad2574c88043 | 241 | fgets(line,100,movfile); |
simon | 0:ad2574c88043 | 242 | num_movements=0; |
simon | 0:ad2574c88043 | 243 | #ifdef VERBOSE |
simon | 0:ad2574c88043 | 244 | printf("Motor report...\n"); |
simon | 0:ad2574c88043 | 245 | #endif |
simon | 0:ad2574c88043 | 246 | while (!feof(movfile)) { |
simon | 0:ad2574c88043 | 247 | if (line[0]!='#') { |
simon | 0:ad2574c88043 | 248 | tmp=line; |
simon | 0:ad2574c88043 | 249 | // first thing on line is time in ms |
simon | 0:ad2574c88043 | 250 | movements[num_movements].sample=(atol(tmp)*samp_rate)/1000; |
simon | 0:ad2574c88043 | 251 | // skip digits (non whitespace) |
simon | 0:ad2574c88043 | 252 | tmp=line; |
simon | 0:ad2574c88043 | 253 | while (*tmp!=' ' && *tmp!='\t' && *tmp!=0) |
simon | 0:ad2574c88043 | 254 | tmp++; |
simon | 0:ad2574c88043 | 255 | // skip whitespace |
simon | 0:ad2574c88043 | 256 | while ((*tmp==' ' | *tmp=='\t') && *tmp!=0) |
simon | 0:ad2574c88043 | 257 | tmp++; |
simon | 0:ad2574c88043 | 258 | if (strstr(tmp,"body")) |
simon | 0:ad2574c88043 | 259 | movements[num_movements].motor=0; |
simon | 0:ad2574c88043 | 260 | if (strstr(tmp,"mouth")) |
simon | 0:ad2574c88043 | 261 | movements[num_movements].motor=1; |
simon | 0:ad2574c88043 | 262 | if (strstr(tmp,"tail")) |
simon | 0:ad2574c88043 | 263 | movements[num_movements].motor=2; |
simon | 0:ad2574c88043 | 264 | // skip letters (non whitespace) |
simon | 0:ad2574c88043 | 265 | while (*tmp!=' ' && *tmp!='\t') |
simon | 0:ad2574c88043 | 266 | tmp++; |
simon | 0:ad2574c88043 | 267 | // skip whitespace |
simon | 0:ad2574c88043 | 268 | while (*tmp==' ' | *tmp=='\t') |
simon | 0:ad2574c88043 | 269 | tmp++; |
simon | 0:ad2574c88043 | 270 | if (tmp) |
simon | 0:ad2574c88043 | 271 | movements[num_movements].duty_cycle=atoi(tmp); |
simon | 0:ad2574c88043 | 272 | movements[num_movements].played=0; |
simon | 0:ad2574c88043 | 273 | #ifdef VERBOSE |
simon | 0:ad2574c88043 | 274 | 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); |
simon | 0:ad2574c88043 | 275 | #endif |
simon | 0:ad2574c88043 | 276 | num_movements++; |
simon | 0:ad2574c88043 | 277 | } |
simon | 0:ad2574c88043 | 278 | fgets(line,100,movfile); |
simon | 0:ad2574c88043 | 279 | } |
simon | 0:ad2574c88043 | 280 | printf(" %d movements read\n",num_movements); |
simon | 0:ad2574c88043 | 281 | printf(" sorting movements..."); |
simon | 0:ad2574c88043 | 282 | for (i=0;i<num_movements;i++) { |
simon | 0:ad2574c88043 | 283 | for (j=i;j<num_movements;j++) { |
simon | 0:ad2574c88043 | 284 | if (movements[j].sample < movements[i].sample) { |
simon | 0:ad2574c88043 | 285 | x=movements[i].sample; |
simon | 0:ad2574c88043 | 286 | movements[i].sample=movements[j].sample; |
simon | 0:ad2574c88043 | 287 | movements[j].sample=x; |
simon | 0:ad2574c88043 | 288 | x=movements[i].motor ; |
simon | 0:ad2574c88043 | 289 | movements[i].motor =movements[j].motor ; |
simon | 0:ad2574c88043 | 290 | movements[j].motor =x; |
simon | 0:ad2574c88043 | 291 | x=movements[i].duty_cycle; |
simon | 0:ad2574c88043 | 292 | movements[i].duty_cycle=movements[j].duty_cycle; |
simon | 0:ad2574c88043 | 293 | movements[j].duty_cycle=x; |
simon | 0:ad2574c88043 | 294 | } |
simon | 0:ad2574c88043 | 295 | } |
simon | 0:ad2574c88043 | 296 | } |
simon | 0:ad2574c88043 | 297 | printf("done\n"); |
simon | 0:ad2574c88043 | 298 | fclose(movfile); |
simon | 0:ad2574c88043 | 299 | return num_movements; |
simon | 0:ad2574c88043 | 300 | } |