MP3 Player. You can change fwd/rev speed and skip. see: http://mbed.org/users/okini3939/notebook/lpc4088_madplayer/

Dependencies:   I2SSlave SDFileSystem TLV320 mbed

Committer:
okini3939
Date:
Tue Feb 18 00:22:50 2014 +0000
Revision:
0:8ba6230eefbd
1st build

Who changed what in which revision?

UserRevisionLine numberNew contents of line
okini3939 0:8ba6230eefbd 1 #include "mbed.h"
okini3939 0:8ba6230eefbd 2 #include "player.h"
okini3939 0:8ba6230eefbd 3 #include "SDFileSystem.h"
okini3939 0:8ba6230eefbd 4 #include "sdram.h"
okini3939 0:8ba6230eefbd 5
okini3939 0:8ba6230eefbd 6 #define DACBUF_SIZE (1024 * 1024 * 8 - 1024)
okini3939 0:8ba6230eefbd 7 SDFileSystem sd(p5, p6, p7, p8, "sd");
okini3939 0:8ba6230eefbd 8 TLV320 audio(p32, p31, 0x34, p11, p12, p13, p14, p16); // I2S Codec / sda, scl, addr, tx_sda, tx_ws, clk, rx_sda, rx_ws
okini3939 0:8ba6230eefbd 9
okini3939 0:8ba6230eefbd 10 static struct mad_decoder decoder;
okini3939 0:8ba6230eefbd 11 struct dacout_s *dacbuf;
okini3939 0:8ba6230eefbd 12 volatile int dac_r, dac_w, dac_l;
okini3939 0:8ba6230eefbd 13 FILE *fp;
okini3939 0:8ba6230eefbd 14 volatile int player_busy = 0, cmd_stop = 0;
okini3939 0:8ba6230eefbd 15 int dac_step = 1, dac_vol = 100;
okini3939 0:8ba6230eefbd 16
okini3939 0:8ba6230eefbd 17 extern DigitalOut led1, led2, led3, led4;
okini3939 0:8ba6230eefbd 18 extern Serial pc;
okini3939 0:8ba6230eefbd 19
okini3939 0:8ba6230eefbd 20
okini3939 0:8ba6230eefbd 21 bool isFull() {
okini3939 0:8ba6230eefbd 22 return (((dac_w + 1) % DACBUF_SIZE) == dac_r);
okini3939 0:8ba6230eefbd 23 };
okini3939 0:8ba6230eefbd 24 bool isEmpty() {
okini3939 0:8ba6230eefbd 25 return (dac_r == dac_w);
okini3939 0:8ba6230eefbd 26 };
okini3939 0:8ba6230eefbd 27 bool isEmpty2() {
okini3939 0:8ba6230eefbd 28 return (dac_r == dac_l);
okini3939 0:8ba6230eefbd 29 };
okini3939 0:8ba6230eefbd 30 uint32_t available() {
okini3939 0:8ba6230eefbd 31 return (dac_w >= dac_r) ? dac_w - dac_r : DACBUF_SIZE - dac_r + dac_w;
okini3939 0:8ba6230eefbd 32 };
okini3939 0:8ba6230eefbd 33 uint32_t available2() {
okini3939 0:8ba6230eefbd 34 return (dac_r >= dac_l) ? dac_r - dac_l : DACBUF_SIZE - dac_l + dac_r;
okini3939 0:8ba6230eefbd 35 };
okini3939 0:8ba6230eefbd 36
okini3939 0:8ba6230eefbd 37
okini3939 0:8ba6230eefbd 38 void isr_audio ()
okini3939 0:8ba6230eefbd 39 {
okini3939 0:8ba6230eefbd 40 int i, j, a;
okini3939 0:8ba6230eefbd 41 static int buf[4] = {0,0,0,0};
okini3939 0:8ba6230eefbd 42 static int w = 0;
okini3939 0:8ba6230eefbd 43 static short l = 0, r = 0;
okini3939 0:8ba6230eefbd 44
okini3939 0:8ba6230eefbd 45 for (i = 0; i < 4; i ++) {
okini3939 0:8ba6230eefbd 46 if (dac_step > 0 && !isEmpty()) {
okini3939 0:8ba6230eefbd 47 // fwd
okini3939 0:8ba6230eefbd 48 for (j = 0; j < dac_step; j ++) {
okini3939 0:8ba6230eefbd 49 // buf[i] = (dacbuf[dac_r].l << 16) | dacbuf[dac_r].r;
okini3939 0:8ba6230eefbd 50 l = dacbuf[dac_r].l;
okini3939 0:8ba6230eefbd 51 r = dacbuf[dac_r].r;
okini3939 0:8ba6230eefbd 52 buf[i] = (l << 16) | (r & 0xffff);
okini3939 0:8ba6230eefbd 53 dac_r = (dac_r + 1) % DACBUF_SIZE;
okini3939 0:8ba6230eefbd 54 if (isEmpty()) break;
okini3939 0:8ba6230eefbd 55 }
okini3939 0:8ba6230eefbd 56 } else
okini3939 0:8ba6230eefbd 57 if (dac_step < 0 && !isEmpty2()) {
okini3939 0:8ba6230eefbd 58 // rev
okini3939 0:8ba6230eefbd 59 for (j = 0; j < -dac_step; j ++) {
okini3939 0:8ba6230eefbd 60 // buf[i] = (dacbuf[dac_r].l << 16) | dacbuf[dac_r].r;
okini3939 0:8ba6230eefbd 61 l = dacbuf[dac_r].l;
okini3939 0:8ba6230eefbd 62 r = dacbuf[dac_r].r;
okini3939 0:8ba6230eefbd 63 buf[i] = (l << 16) | (r & 0xffff);
okini3939 0:8ba6230eefbd 64 dac_r = (dac_r - 1 + DACBUF_SIZE) % DACBUF_SIZE;
okini3939 0:8ba6230eefbd 65 if (isEmpty2()) break;
okini3939 0:8ba6230eefbd 66 }
okini3939 0:8ba6230eefbd 67 } else {
okini3939 0:8ba6230eefbd 68 // under flow
okini3939 0:8ba6230eefbd 69 if (l > 0) l --;
okini3939 0:8ba6230eefbd 70 if (l < 0) l ++;
okini3939 0:8ba6230eefbd 71 if (r > 0) r --;
okini3939 0:8ba6230eefbd 72 if (r < 0) r ++;
okini3939 0:8ba6230eefbd 73 buf[i] = (l << 16) | (r & 0xffff);
okini3939 0:8ba6230eefbd 74 }
okini3939 0:8ba6230eefbd 75 }
okini3939 0:8ba6230eefbd 76 audio.write(buf, 0, 4);
okini3939 0:8ba6230eefbd 77 }
okini3939 0:8ba6230eefbd 78
okini3939 0:8ba6230eefbd 79 int init_audio () {
okini3939 0:8ba6230eefbd 80 if (sdram_init() == 1) {
okini3939 0:8ba6230eefbd 81 pc.printf("Failed to initialize SDRAM\n");
okini3939 0:8ba6230eefbd 82 return -1;
okini3939 0:8ba6230eefbd 83 }
okini3939 0:8ba6230eefbd 84 malloc(16);
okini3939 0:8ba6230eefbd 85 dacbuf = (dacout_s*)malloc(sizeof(dacout_s) * DACBUF_SIZE);
okini3939 0:8ba6230eefbd 86 if (dacbuf == NULL) return -1;
okini3939 0:8ba6230eefbd 87 pc.printf("memory %08x\r\n", dacbuf);
okini3939 0:8ba6230eefbd 88
okini3939 0:8ba6230eefbd 89 audio.power(0x02); // mic off
okini3939 0:8ba6230eefbd 90 audio.inputVolume(0, 0);
okini3939 0:8ba6230eefbd 91 audio.frequency(44100);
okini3939 0:8ba6230eefbd 92 audio.attach(&isr_audio);
okini3939 0:8ba6230eefbd 93 audio.start(TRANSMIT);
okini3939 0:8ba6230eefbd 94 NVIC_SetPriority(I2S_IRQn, 1);
okini3939 0:8ba6230eefbd 95 NVIC_SetPriority(TIMER3_IRQn, 10);
okini3939 0:8ba6230eefbd 96 return 0;
okini3939 0:8ba6230eefbd 97 }
okini3939 0:8ba6230eefbd 98
okini3939 0:8ba6230eefbd 99 int play (char *filename) {
okini3939 0:8ba6230eefbd 100 int i;
okini3939 0:8ba6230eefbd 101
okini3939 0:8ba6230eefbd 102 DBG("play: %s\r\n", filename);
okini3939 0:8ba6230eefbd 103 fp = fopen(filename, "rb");
okini3939 0:8ba6230eefbd 104 if(!fp) {
okini3939 0:8ba6230eefbd 105 pc.printf("file error\r\n");
okini3939 0:8ba6230eefbd 106 return -1;
okini3939 0:8ba6230eefbd 107 }
okini3939 0:8ba6230eefbd 108 player_busy = 1;
okini3939 0:8ba6230eefbd 109 dac_r = dac_w = dac_l = 0;
okini3939 0:8ba6230eefbd 110 dac_step = 1;
okini3939 0:8ba6230eefbd 111 cmd_stop = 0;
okini3939 0:8ba6230eefbd 112 led2 = 0;
okini3939 0:8ba6230eefbd 113 mad_decoder_init(&decoder, NULL, input, 0, 0, output, error_fn, 0);
okini3939 0:8ba6230eefbd 114 mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
okini3939 0:8ba6230eefbd 115 mad_decoder_finish(&decoder);
okini3939 0:8ba6230eefbd 116 fclose(fp);
okini3939 0:8ba6230eefbd 117 fp = 0;
okini3939 0:8ba6230eefbd 118 dac_r = dac_w = 0;
okini3939 0:8ba6230eefbd 119 led2 = 1;
okini3939 0:8ba6230eefbd 120 player_busy = 0;
okini3939 0:8ba6230eefbd 121 wait_ms(100);
okini3939 0:8ba6230eefbd 122 DBG("eof\r\n");
okini3939 0:8ba6230eefbd 123 return 0;
okini3939 0:8ba6230eefbd 124 }
okini3939 0:8ba6230eefbd 125
okini3939 0:8ba6230eefbd 126 int command (char *cmd) {
okini3939 0:8ba6230eefbd 127 int i, r = -1;
okini3939 0:8ba6230eefbd 128
okini3939 0:8ba6230eefbd 129 pc.printf("command %s\r\n", cmd);
okini3939 0:8ba6230eefbd 130 switch (cmd[0]) {
okini3939 0:8ba6230eefbd 131 case 'P':
okini3939 0:8ba6230eefbd 132 if (!player_busy) {
okini3939 0:8ba6230eefbd 133 char buf[40];
okini3939 0:8ba6230eefbd 134 strcpy(buf, "/sd/");
okini3939 0:8ba6230eefbd 135 strcat(buf, &cmd[1]);
okini3939 0:8ba6230eefbd 136 r = play(buf);
okini3939 0:8ba6230eefbd 137 }
okini3939 0:8ba6230eefbd 138 break;
okini3939 0:8ba6230eefbd 139 case 'S':
okini3939 0:8ba6230eefbd 140 cmd_stop = 1;
okini3939 0:8ba6230eefbd 141 r = 0;
okini3939 0:8ba6230eefbd 142 break;
okini3939 0:8ba6230eefbd 143 case 'T':
okini3939 0:8ba6230eefbd 144 i = atoi(&cmd[1]);
okini3939 0:8ba6230eefbd 145 if (i < -10 || i > 10) break;
okini3939 0:8ba6230eefbd 146 dac_step = i;
okini3939 0:8ba6230eefbd 147 DBG("dac_step %d\r\n", dac_step);
okini3939 0:8ba6230eefbd 148 r = 0;
okini3939 0:8ba6230eefbd 149 break;
okini3939 0:8ba6230eefbd 150 case 'Q':
okini3939 0:8ba6230eefbd 151 if (cmd[1] == '+' || cmd[1] == '-') {
okini3939 0:8ba6230eefbd 152 i = atof(&cmd[1]) * 44100;
okini3939 0:8ba6230eefbd 153 if (i < 0 && i < - available2()) break;
okini3939 0:8ba6230eefbd 154 if (i > 0 && i > available()) break;
okini3939 0:8ba6230eefbd 155 dac_r += i;
okini3939 0:8ba6230eefbd 156 DBG("skip %+d\r\n", i);
okini3939 0:8ba6230eefbd 157 } else {
okini3939 0:8ba6230eefbd 158 i = atof(&cmd[1]) * 44100;
okini3939 0:8ba6230eefbd 159 if (i < dac_l || i > dac_r + available()) break;
okini3939 0:8ba6230eefbd 160 dac_r = i;
okini3939 0:8ba6230eefbd 161 DBG("skip %d\r\n", i);
okini3939 0:8ba6230eefbd 162 }
okini3939 0:8ba6230eefbd 163 r = 0;
okini3939 0:8ba6230eefbd 164 break;
okini3939 0:8ba6230eefbd 165 case 'V':
okini3939 0:8ba6230eefbd 166 i = atoi(&cmd[1]);
okini3939 0:8ba6230eefbd 167 if (i < 0 || i > 200) break;
okini3939 0:8ba6230eefbd 168 dac_vol = i;
okini3939 0:8ba6230eefbd 169 DBG("volume %d\r\n", i);
okini3939 0:8ba6230eefbd 170 r = 0;
okini3939 0:8ba6230eefbd 171 break;
okini3939 0:8ba6230eefbd 172 }
okini3939 0:8ba6230eefbd 173
okini3939 0:8ba6230eefbd 174 return r;
okini3939 0:8ba6230eefbd 175 }
okini3939 0:8ba6230eefbd 176
okini3939 0:8ba6230eefbd 177
okini3939 0:8ba6230eefbd 178 /*
okini3939 0:8ba6230eefbd 179 * This is the input callback. The purpose of this callback is to (re)fill
okini3939 0:8ba6230eefbd 180 * the stream buffer which is to be decoded.
okini3939 0:8ba6230eefbd 181 */
okini3939 0:8ba6230eefbd 182
okini3939 0:8ba6230eefbd 183 enum mad_flow input(void *data,
okini3939 0:8ba6230eefbd 184 struct mad_stream *stream)
okini3939 0:8ba6230eefbd 185 {
okini3939 0:8ba6230eefbd 186 static unsigned char strmbuff[2100];
okini3939 0:8ba6230eefbd 187 int ret;
okini3939 0:8ba6230eefbd 188 int rsz;
okini3939 0:8ba6230eefbd 189 unsigned char *bp;
okini3939 0:8ba6230eefbd 190
okini3939 0:8ba6230eefbd 191 /* the remaining bytes from incomplete frames must be copied
okini3939 0:8ba6230eefbd 192 to the beginning of the new buffer !
okini3939 0:8ba6230eefbd 193 */
okini3939 0:8ba6230eefbd 194 bp = strmbuff;
okini3939 0:8ba6230eefbd 195 rsz = 0;
okini3939 0:8ba6230eefbd 196 if(stream->error == MAD_ERROR_BUFLEN||stream->buffer==NULL) {
okini3939 0:8ba6230eefbd 197 if(stream->next_frame!=NULL) {
okini3939 0:8ba6230eefbd 198 rsz = stream->bufend-stream->next_frame;
okini3939 0:8ba6230eefbd 199 memmove(strmbuff,stream->next_frame,rsz);
okini3939 0:8ba6230eefbd 200 bp = strmbuff+rsz;
okini3939 0:8ba6230eefbd 201 }
okini3939 0:8ba6230eefbd 202 }
okini3939 0:8ba6230eefbd 203
okini3939 0:8ba6230eefbd 204 if (feof(fp)) {
okini3939 0:8ba6230eefbd 205 if (isEmpty()) {
okini3939 0:8ba6230eefbd 206 return MAD_FLOW_STOP;
okini3939 0:8ba6230eefbd 207 } else {
okini3939 0:8ba6230eefbd 208 return MAD_FLOW_CONTINUE;
okini3939 0:8ba6230eefbd 209 }
okini3939 0:8ba6230eefbd 210 }
okini3939 0:8ba6230eefbd 211
okini3939 0:8ba6230eefbd 212 led4 = 1;
okini3939 0:8ba6230eefbd 213 ret = fread(bp,1,sizeof(strmbuff) - rsz,fp);
okini3939 0:8ba6230eefbd 214
okini3939 0:8ba6230eefbd 215 if (!ret) {
okini3939 0:8ba6230eefbd 216 DBG("input stop\r\n");
okini3939 0:8ba6230eefbd 217 return MAD_FLOW_STOP;
okini3939 0:8ba6230eefbd 218 }
okini3939 0:8ba6230eefbd 219
okini3939 0:8ba6230eefbd 220 mad_stream_buffer(stream, strmbuff, ret + rsz);
okini3939 0:8ba6230eefbd 221
okini3939 0:8ba6230eefbd 222 return MAD_FLOW_CONTINUE;
okini3939 0:8ba6230eefbd 223 }
okini3939 0:8ba6230eefbd 224
okini3939 0:8ba6230eefbd 225
okini3939 0:8ba6230eefbd 226 /*
okini3939 0:8ba6230eefbd 227 * The following utility routine performs simple rounding, clipping, and
okini3939 0:8ba6230eefbd 228 * scaling of MAD's high-resolution samples down to 16 bits. It does not
okini3939 0:8ba6230eefbd 229 * perform any dithering or noise shaping, which would be recommended to
okini3939 0:8ba6230eefbd 230 * obtain any exceptional audio quality. It is therefore not recommended to
okini3939 0:8ba6230eefbd 231 * use this routine if high-quality output is desired.
okini3939 0:8ba6230eefbd 232 */
okini3939 0:8ba6230eefbd 233
okini3939 0:8ba6230eefbd 234 static /*inline*/
okini3939 0:8ba6230eefbd 235 signed int scale(mad_fixed_t sample)
okini3939 0:8ba6230eefbd 236 {
okini3939 0:8ba6230eefbd 237 /* round */
okini3939 0:8ba6230eefbd 238 sample += (1L << (MAD_F_FRACBITS - 16));
okini3939 0:8ba6230eefbd 239
okini3939 0:8ba6230eefbd 240 /* clip */
okini3939 0:8ba6230eefbd 241 if (sample >= MAD_F_ONE)
okini3939 0:8ba6230eefbd 242 sample = MAD_F_ONE - 1;
okini3939 0:8ba6230eefbd 243 else if (sample < -MAD_F_ONE)
okini3939 0:8ba6230eefbd 244 sample = -MAD_F_ONE;
okini3939 0:8ba6230eefbd 245
okini3939 0:8ba6230eefbd 246 /* quantize */
okini3939 0:8ba6230eefbd 247 return sample >> (MAD_F_FRACBITS + 1 - 16);
okini3939 0:8ba6230eefbd 248 }
okini3939 0:8ba6230eefbd 249
okini3939 0:8ba6230eefbd 250 /*
okini3939 0:8ba6230eefbd 251 * This is the output callback function. It is called after each frame of
okini3939 0:8ba6230eefbd 252 * MPEG audio data has been completely decoded. The purpose of this callback
okini3939 0:8ba6230eefbd 253 * is to output (or play) the decoded PCM audio.
okini3939 0:8ba6230eefbd 254 */
okini3939 0:8ba6230eefbd 255
okini3939 0:8ba6230eefbd 256 enum mad_flow output(void *data,
okini3939 0:8ba6230eefbd 257 struct mad_header const *header,
okini3939 0:8ba6230eefbd 258 struct mad_pcm *pcm)
okini3939 0:8ba6230eefbd 259 {
okini3939 0:8ba6230eefbd 260 unsigned int nchannels, nsamples;
okini3939 0:8ba6230eefbd 261 mad_fixed_t const *left_ch, *right_ch;
okini3939 0:8ba6230eefbd 262
okini3939 0:8ba6230eefbd 263 /* pcm->samplerate contains the sampling frequency */
okini3939 0:8ba6230eefbd 264 nchannels = pcm->channels;
okini3939 0:8ba6230eefbd 265 nsamples = pcm->length;
okini3939 0:8ba6230eefbd 266 left_ch = pcm->samples[0];
okini3939 0:8ba6230eefbd 267 right_ch = pcm->samples[1];
okini3939 0:8ba6230eefbd 268
okini3939 0:8ba6230eefbd 269 poll();
okini3939 0:8ba6230eefbd 270 while (nsamples--) {
okini3939 0:8ba6230eefbd 271 while (isFull() || available() >= (44100 * FWDBUF)) {
okini3939 0:8ba6230eefbd 272 poll();
okini3939 0:8ba6230eefbd 273 if (cmd_stop) break;
okini3939 0:8ba6230eefbd 274 }
okini3939 0:8ba6230eefbd 275 __disable_irq();
okini3939 0:8ba6230eefbd 276 dacbuf[dac_w].l = scale(*left_ch);
okini3939 0:8ba6230eefbd 277 dacbuf[dac_w].r = scale(*right_ch);
okini3939 0:8ba6230eefbd 278 dac_w = (dac_w + 1) % DACBUF_SIZE;
okini3939 0:8ba6230eefbd 279 if (dac_w == 0 || dac_l) dac_l ++;
okini3939 0:8ba6230eefbd 280 __enable_irq();
okini3939 0:8ba6230eefbd 281 left_ch++;
okini3939 0:8ba6230eefbd 282 right_ch++;
okini3939 0:8ba6230eefbd 283 }
okini3939 0:8ba6230eefbd 284
okini3939 0:8ba6230eefbd 285 if (cmd_stop) {
okini3939 0:8ba6230eefbd 286 DBG("output stop o\r\n");
okini3939 0:8ba6230eefbd 287 cmd_stop = 0;
okini3939 0:8ba6230eefbd 288 return MAD_FLOW_STOP;
okini3939 0:8ba6230eefbd 289 }
okini3939 0:8ba6230eefbd 290 return MAD_FLOW_CONTINUE;
okini3939 0:8ba6230eefbd 291 }
okini3939 0:8ba6230eefbd 292
okini3939 0:8ba6230eefbd 293 /*
okini3939 0:8ba6230eefbd 294 * This is the error callback function. It is called whenever a decoding
okini3939 0:8ba6230eefbd 295 * error occurs. The error is indicated by stream->error; the list of
okini3939 0:8ba6230eefbd 296 * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
okini3939 0:8ba6230eefbd 297 * header file.
okini3939 0:8ba6230eefbd 298 */
okini3939 0:8ba6230eefbd 299
okini3939 0:8ba6230eefbd 300 enum mad_flow error_fn(void *data,
okini3939 0:8ba6230eefbd 301 struct mad_stream *stream,
okini3939 0:8ba6230eefbd 302 struct mad_frame *frame)
okini3939 0:8ba6230eefbd 303 {
okini3939 0:8ba6230eefbd 304 /* ID3 tags will cause warnings and short noise, ignore it for the moment*/
okini3939 0:8ba6230eefbd 305 /*
okini3939 0:8ba6230eefbd 306 fprintf(stderr, "decoding error 0x%04x (%s)\n",
okini3939 0:8ba6230eefbd 307 stream->error, mad_stream_errorstr(stream));
okini3939 0:8ba6230eefbd 308 */
okini3939 0:8ba6230eefbd 309
okini3939 0:8ba6230eefbd 310 /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
okini3939 0:8ba6230eefbd 311
okini3939 0:8ba6230eefbd 312 return MAD_FLOW_CONTINUE;
okini3939 0:8ba6230eefbd 313 }
okini3939 0:8ba6230eefbd 314