MP3 Player without external hardware MP3 Player without external hardware. A software based MP3 player based on a modified version of libmad. Mono output (at the moment) via AnalogOut. Files are read from an USB drive. This is a demo program, it plays only one file at the moment. Documentation is in "main.cpp" and "config.h"
main.cpp@0:7627c79db971, 2010-11-26 (annotated)
- Committer:
- Gruenfrosch
- Date:
- Fri Nov 26 12:18:30 2010 +0000
- Revision:
- 0:7627c79db971
- Child:
- 2:f28cf0afd021
First Version
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Gruenfrosch | 0:7627c79db971 | 1 | /* This file demonstrates the use of the modified libmad library on LPC1768 |
Gruenfrosch | 0:7627c79db971 | 2 | * Changes to the library are documented in config.h. |
Gruenfrosch | 0:7627c79db971 | 3 | * |
Gruenfrosch | 0:7627c79db971 | 4 | * The main change is to use parts of the AHB RAM dedicated to the ethernet module, |
Gruenfrosch | 0:7627c79db971 | 5 | * because standard RAM is not sufficient for decoding. |
Gruenfrosch | 0:7627c79db971 | 6 | * This means the ethernet module cannot be used !!! |
Gruenfrosch | 0:7627c79db971 | 7 | * |
Gruenfrosch | 0:7627c79db971 | 8 | * It plays a file "test.mp3" from an external USB-drive/USB-stick. |
Gruenfrosch | 0:7627c79db971 | 9 | * For wiring of the USB-connector, see mbed.org |
Gruenfrosch | 0:7627c79db971 | 10 | * ID3 decoding is not present at the moment and will cause warnings |
Gruenfrosch | 0:7627c79db971 | 11 | * on stderr, and some short noise at the beginning or end of playback. |
Gruenfrosch | 0:7627c79db971 | 12 | * |
Gruenfrosch | 0:7627c79db971 | 13 | * Output is only for one channel on the DAC (AnalogOut) pin. |
Gruenfrosch | 0:7627c79db971 | 14 | * (For connections see datasheets/mbed.org) |
Gruenfrosch | 0:7627c79db971 | 15 | * This pin should be decoupled with a capacitor (100u or so) to remove DC. |
Gruenfrosch | 0:7627c79db971 | 16 | * The output current is high enough to drive small headphones or active |
Gruenfrosch | 0:7627c79db971 | 17 | * speakers directly. |
Gruenfrosch | 0:7627c79db971 | 18 | * |
Gruenfrosch | 0:7627c79db971 | 19 | * Schematic: :-) |
Gruenfrosch | 0:7627c79db971 | 20 | * MBED Pin 18 (AOut) o--||--o Headphone Left |
Gruenfrosch | 0:7627c79db971 | 21 | * MBED Pin 1 (GND) o------o Headphone Common |
Gruenfrosch | 0:7627c79db971 | 22 | * |
Gruenfrosch | 0:7627c79db971 | 23 | * It has been tested with fixed bitrate MP3's up to 320kbps and VBR files. |
Gruenfrosch | 0:7627c79db971 | 24 | * |
Gruenfrosch | 0:7627c79db971 | 25 | * The remaining RAM is very limited, so don't overuse it ! |
Gruenfrosch | 0:7627c79db971 | 26 | * The MSCFileSystem library from mbed.org is needed ! |
Gruenfrosch | 0:7627c79db971 | 27 | * Last warning: the main include file "mad.h" maybe not up to date, |
Gruenfrosch | 0:7627c79db971 | 28 | * use "decoder.h" for now |
Gruenfrosch | 0:7627c79db971 | 29 | * Have fun, |
Gruenfrosch | 0:7627c79db971 | 30 | * Andreas Gruen |
Gruenfrosch | 0:7627c79db971 | 31 | */ |
Gruenfrosch | 0:7627c79db971 | 32 | |
Gruenfrosch | 0:7627c79db971 | 33 | #include "mbed.h" |
Gruenfrosch | 0:7627c79db971 | 34 | # include "decoder.h" |
Gruenfrosch | 0:7627c79db971 | 35 | |
Gruenfrosch | 0:7627c79db971 | 36 | static int decode(void); |
Gruenfrosch | 0:7627c79db971 | 37 | FILE *fp; |
Gruenfrosch | 0:7627c79db971 | 38 | #include "MSCFileSystem.h" |
Gruenfrosch | 0:7627c79db971 | 39 | MSCFileSystem fs("usb"); |
Gruenfrosch | 0:7627c79db971 | 40 | |
Gruenfrosch | 0:7627c79db971 | 41 | volatile unsigned short dacbuf[1200]; |
Gruenfrosch | 0:7627c79db971 | 42 | volatile unsigned short *dac_s, *dac_e; |
Gruenfrosch | 0:7627c79db971 | 43 | |
Gruenfrosch | 0:7627c79db971 | 44 | AnalogOut dac(p18); |
Gruenfrosch | 0:7627c79db971 | 45 | Ticker dacclk; |
Gruenfrosch | 0:7627c79db971 | 46 | |
Gruenfrosch | 0:7627c79db971 | 47 | void dacout(void) |
Gruenfrosch | 0:7627c79db971 | 48 | { |
Gruenfrosch | 0:7627c79db971 | 49 | if(dac_s < dac_e) |
Gruenfrosch | 0:7627c79db971 | 50 | { |
Gruenfrosch | 0:7627c79db971 | 51 | dac.write_u16(*dac_s++); |
Gruenfrosch | 0:7627c79db971 | 52 | } |
Gruenfrosch | 0:7627c79db971 | 53 | } |
Gruenfrosch | 0:7627c79db971 | 54 | |
Gruenfrosch | 0:7627c79db971 | 55 | int main(int argc, char *argv[]) |
Gruenfrosch | 0:7627c79db971 | 56 | { |
Gruenfrosch | 0:7627c79db971 | 57 | int ret; |
Gruenfrosch | 0:7627c79db971 | 58 | Timer t; |
Gruenfrosch | 0:7627c79db971 | 59 | |
Gruenfrosch | 0:7627c79db971 | 60 | dac_s = dac_e = dacbuf; |
Gruenfrosch | 0:7627c79db971 | 61 | dacclk.attach_us(dacout,23); |
Gruenfrosch | 0:7627c79db971 | 62 | |
Gruenfrosch | 0:7627c79db971 | 63 | fp = fopen("/usb/test.mp3","rb"); |
Gruenfrosch | 0:7627c79db971 | 64 | if(!fp) return(printf("no file\r\n")); |
Gruenfrosch | 0:7627c79db971 | 65 | t.start(); |
Gruenfrosch | 0:7627c79db971 | 66 | ret = decode(); |
Gruenfrosch | 0:7627c79db971 | 67 | t.stop(); |
Gruenfrosch | 0:7627c79db971 | 68 | printf("decode ret=%d in %d ms\r\n",ret,t.read_ms()); |
Gruenfrosch | 0:7627c79db971 | 69 | fclose(fp); |
Gruenfrosch | 0:7627c79db971 | 70 | |
Gruenfrosch | 0:7627c79db971 | 71 | return 0; |
Gruenfrosch | 0:7627c79db971 | 72 | } |
Gruenfrosch | 0:7627c79db971 | 73 | |
Gruenfrosch | 0:7627c79db971 | 74 | /* |
Gruenfrosch | 0:7627c79db971 | 75 | * This is the input callback. The purpose of this callback is to (re)fill |
Gruenfrosch | 0:7627c79db971 | 76 | * the stream buffer which is to be decoded. |
Gruenfrosch | 0:7627c79db971 | 77 | */ |
Gruenfrosch | 0:7627c79db971 | 78 | |
Gruenfrosch | 0:7627c79db971 | 79 | static |
Gruenfrosch | 0:7627c79db971 | 80 | enum mad_flow input(void *data, |
Gruenfrosch | 0:7627c79db971 | 81 | struct mad_stream *stream) |
Gruenfrosch | 0:7627c79db971 | 82 | { |
Gruenfrosch | 0:7627c79db971 | 83 | static unsigned char strmbuff[2100]; |
Gruenfrosch | 0:7627c79db971 | 84 | int ret; |
Gruenfrosch | 0:7627c79db971 | 85 | int rsz; |
Gruenfrosch | 0:7627c79db971 | 86 | unsigned char *bp; |
Gruenfrosch | 0:7627c79db971 | 87 | |
Gruenfrosch | 0:7627c79db971 | 88 | /* the remaining bytes from incomplete frames must be copied |
Gruenfrosch | 0:7627c79db971 | 89 | to the beginning of the new buffer ! |
Gruenfrosch | 0:7627c79db971 | 90 | */ |
Gruenfrosch | 0:7627c79db971 | 91 | bp = strmbuff; |
Gruenfrosch | 0:7627c79db971 | 92 | rsz = 0; |
Gruenfrosch | 0:7627c79db971 | 93 | if(stream->error == MAD_ERROR_BUFLEN||stream->buffer==NULL) |
Gruenfrosch | 0:7627c79db971 | 94 | { |
Gruenfrosch | 0:7627c79db971 | 95 | if(stream->next_frame!=NULL) |
Gruenfrosch | 0:7627c79db971 | 96 | { |
Gruenfrosch | 0:7627c79db971 | 97 | rsz = stream->bufend-stream->next_frame; |
Gruenfrosch | 0:7627c79db971 | 98 | memmove(strmbuff,stream->next_frame,rsz); |
Gruenfrosch | 0:7627c79db971 | 99 | bp = strmbuff+rsz; |
Gruenfrosch | 0:7627c79db971 | 100 | } |
Gruenfrosch | 0:7627c79db971 | 101 | } |
Gruenfrosch | 0:7627c79db971 | 102 | |
Gruenfrosch | 0:7627c79db971 | 103 | ret = fread(bp,1,sizeof(strmbuff) - rsz,fp); |
Gruenfrosch | 0:7627c79db971 | 104 | |
Gruenfrosch | 0:7627c79db971 | 105 | if (!ret) |
Gruenfrosch | 0:7627c79db971 | 106 | return MAD_FLOW_STOP; |
Gruenfrosch | 0:7627c79db971 | 107 | |
Gruenfrosch | 0:7627c79db971 | 108 | |
Gruenfrosch | 0:7627c79db971 | 109 | mad_stream_buffer(stream, strmbuff, ret + rsz); |
Gruenfrosch | 0:7627c79db971 | 110 | |
Gruenfrosch | 0:7627c79db971 | 111 | return MAD_FLOW_CONTINUE;} |
Gruenfrosch | 0:7627c79db971 | 112 | |
Gruenfrosch | 0:7627c79db971 | 113 | |
Gruenfrosch | 0:7627c79db971 | 114 | /* |
Gruenfrosch | 0:7627c79db971 | 115 | * The following utility routine performs simple rounding, clipping, and |
Gruenfrosch | 0:7627c79db971 | 116 | * scaling of MAD's high-resolution samples down to 16 bits. It does not |
Gruenfrosch | 0:7627c79db971 | 117 | * perform any dithering or noise shaping, which would be recommended to |
Gruenfrosch | 0:7627c79db971 | 118 | * obtain any exceptional audio quality. It is therefore not recommended to |
Gruenfrosch | 0:7627c79db971 | 119 | * use this routine if high-quality output is desired. |
Gruenfrosch | 0:7627c79db971 | 120 | */ |
Gruenfrosch | 0:7627c79db971 | 121 | |
Gruenfrosch | 0:7627c79db971 | 122 | static /*inline*/ |
Gruenfrosch | 0:7627c79db971 | 123 | signed int scale(mad_fixed_t sample) |
Gruenfrosch | 0:7627c79db971 | 124 | { |
Gruenfrosch | 0:7627c79db971 | 125 | /* round */ |
Gruenfrosch | 0:7627c79db971 | 126 | sample += (1L << (MAD_F_FRACBITS - 16)); |
Gruenfrosch | 0:7627c79db971 | 127 | |
Gruenfrosch | 0:7627c79db971 | 128 | /* clip */ |
Gruenfrosch | 0:7627c79db971 | 129 | if (sample >= MAD_F_ONE) |
Gruenfrosch | 0:7627c79db971 | 130 | sample = MAD_F_ONE - 1; |
Gruenfrosch | 0:7627c79db971 | 131 | else if (sample < -MAD_F_ONE) |
Gruenfrosch | 0:7627c79db971 | 132 | sample = -MAD_F_ONE; |
Gruenfrosch | 0:7627c79db971 | 133 | |
Gruenfrosch | 0:7627c79db971 | 134 | /* quantize */ |
Gruenfrosch | 0:7627c79db971 | 135 | return sample >> (MAD_F_FRACBITS + 1 - 16); |
Gruenfrosch | 0:7627c79db971 | 136 | } |
Gruenfrosch | 0:7627c79db971 | 137 | |
Gruenfrosch | 0:7627c79db971 | 138 | /* |
Gruenfrosch | 0:7627c79db971 | 139 | * This is the output callback function. It is called after each frame of |
Gruenfrosch | 0:7627c79db971 | 140 | * MPEG audio data has been completely decoded. The purpose of this callback |
Gruenfrosch | 0:7627c79db971 | 141 | * is to output (or play) the decoded PCM audio. |
Gruenfrosch | 0:7627c79db971 | 142 | */ |
Gruenfrosch | 0:7627c79db971 | 143 | |
Gruenfrosch | 0:7627c79db971 | 144 | static |
Gruenfrosch | 0:7627c79db971 | 145 | enum mad_flow output(void *data, |
Gruenfrosch | 0:7627c79db971 | 146 | struct mad_header const *header, |
Gruenfrosch | 0:7627c79db971 | 147 | struct mad_pcm *pcm) |
Gruenfrosch | 0:7627c79db971 | 148 | { |
Gruenfrosch | 0:7627c79db971 | 149 | unsigned int nchannels, nsamples; |
Gruenfrosch | 0:7627c79db971 | 150 | mad_fixed_t const *left_ch, *right_ch; |
Gruenfrosch | 0:7627c79db971 | 151 | |
Gruenfrosch | 0:7627c79db971 | 152 | /* pcm->samplerate contains the sampling frequency */ |
Gruenfrosch | 0:7627c79db971 | 153 | nchannels = pcm->channels; |
Gruenfrosch | 0:7627c79db971 | 154 | nsamples = pcm->length; |
Gruenfrosch | 0:7627c79db971 | 155 | left_ch = pcm->samples[0]; |
Gruenfrosch | 0:7627c79db971 | 156 | right_ch = pcm->samples[1]; |
Gruenfrosch | 0:7627c79db971 | 157 | |
Gruenfrosch | 0:7627c79db971 | 158 | while(dac_s < dac_e) wait_us(10); |
Gruenfrosch | 0:7627c79db971 | 159 | dac_e = dacbuf; // potential thread problem ?? |
Gruenfrosch | 0:7627c79db971 | 160 | dac_s = dacbuf; |
Gruenfrosch | 0:7627c79db971 | 161 | |
Gruenfrosch | 0:7627c79db971 | 162 | while (nsamples--) { |
Gruenfrosch | 0:7627c79db971 | 163 | signed int sample; |
Gruenfrosch | 0:7627c79db971 | 164 | |
Gruenfrosch | 0:7627c79db971 | 165 | /* output sample(s) in 16-bit signed little-endian PCM */ |
Gruenfrosch | 0:7627c79db971 | 166 | |
Gruenfrosch | 0:7627c79db971 | 167 | sample = scale(*left_ch++); |
Gruenfrosch | 0:7627c79db971 | 168 | *dac_e++ = sample +32700; |
Gruenfrosch | 0:7627c79db971 | 169 | //putchar((sample >> 0) & 0xff); |
Gruenfrosch | 0:7627c79db971 | 170 | //putchar((sample >> 8) & 0xff); |
Gruenfrosch | 0:7627c79db971 | 171 | /* the second channel is not supported at the moment*/ |
Gruenfrosch | 0:7627c79db971 | 172 | if (nchannels == 2) { |
Gruenfrosch | 0:7627c79db971 | 173 | sample = scale(*right_ch++); |
Gruenfrosch | 0:7627c79db971 | 174 | //putchar((sample >> 0) & 0xff); |
Gruenfrosch | 0:7627c79db971 | 175 | //putchar((sample >> 8) & 0xff); |
Gruenfrosch | 0:7627c79db971 | 176 | } |
Gruenfrosch | 0:7627c79db971 | 177 | } |
Gruenfrosch | 0:7627c79db971 | 178 | |
Gruenfrosch | 0:7627c79db971 | 179 | return MAD_FLOW_CONTINUE; |
Gruenfrosch | 0:7627c79db971 | 180 | } |
Gruenfrosch | 0:7627c79db971 | 181 | |
Gruenfrosch | 0:7627c79db971 | 182 | /* |
Gruenfrosch | 0:7627c79db971 | 183 | * This is the error callback function. It is called whenever a decoding |
Gruenfrosch | 0:7627c79db971 | 184 | * error occurs. The error is indicated by stream->error; the list of |
Gruenfrosch | 0:7627c79db971 | 185 | * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h) |
Gruenfrosch | 0:7627c79db971 | 186 | * header file. |
Gruenfrosch | 0:7627c79db971 | 187 | */ |
Gruenfrosch | 0:7627c79db971 | 188 | |
Gruenfrosch | 0:7627c79db971 | 189 | static |
Gruenfrosch | 0:7627c79db971 | 190 | enum mad_flow error_fn(void *data, |
Gruenfrosch | 0:7627c79db971 | 191 | struct mad_stream *stream, |
Gruenfrosch | 0:7627c79db971 | 192 | struct mad_frame *frame) |
Gruenfrosch | 0:7627c79db971 | 193 | { |
Gruenfrosch | 0:7627c79db971 | 194 | /* ID3 tags will cause warnings and short noise, ignore it for the moment*/ |
Gruenfrosch | 0:7627c79db971 | 195 | |
Gruenfrosch | 0:7627c79db971 | 196 | fprintf(stderr, "decoding error 0x%04x (%s)\n", |
Gruenfrosch | 0:7627c79db971 | 197 | stream->error, mad_stream_errorstr(stream)); |
Gruenfrosch | 0:7627c79db971 | 198 | |
Gruenfrosch | 0:7627c79db971 | 199 | |
Gruenfrosch | 0:7627c79db971 | 200 | /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */ |
Gruenfrosch | 0:7627c79db971 | 201 | |
Gruenfrosch | 0:7627c79db971 | 202 | return MAD_FLOW_CONTINUE; |
Gruenfrosch | 0:7627c79db971 | 203 | } |
Gruenfrosch | 0:7627c79db971 | 204 | |
Gruenfrosch | 0:7627c79db971 | 205 | /* |
Gruenfrosch | 0:7627c79db971 | 206 | * This is the function called by main() above to perform all the decoding. |
Gruenfrosch | 0:7627c79db971 | 207 | * It instantiates a decoder object and configures it with the input, |
Gruenfrosch | 0:7627c79db971 | 208 | * output, and error callback functions above. A single call to |
Gruenfrosch | 0:7627c79db971 | 209 | * mad_decoder_run() continues until a callback function returns |
Gruenfrosch | 0:7627c79db971 | 210 | * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and |
Gruenfrosch | 0:7627c79db971 | 211 | * signal an error). |
Gruenfrosch | 0:7627c79db971 | 212 | */ |
Gruenfrosch | 0:7627c79db971 | 213 | |
Gruenfrosch | 0:7627c79db971 | 214 | static |
Gruenfrosch | 0:7627c79db971 | 215 | int decode() |
Gruenfrosch | 0:7627c79db971 | 216 | { |
Gruenfrosch | 0:7627c79db971 | 217 | struct mad_decoder decoder; |
Gruenfrosch | 0:7627c79db971 | 218 | int result; |
Gruenfrosch | 0:7627c79db971 | 219 | |
Gruenfrosch | 0:7627c79db971 | 220 | /* configure input, output, and error functions */ |
Gruenfrosch | 0:7627c79db971 | 221 | |
Gruenfrosch | 0:7627c79db971 | 222 | mad_decoder_init(&decoder, NULL, |
Gruenfrosch | 0:7627c79db971 | 223 | input, 0 /* header */, 0 /* filter */, output, |
Gruenfrosch | 0:7627c79db971 | 224 | error_fn, 0 /* message */); |
Gruenfrosch | 0:7627c79db971 | 225 | |
Gruenfrosch | 0:7627c79db971 | 226 | /* start decoding */ |
Gruenfrosch | 0:7627c79db971 | 227 | |
Gruenfrosch | 0:7627c79db971 | 228 | result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); |
Gruenfrosch | 0:7627c79db971 | 229 | |
Gruenfrosch | 0:7627c79db971 | 230 | /* release the decoder */ |
Gruenfrosch | 0:7627c79db971 | 231 | |
Gruenfrosch | 0:7627c79db971 | 232 | mad_decoder_finish(&decoder); |
Gruenfrosch | 0:7627c79db971 | 233 | |
Gruenfrosch | 0:7627c79db971 | 234 | return result; |
Gruenfrosch | 0:7627c79db971 | 235 | } |
Gruenfrosch | 0:7627c79db971 | 236 | |
Gruenfrosch | 0:7627c79db971 | 237 |