streo mp3 player see: http://mbed.org/users/okini3939/notebook/I2S_AUDIO
Dependencies: FatFileSystemCpp I2SSlave TLV320 mbed
Fork of madplayer by
main.cpp@2:f28cf0afd021, 2010-11-27 (annotated)
- Committer:
- Gruenfrosch
- Date:
- Sat Nov 27 17:27:33 2010 +0000
- Revision:
- 2:f28cf0afd021
- Parent:
- 0:7627c79db971
- Child:
- 3:6f07b5f52c38
Version 3:
* moved another memory block into AHB RAM, giving more room for
* stereo buffer.
* moved content of decode() to main()
* decoding is now safe to be called multiple times (bug in older versions)
* Output routine now fills stereo buffer, DAC output sums channels,
* just for demonstration that stereo output could go here
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 | 2:f28cf0afd021 | 31 | * *** Version 3: *** |
Gruenfrosch | 2:f28cf0afd021 | 32 | * moved another memory block into AHB RAM, giving more room for |
Gruenfrosch | 2:f28cf0afd021 | 33 | * stereo buffer. |
Gruenfrosch | 2:f28cf0afd021 | 34 | * moved content of decode() to main() |
Gruenfrosch | 2:f28cf0afd021 | 35 | * decoding is now safe to be called multiple times (bug in older versions) |
Gruenfrosch | 2:f28cf0afd021 | 36 | * Output routine now fills stereo buffer, DAC output sums channels, |
Gruenfrosch | 2:f28cf0afd021 | 37 | * just for demonstration that stereo output could go here |
Gruenfrosch | 0:7627c79db971 | 38 | */ |
Gruenfrosch | 0:7627c79db971 | 39 | |
Gruenfrosch | 0:7627c79db971 | 40 | #include "mbed.h" |
Gruenfrosch | 0:7627c79db971 | 41 | # include "decoder.h" |
Gruenfrosch | 0:7627c79db971 | 42 | |
Gruenfrosch | 0:7627c79db971 | 43 | FILE *fp; |
Gruenfrosch | 0:7627c79db971 | 44 | #include "MSCFileSystem.h" |
Gruenfrosch | 0:7627c79db971 | 45 | MSCFileSystem fs("usb"); |
Gruenfrosch | 0:7627c79db971 | 46 | |
Gruenfrosch | 2:f28cf0afd021 | 47 | static enum mad_flow input(void *data,struct mad_stream *stream); |
Gruenfrosch | 2:f28cf0afd021 | 48 | static enum mad_flow output(void *data,struct mad_header const *header,struct mad_pcm *pcm); |
Gruenfrosch | 2:f28cf0afd021 | 49 | static enum mad_flow error_fn(void *data,struct mad_stream *stream,struct mad_frame *frame); |
Gruenfrosch | 2:f28cf0afd021 | 50 | |
Gruenfrosch | 2:f28cf0afd021 | 51 | struct dacout_s { |
Gruenfrosch | 2:f28cf0afd021 | 52 | unsigned short l; |
Gruenfrosch | 2:f28cf0afd021 | 53 | unsigned short r; |
Gruenfrosch | 2:f28cf0afd021 | 54 | }; |
Gruenfrosch | 2:f28cf0afd021 | 55 | |
Gruenfrosch | 2:f28cf0afd021 | 56 | volatile dacout_s dacbuf[1152]; |
Gruenfrosch | 2:f28cf0afd021 | 57 | volatile dacout_s *dac_s, *dac_e; |
Gruenfrosch | 0:7627c79db971 | 58 | |
Gruenfrosch | 0:7627c79db971 | 59 | AnalogOut dac(p18); |
Gruenfrosch | 0:7627c79db971 | 60 | Ticker dacclk; |
Gruenfrosch | 0:7627c79db971 | 61 | |
Gruenfrosch | 0:7627c79db971 | 62 | void dacout(void) |
Gruenfrosch | 0:7627c79db971 | 63 | { |
Gruenfrosch | 0:7627c79db971 | 64 | if(dac_s < dac_e) |
Gruenfrosch | 0:7627c79db971 | 65 | { |
Gruenfrosch | 2:f28cf0afd021 | 66 | dac.write_u16((dac_s->l/2)+(dac_s->r/2)); |
Gruenfrosch | 2:f28cf0afd021 | 67 | dac_s++; |
Gruenfrosch | 0:7627c79db971 | 68 | } |
Gruenfrosch | 0:7627c79db971 | 69 | } |
Gruenfrosch | 0:7627c79db971 | 70 | |
Gruenfrosch | 0:7627c79db971 | 71 | int main(int argc, char *argv[]) |
Gruenfrosch | 0:7627c79db971 | 72 | { |
Gruenfrosch | 2:f28cf0afd021 | 73 | int result; |
Gruenfrosch | 0:7627c79db971 | 74 | Timer t; |
Gruenfrosch | 2:f28cf0afd021 | 75 | struct mad_decoder decoder; |
Gruenfrosch | 0:7627c79db971 | 76 | |
Gruenfrosch | 0:7627c79db971 | 77 | dac_s = dac_e = dacbuf; |
Gruenfrosch | 0:7627c79db971 | 78 | dacclk.attach_us(dacout,23); |
Gruenfrosch | 2:f28cf0afd021 | 79 | while(1) { |
Gruenfrosch | 2:f28cf0afd021 | 80 | fp = fopen("/usb/test.mp3","rb"); |
Gruenfrosch | 2:f28cf0afd021 | 81 | |
Gruenfrosch | 2:f28cf0afd021 | 82 | if(!fp) return(printf("file error\r\n")); |
Gruenfrosch | 2:f28cf0afd021 | 83 | fprintf(stderr,"decode start\r\n"); |
Gruenfrosch | 2:f28cf0afd021 | 84 | mad_decoder_init(&decoder, NULL,input, 0, 0, output,error_fn, 0); |
Gruenfrosch | 2:f28cf0afd021 | 85 | t.reset(); |
Gruenfrosch | 2:f28cf0afd021 | 86 | t.start(); |
Gruenfrosch | 2:f28cf0afd021 | 87 | result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); |
Gruenfrosch | 2:f28cf0afd021 | 88 | t.stop(); |
Gruenfrosch | 2:f28cf0afd021 | 89 | fprintf(stderr,"decode ret=%d in %d ms\r\n",result,t.read_ms()); |
Gruenfrosch | 2:f28cf0afd021 | 90 | mad_decoder_finish(&decoder); |
Gruenfrosch | 2:f28cf0afd021 | 91 | fclose(fp); |
Gruenfrosch | 2:f28cf0afd021 | 92 | } |
Gruenfrosch | 0:7627c79db971 | 93 | return 0; |
Gruenfrosch | 0:7627c79db971 | 94 | } |
Gruenfrosch | 0:7627c79db971 | 95 | |
Gruenfrosch | 0:7627c79db971 | 96 | /* |
Gruenfrosch | 0:7627c79db971 | 97 | * This is the input callback. The purpose of this callback is to (re)fill |
Gruenfrosch | 0:7627c79db971 | 98 | * the stream buffer which is to be decoded. |
Gruenfrosch | 0:7627c79db971 | 99 | */ |
Gruenfrosch | 0:7627c79db971 | 100 | |
Gruenfrosch | 0:7627c79db971 | 101 | static |
Gruenfrosch | 0:7627c79db971 | 102 | enum mad_flow input(void *data, |
Gruenfrosch | 0:7627c79db971 | 103 | struct mad_stream *stream) |
Gruenfrosch | 0:7627c79db971 | 104 | { |
Gruenfrosch | 0:7627c79db971 | 105 | static unsigned char strmbuff[2100]; |
Gruenfrosch | 0:7627c79db971 | 106 | int ret; |
Gruenfrosch | 0:7627c79db971 | 107 | int rsz; |
Gruenfrosch | 0:7627c79db971 | 108 | unsigned char *bp; |
Gruenfrosch | 0:7627c79db971 | 109 | |
Gruenfrosch | 0:7627c79db971 | 110 | /* the remaining bytes from incomplete frames must be copied |
Gruenfrosch | 0:7627c79db971 | 111 | to the beginning of the new buffer ! |
Gruenfrosch | 0:7627c79db971 | 112 | */ |
Gruenfrosch | 0:7627c79db971 | 113 | bp = strmbuff; |
Gruenfrosch | 0:7627c79db971 | 114 | rsz = 0; |
Gruenfrosch | 0:7627c79db971 | 115 | if(stream->error == MAD_ERROR_BUFLEN||stream->buffer==NULL) |
Gruenfrosch | 0:7627c79db971 | 116 | { |
Gruenfrosch | 0:7627c79db971 | 117 | if(stream->next_frame!=NULL) |
Gruenfrosch | 0:7627c79db971 | 118 | { |
Gruenfrosch | 0:7627c79db971 | 119 | rsz = stream->bufend-stream->next_frame; |
Gruenfrosch | 0:7627c79db971 | 120 | memmove(strmbuff,stream->next_frame,rsz); |
Gruenfrosch | 0:7627c79db971 | 121 | bp = strmbuff+rsz; |
Gruenfrosch | 0:7627c79db971 | 122 | } |
Gruenfrosch | 0:7627c79db971 | 123 | } |
Gruenfrosch | 0:7627c79db971 | 124 | |
Gruenfrosch | 0:7627c79db971 | 125 | ret = fread(bp,1,sizeof(strmbuff) - rsz,fp); |
Gruenfrosch | 0:7627c79db971 | 126 | |
Gruenfrosch | 0:7627c79db971 | 127 | if (!ret) |
Gruenfrosch | 0:7627c79db971 | 128 | return MAD_FLOW_STOP; |
Gruenfrosch | 0:7627c79db971 | 129 | |
Gruenfrosch | 0:7627c79db971 | 130 | |
Gruenfrosch | 0:7627c79db971 | 131 | mad_stream_buffer(stream, strmbuff, ret + rsz); |
Gruenfrosch | 0:7627c79db971 | 132 | |
Gruenfrosch | 0:7627c79db971 | 133 | return MAD_FLOW_CONTINUE;} |
Gruenfrosch | 0:7627c79db971 | 134 | |
Gruenfrosch | 0:7627c79db971 | 135 | |
Gruenfrosch | 0:7627c79db971 | 136 | /* |
Gruenfrosch | 0:7627c79db971 | 137 | * The following utility routine performs simple rounding, clipping, and |
Gruenfrosch | 0:7627c79db971 | 138 | * scaling of MAD's high-resolution samples down to 16 bits. It does not |
Gruenfrosch | 0:7627c79db971 | 139 | * perform any dithering or noise shaping, which would be recommended to |
Gruenfrosch | 0:7627c79db971 | 140 | * obtain any exceptional audio quality. It is therefore not recommended to |
Gruenfrosch | 0:7627c79db971 | 141 | * use this routine if high-quality output is desired. |
Gruenfrosch | 0:7627c79db971 | 142 | */ |
Gruenfrosch | 0:7627c79db971 | 143 | |
Gruenfrosch | 0:7627c79db971 | 144 | static /*inline*/ |
Gruenfrosch | 0:7627c79db971 | 145 | signed int scale(mad_fixed_t sample) |
Gruenfrosch | 0:7627c79db971 | 146 | { |
Gruenfrosch | 0:7627c79db971 | 147 | /* round */ |
Gruenfrosch | 0:7627c79db971 | 148 | sample += (1L << (MAD_F_FRACBITS - 16)); |
Gruenfrosch | 0:7627c79db971 | 149 | |
Gruenfrosch | 0:7627c79db971 | 150 | /* clip */ |
Gruenfrosch | 0:7627c79db971 | 151 | if (sample >= MAD_F_ONE) |
Gruenfrosch | 0:7627c79db971 | 152 | sample = MAD_F_ONE - 1; |
Gruenfrosch | 0:7627c79db971 | 153 | else if (sample < -MAD_F_ONE) |
Gruenfrosch | 0:7627c79db971 | 154 | sample = -MAD_F_ONE; |
Gruenfrosch | 0:7627c79db971 | 155 | |
Gruenfrosch | 0:7627c79db971 | 156 | /* quantize */ |
Gruenfrosch | 0:7627c79db971 | 157 | return sample >> (MAD_F_FRACBITS + 1 - 16); |
Gruenfrosch | 0:7627c79db971 | 158 | } |
Gruenfrosch | 0:7627c79db971 | 159 | |
Gruenfrosch | 0:7627c79db971 | 160 | /* |
Gruenfrosch | 0:7627c79db971 | 161 | * This is the output callback function. It is called after each frame of |
Gruenfrosch | 0:7627c79db971 | 162 | * MPEG audio data has been completely decoded. The purpose of this callback |
Gruenfrosch | 0:7627c79db971 | 163 | * is to output (or play) the decoded PCM audio. |
Gruenfrosch | 0:7627c79db971 | 164 | */ |
Gruenfrosch | 0:7627c79db971 | 165 | |
Gruenfrosch | 0:7627c79db971 | 166 | static |
Gruenfrosch | 0:7627c79db971 | 167 | enum mad_flow output(void *data, |
Gruenfrosch | 0:7627c79db971 | 168 | struct mad_header const *header, |
Gruenfrosch | 0:7627c79db971 | 169 | struct mad_pcm *pcm) |
Gruenfrosch | 0:7627c79db971 | 170 | { |
Gruenfrosch | 0:7627c79db971 | 171 | unsigned int nchannels, nsamples; |
Gruenfrosch | 0:7627c79db971 | 172 | mad_fixed_t const *left_ch, *right_ch; |
Gruenfrosch | 0:7627c79db971 | 173 | |
Gruenfrosch | 2:f28cf0afd021 | 174 | |
Gruenfrosch | 0:7627c79db971 | 175 | /* pcm->samplerate contains the sampling frequency */ |
Gruenfrosch | 0:7627c79db971 | 176 | nchannels = pcm->channels; |
Gruenfrosch | 0:7627c79db971 | 177 | nsamples = pcm->length; |
Gruenfrosch | 0:7627c79db971 | 178 | left_ch = pcm->samples[0]; |
Gruenfrosch | 0:7627c79db971 | 179 | right_ch = pcm->samples[1]; |
Gruenfrosch | 2:f28cf0afd021 | 180 | |
Gruenfrosch | 2:f28cf0afd021 | 181 | while(dac_s < dac_e) wait_us(1); |
Gruenfrosch | 2:f28cf0afd021 | 182 | dac_e = dacbuf; // potential thread problem ?? no... |
Gruenfrosch | 2:f28cf0afd021 | 183 | dac_s = dacbuf; |
Gruenfrosch | 0:7627c79db971 | 184 | |
Gruenfrosch | 0:7627c79db971 | 185 | while (nsamples--) { |
Gruenfrosch | 2:f28cf0afd021 | 186 | signed int sample_l,sample_r; |
Gruenfrosch | 2:f28cf0afd021 | 187 | sample_l = scale(*left_ch); |
Gruenfrosch | 2:f28cf0afd021 | 188 | sample_r = scale(*right_ch); |
Gruenfrosch | 2:f28cf0afd021 | 189 | dac_e->l = sample_l +32768; |
Gruenfrosch | 2:f28cf0afd021 | 190 | dac_e->r = sample_r +32768; |
Gruenfrosch | 2:f28cf0afd021 | 191 | dac_e++; |
Gruenfrosch | 2:f28cf0afd021 | 192 | left_ch++; |
Gruenfrosch | 2:f28cf0afd021 | 193 | right_ch++; |
Gruenfrosch | 0:7627c79db971 | 194 | } |
Gruenfrosch | 0:7627c79db971 | 195 | return MAD_FLOW_CONTINUE; |
Gruenfrosch | 0:7627c79db971 | 196 | } |
Gruenfrosch | 0:7627c79db971 | 197 | |
Gruenfrosch | 0:7627c79db971 | 198 | /* |
Gruenfrosch | 0:7627c79db971 | 199 | * This is the error callback function. It is called whenever a decoding |
Gruenfrosch | 0:7627c79db971 | 200 | * error occurs. The error is indicated by stream->error; the list of |
Gruenfrosch | 0:7627c79db971 | 201 | * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h) |
Gruenfrosch | 0:7627c79db971 | 202 | * header file. |
Gruenfrosch | 0:7627c79db971 | 203 | */ |
Gruenfrosch | 0:7627c79db971 | 204 | |
Gruenfrosch | 0:7627c79db971 | 205 | static |
Gruenfrosch | 0:7627c79db971 | 206 | enum mad_flow error_fn(void *data, |
Gruenfrosch | 0:7627c79db971 | 207 | struct mad_stream *stream, |
Gruenfrosch | 0:7627c79db971 | 208 | struct mad_frame *frame) |
Gruenfrosch | 0:7627c79db971 | 209 | { |
Gruenfrosch | 0:7627c79db971 | 210 | /* ID3 tags will cause warnings and short noise, ignore it for the moment*/ |
Gruenfrosch | 0:7627c79db971 | 211 | |
Gruenfrosch | 0:7627c79db971 | 212 | fprintf(stderr, "decoding error 0x%04x (%s)\n", |
Gruenfrosch | 0:7627c79db971 | 213 | stream->error, mad_stream_errorstr(stream)); |
Gruenfrosch | 0:7627c79db971 | 214 | |
Gruenfrosch | 0:7627c79db971 | 215 | |
Gruenfrosch | 0:7627c79db971 | 216 | /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */ |
Gruenfrosch | 0:7627c79db971 | 217 | |
Gruenfrosch | 0:7627c79db971 | 218 | return MAD_FLOW_CONTINUE; |
Gruenfrosch | 0:7627c79db971 | 219 | } |
Gruenfrosch | 0:7627c79db971 | 220 | |
Gruenfrosch | 0:7627c79db971 | 221 |