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

Dependencies:   I2SSlave SDFileSystem TLV320 mbed

Revision:
0:8ba6230eefbd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/player.cpp	Tue Feb 18 00:22:50 2014 +0000
@@ -0,0 +1,314 @@
+#include "mbed.h"
+#include "player.h"
+#include "SDFileSystem.h"
+#include "sdram.h"
+
+#define DACBUF_SIZE (1024 * 1024 * 8 - 1024)
+SDFileSystem sd(p5, p6, p7, p8, "sd");
+TLV320 audio(p32, p31, 0x34, p11, p12, p13, p14, p16); // I2S Codec / sda, scl, addr, tx_sda, tx_ws, clk, rx_sda, rx_ws
+
+static struct mad_decoder decoder;
+struct dacout_s *dacbuf;
+volatile int dac_r, dac_w, dac_l;
+FILE *fp;
+volatile int player_busy = 0, cmd_stop = 0;
+int dac_step = 1, dac_vol = 100;
+
+extern DigitalOut led1, led2, led3, led4;
+extern Serial pc;
+
+
+bool isFull() {
+    return (((dac_w + 1) % DACBUF_SIZE) == dac_r);
+};
+bool isEmpty() {
+    return (dac_r == dac_w);
+};
+bool isEmpty2() {
+    return (dac_r == dac_l);
+};
+uint32_t available() {
+    return (dac_w >= dac_r) ? dac_w - dac_r : DACBUF_SIZE - dac_r + dac_w;
+};
+uint32_t available2() {
+    return (dac_r >= dac_l) ? dac_r - dac_l : DACBUF_SIZE - dac_l + dac_r;
+};
+
+
+void isr_audio ()
+{
+    int i, j, a;
+    static int buf[4] = {0,0,0,0};
+    static int w = 0;
+    static short l = 0, r = 0;
+
+    for (i = 0; i < 4; i ++) {
+        if (dac_step > 0 && !isEmpty()) {
+            // fwd
+            for (j = 0; j < dac_step; j ++) {
+//              buf[i] = (dacbuf[dac_r].l << 16) | dacbuf[dac_r].r;
+              l = dacbuf[dac_r].l;
+              r = dacbuf[dac_r].r;
+              buf[i] = (l << 16) | (r & 0xffff);
+              dac_r = (dac_r + 1) % DACBUF_SIZE;
+              if (isEmpty()) break;
+            }
+        } else
+        if (dac_step < 0 && !isEmpty2()) {
+            // rev
+            for (j = 0; j < -dac_step; j ++) {
+//              buf[i] = (dacbuf[dac_r].l << 16) | dacbuf[dac_r].r;
+              l = dacbuf[dac_r].l;
+              r = dacbuf[dac_r].r;
+              buf[i] = (l << 16) | (r & 0xffff);
+              dac_r = (dac_r - 1 + DACBUF_SIZE) % DACBUF_SIZE;
+              if (isEmpty2()) break;
+            }
+        } else {
+            // under flow
+            if (l > 0) l --;
+            if (l < 0) l ++;
+            if (r > 0) r --;
+            if (r < 0) r ++;
+            buf[i] = (l << 16) | (r & 0xffff);
+        }
+    }
+    audio.write(buf, 0, 4);
+}
+
+int init_audio () {
+    if (sdram_init() == 1) {
+        pc.printf("Failed to initialize SDRAM\n");
+        return -1;
+    }
+    malloc(16); 
+    dacbuf = (dacout_s*)malloc(sizeof(dacout_s) * DACBUF_SIZE);
+    if (dacbuf == NULL) return -1;
+    pc.printf("memory %08x\r\n", dacbuf);
+
+    audio.power(0x02); // mic off
+    audio.inputVolume(0, 0);
+    audio.frequency(44100);
+    audio.attach(&isr_audio);
+    audio.start(TRANSMIT);
+    NVIC_SetPriority(I2S_IRQn, 1);
+    NVIC_SetPriority(TIMER3_IRQn, 10);
+    return 0;
+}
+
+int play (char *filename) {
+    int i;
+
+    DBG("play: %s\r\n", filename);
+    fp = fopen(filename, "rb");
+    if(!fp) {
+        pc.printf("file error\r\n");
+        return -1;
+    }
+    player_busy = 1;
+    dac_r = dac_w = dac_l = 0;
+    dac_step = 1;
+    cmd_stop = 0;
+    led2 = 0;
+    mad_decoder_init(&decoder, NULL, input, 0, 0, output, error_fn, 0);
+    mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
+    mad_decoder_finish(&decoder);
+    fclose(fp);
+    fp = 0;
+    dac_r = dac_w = 0;
+    led2 = 1;
+    player_busy = 0;
+    wait_ms(100);
+    DBG("eof\r\n");
+    return 0;
+}
+
+int command (char *cmd) {
+    int i, r = -1;
+
+    pc.printf("command %s\r\n", cmd);
+    switch (cmd[0]) {
+    case 'P':
+        if (!player_busy) {
+            char buf[40];
+            strcpy(buf, "/sd/");
+            strcat(buf, &cmd[1]);
+            r = play(buf);
+        }
+        break;
+    case 'S':
+        cmd_stop = 1;
+        r = 0;
+        break;
+    case 'T':
+        i = atoi(&cmd[1]);
+        if (i < -10 || i > 10) break;
+        dac_step = i;
+        DBG("dac_step %d\r\n", dac_step);
+        r = 0;
+        break;
+    case 'Q':
+        if (cmd[1] == '+' || cmd[1] == '-') {
+            i = atof(&cmd[1]) * 44100;
+            if (i < 0 && i < - available2()) break;
+            if (i > 0 && i > available()) break;
+            dac_r += i;
+            DBG("skip %+d\r\n", i);
+        } else {
+            i = atof(&cmd[1]) * 44100;
+            if (i < dac_l || i > dac_r + available()) break;
+            dac_r = i;
+            DBG("skip %d\r\n", i);
+        }
+        r = 0;
+        break;
+    case 'V':
+        i = atoi(&cmd[1]);
+        if (i < 0 || i > 200) break;
+        dac_vol = i;
+        DBG("volume %d\r\n", i);
+        r = 0;
+        break;
+    }
+
+    return r;
+}
+
+
+/*
+ * This is the input callback. The purpose of this callback is to (re)fill
+ * the stream buffer which is to be decoded.
+ */
+
+enum mad_flow input(void *data,
+                    struct mad_stream *stream)
+{
+    static unsigned char strmbuff[2100];
+    int ret;
+    int rsz;
+    unsigned char *bp;
+
+    /* the remaining bytes from incomplete frames must be copied
+    to the beginning of the new buffer !
+    */
+    bp = strmbuff;
+    rsz = 0;
+    if(stream->error == MAD_ERROR_BUFLEN||stream->buffer==NULL) {
+        if(stream->next_frame!=NULL) {
+            rsz = stream->bufend-stream->next_frame;
+            memmove(strmbuff,stream->next_frame,rsz);
+            bp = strmbuff+rsz;
+        }
+    }
+
+    if (feof(fp)) {
+        if (isEmpty()) {
+            return MAD_FLOW_STOP;
+        } else {
+            return MAD_FLOW_CONTINUE;
+        }
+    }
+
+    led4 = 1;
+    ret = fread(bp,1,sizeof(strmbuff) - rsz,fp);
+
+    if (!ret) {
+        DBG("input stop\r\n");
+        return MAD_FLOW_STOP;
+    }
+
+    mad_stream_buffer(stream, strmbuff, ret + rsz);
+
+    return MAD_FLOW_CONTINUE;
+}
+
+
+/*
+ * The following utility routine performs simple rounding, clipping, and
+ * scaling of MAD's high-resolution samples down to 16 bits. It does not
+ * perform any dithering or noise shaping, which would be recommended to
+ * obtain any exceptional audio quality. It is therefore not recommended to
+ * use this routine if high-quality output is desired.
+ */
+
+static /*inline*/
+signed int scale(mad_fixed_t sample)
+{
+    /* round */
+    sample += (1L << (MAD_F_FRACBITS - 16));
+
+    /* clip */
+    if (sample >= MAD_F_ONE)
+        sample = MAD_F_ONE - 1;
+    else if (sample < -MAD_F_ONE)
+        sample = -MAD_F_ONE;
+
+    /* quantize */
+    return sample >> (MAD_F_FRACBITS + 1 - 16);
+}
+
+/*
+ * This is the output callback function. It is called after each frame of
+ * MPEG audio data has been completely decoded. The purpose of this callback
+ * is to output (or play) the decoded PCM audio.
+ */
+
+enum mad_flow output(void *data,
+                     struct mad_header const *header,
+                     struct mad_pcm *pcm)
+{
+    unsigned int nchannels, nsamples;
+    mad_fixed_t const *left_ch, *right_ch;
+
+    /* pcm->samplerate contains the sampling frequency */
+    nchannels = pcm->channels;
+    nsamples  = pcm->length;
+    left_ch   = pcm->samples[0];
+    right_ch  = pcm->samples[1];
+
+    poll();
+    while (nsamples--) {
+        while (isFull() || available() >= (44100 * FWDBUF)) {
+            poll();
+            if (cmd_stop) break;
+        }
+        __disable_irq();
+        dacbuf[dac_w].l = scale(*left_ch);
+        dacbuf[dac_w].r = scale(*right_ch);
+        dac_w = (dac_w + 1) % DACBUF_SIZE;
+        if (dac_w == 0 || dac_l) dac_l ++;
+        __enable_irq();
+        left_ch++;
+        right_ch++;
+    }
+
+    if (cmd_stop) {
+        DBG("output stop o\r\n");
+        cmd_stop = 0;
+        return MAD_FLOW_STOP;
+    }
+    return MAD_FLOW_CONTINUE;
+}
+
+/*
+ * This is the error callback function. It is called whenever a decoding
+ * error occurs. The error is indicated by stream->error; the list of
+ * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
+ * header file.
+ */
+
+enum mad_flow error_fn(void *data,
+                       struct mad_stream *stream,
+                       struct mad_frame *frame)
+{
+    /* ID3 tags will cause warnings and short noise, ignore it for the moment*/
+    /*
+      fprintf(stderr, "decoding error 0x%04x (%s)\n",
+          stream->error, mad_stream_errorstr(stream));
+    */
+
+    /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
+
+    return MAD_FLOW_CONTINUE;
+}
+