![](/media/cache/profiles/f095cedd23b99f1696fc8caecbcf257e.jpg.50x50_q85.jpg)
streo mp3 player see: http://mbed.org/users/okini3939/notebook/I2S_AUDIO
Dependencies: FatFileSystemCpp I2SSlave TLV320 mbed
Fork of madplayer by
main.cpp@4:30b2cf4a8ee2, 2012-12-19 (annotated)
- Committer:
- okini3939
- Date:
- Wed Dec 19 06:31:58 2012 +0000
- Revision:
- 4:30b2cf4a8ee2
- Parent:
- 3:6f07b5f52c38
- Child:
- 5:50015f4868e2
fix
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 | * |
okini3939 | 3:6f07b5f52c38 | 19 | * Schematic: :-) |
Gruenfrosch | 0:7627c79db971 | 20 | * MBED Pin 18 (AOut) o--||--o Headphone Left |
okini3939 | 3:6f07b5f52c38 | 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() |
okini3939 | 3:6f07b5f52c38 | 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" |
okini3939 | 3:6f07b5f52c38 | 41 | #include "decoder.h" |
okini3939 | 3:6f07b5f52c38 | 42 | #include "TLV320.h" |
Gruenfrosch | 0:7627c79db971 | 43 | |
okini3939 | 3:6f07b5f52c38 | 44 | DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4); |
okini3939 | 3:6f07b5f52c38 | 45 | Serial pc(USBTX, USBRX); |
Gruenfrosch | 0:7627c79db971 | 46 | FILE *fp; |
okini3939 | 3:6f07b5f52c38 | 47 | #include "SDHCFileSystem.h" |
okini3939 | 3:6f07b5f52c38 | 48 | SDFileSystem sd(p11, p12, p13, p14, "sd"); |
okini3939 | 3:6f07b5f52c38 | 49 | TLV320 audio(p9, p10, 0x34, p5, p6, p7, p8, p16); // I2S Codec |
Gruenfrosch | 0:7627c79db971 | 50 | |
Gruenfrosch | 2:f28cf0afd021 | 51 | static enum mad_flow input(void *data,struct mad_stream *stream); |
Gruenfrosch | 2:f28cf0afd021 | 52 | static enum mad_flow output(void *data,struct mad_header const *header,struct mad_pcm *pcm); |
Gruenfrosch | 2:f28cf0afd021 | 53 | static enum mad_flow error_fn(void *data,struct mad_stream *stream,struct mad_frame *frame); |
Gruenfrosch | 2:f28cf0afd021 | 54 | |
Gruenfrosch | 2:f28cf0afd021 | 55 | struct dacout_s { |
Gruenfrosch | 2:f28cf0afd021 | 56 | unsigned short l; |
Gruenfrosch | 2:f28cf0afd021 | 57 | unsigned short r; |
Gruenfrosch | 2:f28cf0afd021 | 58 | }; |
Gruenfrosch | 2:f28cf0afd021 | 59 | |
Gruenfrosch | 2:f28cf0afd021 | 60 | volatile dacout_s dacbuf[1152]; |
Gruenfrosch | 2:f28cf0afd021 | 61 | volatile dacout_s *dac_s, *dac_e; |
Gruenfrosch | 0:7627c79db971 | 62 | |
okini3939 | 3:6f07b5f52c38 | 63 | void isr_audio () { |
okini3939 | 3:6f07b5f52c38 | 64 | int i; |
okini3939 | 3:6f07b5f52c38 | 65 | static int buf[4] = {0,0,0,0}; |
okini3939 | 3:6f07b5f52c38 | 66 | |
okini3939 | 3:6f07b5f52c38 | 67 | for (i = 0; i < 4; i ++) { |
okini3939 | 3:6f07b5f52c38 | 68 | if (dac_s < dac_e) { |
okini3939 | 4:30b2cf4a8ee2 | 69 | buf[i] = (dac_s->l << 16) | dac_s->r; |
okini3939 | 3:6f07b5f52c38 | 70 | dac_s++; |
okini3939 | 3:6f07b5f52c38 | 71 | led3 = !led3; |
okini3939 | 3:6f07b5f52c38 | 72 | } else { |
okini3939 | 3:6f07b5f52c38 | 73 | if (i) { |
okini3939 | 3:6f07b5f52c38 | 74 | buf[i] = buf[i - 1]; |
okini3939 | 3:6f07b5f52c38 | 75 | } else { |
okini3939 | 3:6f07b5f52c38 | 76 | buf[i] = buf[3]; |
okini3939 | 3:6f07b5f52c38 | 77 | } |
okini3939 | 3:6f07b5f52c38 | 78 | led4 = !led4; |
okini3939 | 3:6f07b5f52c38 | 79 | } |
okini3939 | 3:6f07b5f52c38 | 80 | } |
okini3939 | 3:6f07b5f52c38 | 81 | audio.write(buf, 0, 4); |
okini3939 | 3:6f07b5f52c38 | 82 | } |
okini3939 | 3:6f07b5f52c38 | 83 | |
Gruenfrosch | 0:7627c79db971 | 84 | int main(int argc, char *argv[]) |
Gruenfrosch | 0:7627c79db971 | 85 | { |
Gruenfrosch | 2:f28cf0afd021 | 86 | int result; |
Gruenfrosch | 0:7627c79db971 | 87 | Timer t; |
Gruenfrosch | 2:f28cf0afd021 | 88 | struct mad_decoder decoder; |
Gruenfrosch | 0:7627c79db971 | 89 | |
okini3939 | 3:6f07b5f52c38 | 90 | pc.baud(115200); |
Gruenfrosch | 0:7627c79db971 | 91 | dac_s = dac_e = dacbuf; |
okini3939 | 3:6f07b5f52c38 | 92 | |
okini3939 | 3:6f07b5f52c38 | 93 | audio.power(0x02); // mic off |
okini3939 | 3:6f07b5f52c38 | 94 | audio.outputVolume(1, 1); |
okini3939 | 3:6f07b5f52c38 | 95 | audio.inputVolume(0.7, 0.7); |
okini3939 | 3:6f07b5f52c38 | 96 | audio.frequency(44100); |
okini3939 | 3:6f07b5f52c38 | 97 | audio.attach(&isr_audio); |
okini3939 | 3:6f07b5f52c38 | 98 | audio.start(TRANSMIT); |
okini3939 | 3:6f07b5f52c38 | 99 | |
Gruenfrosch | 2:f28cf0afd021 | 100 | while(1) { |
okini3939 | 3:6f07b5f52c38 | 101 | fp = fopen("/sd/canyouparty.mp3","rb"); |
okini3939 | 3:6f07b5f52c38 | 102 | |
okini3939 | 3:6f07b5f52c38 | 103 | if(!fp) return(printf("file error\r\n")); |
okini3939 | 3:6f07b5f52c38 | 104 | fprintf(stderr,"decode start\r\n"); |
okini3939 | 3:6f07b5f52c38 | 105 | led1 = 1; |
okini3939 | 3:6f07b5f52c38 | 106 | mad_decoder_init(&decoder, NULL,input, 0, 0, output,error_fn, 0); |
okini3939 | 3:6f07b5f52c38 | 107 | t.reset(); |
okini3939 | 3:6f07b5f52c38 | 108 | t.start(); |
okini3939 | 3:6f07b5f52c38 | 109 | led2 = 1; |
okini3939 | 3:6f07b5f52c38 | 110 | result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); |
okini3939 | 3:6f07b5f52c38 | 111 | t.stop(); |
okini3939 | 3:6f07b5f52c38 | 112 | fprintf(stderr,"decode ret=%d in %d ms\r\n",result,t.read_ms()); |
okini3939 | 3:6f07b5f52c38 | 113 | led2 = 0; |
okini3939 | 3:6f07b5f52c38 | 114 | mad_decoder_finish(&decoder); |
okini3939 | 3:6f07b5f52c38 | 115 | fclose(fp); |
okini3939 | 3:6f07b5f52c38 | 116 | } |
Gruenfrosch | 0:7627c79db971 | 117 | } |
Gruenfrosch | 0:7627c79db971 | 118 | |
Gruenfrosch | 0:7627c79db971 | 119 | /* |
Gruenfrosch | 0:7627c79db971 | 120 | * This is the input callback. The purpose of this callback is to (re)fill |
Gruenfrosch | 0:7627c79db971 | 121 | * the stream buffer which is to be decoded. |
Gruenfrosch | 0:7627c79db971 | 122 | */ |
Gruenfrosch | 0:7627c79db971 | 123 | |
Gruenfrosch | 0:7627c79db971 | 124 | static |
Gruenfrosch | 0:7627c79db971 | 125 | enum mad_flow input(void *data, |
Gruenfrosch | 0:7627c79db971 | 126 | struct mad_stream *stream) |
Gruenfrosch | 0:7627c79db971 | 127 | { |
Gruenfrosch | 0:7627c79db971 | 128 | static unsigned char strmbuff[2100]; |
Gruenfrosch | 0:7627c79db971 | 129 | int ret; |
Gruenfrosch | 0:7627c79db971 | 130 | int rsz; |
Gruenfrosch | 0:7627c79db971 | 131 | unsigned char *bp; |
Gruenfrosch | 0:7627c79db971 | 132 | |
Gruenfrosch | 0:7627c79db971 | 133 | /* the remaining bytes from incomplete frames must be copied |
Gruenfrosch | 0:7627c79db971 | 134 | to the beginning of the new buffer ! |
Gruenfrosch | 0:7627c79db971 | 135 | */ |
Gruenfrosch | 0:7627c79db971 | 136 | bp = strmbuff; |
Gruenfrosch | 0:7627c79db971 | 137 | rsz = 0; |
Gruenfrosch | 0:7627c79db971 | 138 | if(stream->error == MAD_ERROR_BUFLEN||stream->buffer==NULL) |
Gruenfrosch | 0:7627c79db971 | 139 | { |
Gruenfrosch | 0:7627c79db971 | 140 | if(stream->next_frame!=NULL) |
Gruenfrosch | 0:7627c79db971 | 141 | { |
Gruenfrosch | 0:7627c79db971 | 142 | rsz = stream->bufend-stream->next_frame; |
Gruenfrosch | 0:7627c79db971 | 143 | memmove(strmbuff,stream->next_frame,rsz); |
Gruenfrosch | 0:7627c79db971 | 144 | bp = strmbuff+rsz; |
Gruenfrosch | 0:7627c79db971 | 145 | } |
Gruenfrosch | 0:7627c79db971 | 146 | } |
Gruenfrosch | 0:7627c79db971 | 147 | |
Gruenfrosch | 0:7627c79db971 | 148 | ret = fread(bp,1,sizeof(strmbuff) - rsz,fp); |
Gruenfrosch | 0:7627c79db971 | 149 | |
Gruenfrosch | 0:7627c79db971 | 150 | if (!ret) |
Gruenfrosch | 0:7627c79db971 | 151 | return MAD_FLOW_STOP; |
Gruenfrosch | 0:7627c79db971 | 152 | |
Gruenfrosch | 0:7627c79db971 | 153 | |
Gruenfrosch | 0:7627c79db971 | 154 | mad_stream_buffer(stream, strmbuff, ret + rsz); |
Gruenfrosch | 0:7627c79db971 | 155 | |
Gruenfrosch | 0:7627c79db971 | 156 | return MAD_FLOW_CONTINUE;} |
Gruenfrosch | 0:7627c79db971 | 157 | |
Gruenfrosch | 0:7627c79db971 | 158 | |
Gruenfrosch | 0:7627c79db971 | 159 | /* |
Gruenfrosch | 0:7627c79db971 | 160 | * The following utility routine performs simple rounding, clipping, and |
Gruenfrosch | 0:7627c79db971 | 161 | * scaling of MAD's high-resolution samples down to 16 bits. It does not |
Gruenfrosch | 0:7627c79db971 | 162 | * perform any dithering or noise shaping, which would be recommended to |
Gruenfrosch | 0:7627c79db971 | 163 | * obtain any exceptional audio quality. It is therefore not recommended to |
Gruenfrosch | 0:7627c79db971 | 164 | * use this routine if high-quality output is desired. |
Gruenfrosch | 0:7627c79db971 | 165 | */ |
Gruenfrosch | 0:7627c79db971 | 166 | |
Gruenfrosch | 0:7627c79db971 | 167 | static /*inline*/ |
Gruenfrosch | 0:7627c79db971 | 168 | signed int scale(mad_fixed_t sample) |
Gruenfrosch | 0:7627c79db971 | 169 | { |
Gruenfrosch | 0:7627c79db971 | 170 | /* round */ |
Gruenfrosch | 0:7627c79db971 | 171 | sample += (1L << (MAD_F_FRACBITS - 16)); |
Gruenfrosch | 0:7627c79db971 | 172 | |
Gruenfrosch | 0:7627c79db971 | 173 | /* clip */ |
Gruenfrosch | 0:7627c79db971 | 174 | if (sample >= MAD_F_ONE) |
Gruenfrosch | 0:7627c79db971 | 175 | sample = MAD_F_ONE - 1; |
Gruenfrosch | 0:7627c79db971 | 176 | else if (sample < -MAD_F_ONE) |
Gruenfrosch | 0:7627c79db971 | 177 | sample = -MAD_F_ONE; |
Gruenfrosch | 0:7627c79db971 | 178 | |
Gruenfrosch | 0:7627c79db971 | 179 | /* quantize */ |
Gruenfrosch | 0:7627c79db971 | 180 | return sample >> (MAD_F_FRACBITS + 1 - 16); |
Gruenfrosch | 0:7627c79db971 | 181 | } |
Gruenfrosch | 0:7627c79db971 | 182 | |
Gruenfrosch | 0:7627c79db971 | 183 | /* |
Gruenfrosch | 0:7627c79db971 | 184 | * This is the output callback function. It is called after each frame of |
Gruenfrosch | 0:7627c79db971 | 185 | * MPEG audio data has been completely decoded. The purpose of this callback |
Gruenfrosch | 0:7627c79db971 | 186 | * is to output (or play) the decoded PCM audio. |
Gruenfrosch | 0:7627c79db971 | 187 | */ |
Gruenfrosch | 0:7627c79db971 | 188 | |
Gruenfrosch | 0:7627c79db971 | 189 | static |
Gruenfrosch | 0:7627c79db971 | 190 | enum mad_flow output(void *data, |
Gruenfrosch | 0:7627c79db971 | 191 | struct mad_header const *header, |
Gruenfrosch | 0:7627c79db971 | 192 | struct mad_pcm *pcm) |
Gruenfrosch | 0:7627c79db971 | 193 | { |
Gruenfrosch | 0:7627c79db971 | 194 | unsigned int nchannels, nsamples; |
Gruenfrosch | 0:7627c79db971 | 195 | mad_fixed_t const *left_ch, *right_ch; |
Gruenfrosch | 0:7627c79db971 | 196 | |
Gruenfrosch | 2:f28cf0afd021 | 197 | |
Gruenfrosch | 0:7627c79db971 | 198 | /* pcm->samplerate contains the sampling frequency */ |
Gruenfrosch | 0:7627c79db971 | 199 | nchannels = pcm->channels; |
Gruenfrosch | 0:7627c79db971 | 200 | nsamples = pcm->length; |
Gruenfrosch | 0:7627c79db971 | 201 | left_ch = pcm->samples[0]; |
Gruenfrosch | 0:7627c79db971 | 202 | right_ch = pcm->samples[1]; |
Gruenfrosch | 2:f28cf0afd021 | 203 | |
Gruenfrosch | 2:f28cf0afd021 | 204 | while(dac_s < dac_e) wait_us(1); |
Gruenfrosch | 2:f28cf0afd021 | 205 | dac_e = dacbuf; // potential thread problem ?? no... |
Gruenfrosch | 2:f28cf0afd021 | 206 | dac_s = dacbuf; |
Gruenfrosch | 0:7627c79db971 | 207 | |
Gruenfrosch | 0:7627c79db971 | 208 | while (nsamples--) { |
Gruenfrosch | 2:f28cf0afd021 | 209 | signed int sample_l,sample_r; |
Gruenfrosch | 2:f28cf0afd021 | 210 | sample_l = scale(*left_ch); |
Gruenfrosch | 2:f28cf0afd021 | 211 | sample_r = scale(*right_ch); |
okini3939 | 4:30b2cf4a8ee2 | 212 | // dac_e->l = sample_l +32768; |
okini3939 | 4:30b2cf4a8ee2 | 213 | // dac_e->r = sample_r +32768; |
okini3939 | 4:30b2cf4a8ee2 | 214 | dac_e->l = sample_l; |
okini3939 | 4:30b2cf4a8ee2 | 215 | dac_e->r = sample_r; |
Gruenfrosch | 2:f28cf0afd021 | 216 | dac_e++; |
Gruenfrosch | 2:f28cf0afd021 | 217 | left_ch++; |
Gruenfrosch | 2:f28cf0afd021 | 218 | right_ch++; |
Gruenfrosch | 0:7627c79db971 | 219 | } |
Gruenfrosch | 0:7627c79db971 | 220 | return MAD_FLOW_CONTINUE; |
Gruenfrosch | 0:7627c79db971 | 221 | } |
Gruenfrosch | 0:7627c79db971 | 222 | |
Gruenfrosch | 0:7627c79db971 | 223 | /* |
Gruenfrosch | 0:7627c79db971 | 224 | * This is the error callback function. It is called whenever a decoding |
Gruenfrosch | 0:7627c79db971 | 225 | * error occurs. The error is indicated by stream->error; the list of |
Gruenfrosch | 0:7627c79db971 | 226 | * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h) |
Gruenfrosch | 0:7627c79db971 | 227 | * header file. |
Gruenfrosch | 0:7627c79db971 | 228 | */ |
Gruenfrosch | 0:7627c79db971 | 229 | |
Gruenfrosch | 0:7627c79db971 | 230 | static |
Gruenfrosch | 0:7627c79db971 | 231 | enum mad_flow error_fn(void *data, |
Gruenfrosch | 0:7627c79db971 | 232 | struct mad_stream *stream, |
Gruenfrosch | 0:7627c79db971 | 233 | struct mad_frame *frame) |
Gruenfrosch | 0:7627c79db971 | 234 | { |
Gruenfrosch | 0:7627c79db971 | 235 | /* ID3 tags will cause warnings and short noise, ignore it for the moment*/ |
okini3939 | 3:6f07b5f52c38 | 236 | /* |
Gruenfrosch | 0:7627c79db971 | 237 | fprintf(stderr, "decoding error 0x%04x (%s)\n", |
Gruenfrosch | 0:7627c79db971 | 238 | stream->error, mad_stream_errorstr(stream)); |
okini3939 | 3:6f07b5f52c38 | 239 | */ |
Gruenfrosch | 0:7627c79db971 | 240 | |
Gruenfrosch | 0:7627c79db971 | 241 | /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */ |
Gruenfrosch | 0:7627c79db971 | 242 | |
Gruenfrosch | 0:7627c79db971 | 243 | return MAD_FLOW_CONTINUE; |
Gruenfrosch | 0:7627c79db971 | 244 | } |
Gruenfrosch | 0:7627c79db971 | 245 | |
Gruenfrosch | 0:7627c79db971 | 246 |