DMA_fancy_player.ino
1 /* 2 * A fancy DMA MP3 player 3 * the PLAY pin will start playback from the beginning of the file 4 * the PAUSE pin will pause playback at the current point 5 * the RESUME pin will resume playback at the point it was paused 6 * a potentiometer is connected to the VOLUME pin to set the playback volume 7 */ 8 9 #include "Adafruit_MP3.h" 10 #include <SPI.h> 11 #include <SD.h> 12 13 #include <Adafruit_ZeroDMA.h> 14 #include "utility/dma.h" 15 16 #define VOLUME_MAX 2047 17 #define PIN_PAUSE 5 18 #define PIN_RESUME 6 19 #define PIN_PLAY 9 20 #define PIN_VOLUME A3 21 22 const char *filename = "test3.mp3"; 23 const int chipSelect = 10; 24 25 Adafruit_ZeroDMA leftDMA; 26 Adafruit_ZeroDMA rightDMA; 27 ZeroDMAstatus stat; // DMA status codes returned by some functions 28 File dataFile; 29 Adafruit_MP3_DMA player; 30 DmacDescriptor *desc; 31 32 //the output data buffers 33 int16_t *ping, *pong; 34 int volume; 35 36 //this gets called when the player wants more data 37 int getMoreData(uint8_t *writeHere, int thisManyBytes){ 38 int bytesRead = 0; 39 while(dataFile.available() && bytesRead < thisManyBytes){ 40 *writeHere = dataFile.read(); 41 writeHere++; 42 bytesRead++; 43 } 44 return bytesRead; 45 } 46 47 48 //this will get called when data has been decoded 49 void decodeCallback(int16_t *data, int len){ 50 for(int i=0; i<len; i++){ 51 int val = map(*data, -32768, 32767, 0, volume); 52 *data++ = val; 53 } 54 } 55 56 void dma_callback(Adafruit_ZeroDMA *dma) { 57 58 digitalWrite(13, HIGH); 59 //try to fill the next buffer 60 if(player.fill()){ 61 //stop 62 leftDMA.abort(); 63 rightDMA.abort(); 64 } 65 digitalWrite(13, LOW); 66 } 67 68 void doNothing(Adafruit_ZeroDMA *dma) { 69 70 } 71 72 void initMonoDMA(){ 73 //set up the DMA channels 74 leftDMA.setTrigger(MP3_DMA_TRIGGER); 75 leftDMA.setAction(DMA_TRIGGER_ACTON_BEAT); 76 stat = leftDMA.allocate(); 77 78 //ask for the buffers we're going to use 79 player.getBuffers(&ping, &pong); 80 81 //make the descriptors 82 desc = leftDMA.addDescriptor( 83 ping, // move data from here 84 (void *)(&DAC->DATA[0]), // to here (M4) 85 MP3_OUTBUF_SIZE, 86 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 87 true, // increment source addr? 88 false); 89 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 90 91 desc = leftDMA.addDescriptor( 92 pong, // move data from here 93 (void *)(&DAC->DATA[0]), // to here (M4) 94 MP3_OUTBUF_SIZE, 95 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 96 true, // increment source addr? 97 false); // increment dest addr? 98 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 99 100 //make the descriptor list loop 101 leftDMA.loop(true); 102 leftDMA.setCallback(dma_callback); 103 104 rightDMA.setTrigger(MP3_DMA_TRIGGER); 105 rightDMA.setAction(DMA_TRIGGER_ACTON_BEAT); 106 stat = rightDMA.allocate(); 107 108 //make the descriptors 109 desc = rightDMA.addDescriptor( 110 ping + 1, // move data from here 111 (void *)(&DAC->DATA[1]), // to here (M4) 112 MP3_OUTBUF_SIZE, // this many... 113 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 114 true, // increment source addr? 115 false); // increment dest addr? 116 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 117 118 desc = rightDMA.addDescriptor( 119 pong + 1, // move data from here 120 (void *)(&DAC->DATA[1]), // to here (M4) 121 MP3_OUTBUF_SIZE, // this many... 122 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 123 true, // increment source addr? 124 false); // increment dest addr? 125 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 126 127 //make the descriptor list loop 128 rightDMA.loop(true); 129 rightDMA.setCallback(doNothing); 130 } 131 132 void initStereoDMA(){ 133 //######### LEFT CHANNEL DMA ##############// 134 135 //set up the DMA channels 136 leftDMA.setTrigger(MP3_DMA_TRIGGER); 137 leftDMA.setAction(DMA_TRIGGER_ACTON_BEAT); 138 stat = leftDMA.allocate(); 139 140 //ask for the buffers we're going to use 141 player.getBuffers(&ping, &pong); 142 143 //make the descriptors 144 desc = leftDMA.addDescriptor( 145 ping, // move data from here 146 (void *)(&DAC->DATA[0]), // to here (M4) 147 MP3_OUTBUF_SIZE >> 1, // this many... 148 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 149 true, // increment source addr? 150 false, 151 DMA_ADDRESS_INCREMENT_STEP_SIZE_2, 152 DMA_STEPSEL_SRC); // increment dest addr? 153 154 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 155 156 desc = leftDMA.addDescriptor( 157 pong, // move data from here 158 (void *)(&DAC->DATA[0]), // to here (M4) 159 MP3_OUTBUF_SIZE >> 1, // this many... 160 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 161 true, // increment source addr? 162 false, 163 DMA_ADDRESS_INCREMENT_STEP_SIZE_2, 164 DMA_STEPSEL_SRC); // increment dest addr? 165 166 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 167 168 //make the descriptor list loop 169 leftDMA.loop(true); 170 leftDMA.setCallback(dma_callback); 171 172 //######### RIGHT CHANNEL DMA ##############// 173 174 rightDMA.setTrigger(MP3_DMA_TRIGGER); 175 rightDMA.setAction(DMA_TRIGGER_ACTON_BEAT); 176 stat = rightDMA.allocate(); 177 178 //make the descriptors 179 desc = rightDMA.addDescriptor( 180 ping + 1, // move data from here 181 (void *)(&DAC->DATA[1]), // to here (M4) 182 MP3_OUTBUF_SIZE >> 1, // this many... 183 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 184 true, // increment source addr? 185 false, 186 DMA_ADDRESS_INCREMENT_STEP_SIZE_2, 187 DMA_STEPSEL_SRC); // increment dest addr? 188 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 189 190 desc = rightDMA.addDescriptor( 191 pong + 1, // move data from here 192 (void *)(&DAC->DATA[1]), // to here (M4) 193 MP3_OUTBUF_SIZE >> 1, // this many... 194 DMA_BEAT_SIZE_HWORD, // bytes/hword/words 195 true, // increment source addr? 196 false, 197 DMA_ADDRESS_INCREMENT_STEP_SIZE_2, 198 DMA_STEPSEL_SRC); // increment dest addr? 199 desc->BTCTRL.bit.BLOCKACT = DMA_BLOCK_ACTION_INT; 200 201 //make the descriptor list loop 202 rightDMA.loop(true); 203 rightDMA.setCallback(doNothing); 204 } 205 206 // the setup routine runs once when you press reset: 207 void setup() { 208 pinMode(13, OUTPUT); 209 Serial.begin(9600); 210 while(!Serial); 211 212 pinMode(PIN_PAUSE, INPUT_PULLUP); 213 pinMode(PIN_RESUME, INPUT_PULLUP); 214 pinMode(PIN_PLAY, INPUT_PULLUP); 215 216 while (!SD.begin(12000000, chipSelect)) { 217 Serial.println("Card failed, or not present"); 218 delay(2000); 219 } 220 Serial.println("card initialized."); 221 222 dataFile = SD.open(filename); 223 if(!dataFile){ 224 Serial.println("could not open file!"); 225 while(1); 226 } 227 228 //set the DAC to the center of the range 229 analogWrite(A0, 2048); 230 analogWrite(A1, 2048); 231 232 //begin the player 233 player.begin(); 234 235 //this will be how the player asks for data 236 player.setBufferCallback(getMoreData); 237 238 //this will be how the player asks you to clean the data 239 player.setDecodeCallback(decodeCallback); 240 } 241 242 void loop() { 243 //read volume control 244 int v = analogRead(PIN_VOLUME); 245 volume = map(v, 0, 1023, 0, VOLUME_MAX); 246 247 //read pins 248 249 if(!digitalRead(PIN_PAUSE)){ 250 player.pause(); 251 } 252 else if(!digitalRead(PIN_RESUME)){ 253 player.resume(); 254 } 255 else if(!digitalRead(PIN_PLAY)){ 256 //this button will restart playback of the file 257 rightDMA.abort(); 258 leftDMA.abort(); 259 260 if(dataFile){ 261 dataFile.close(); 262 } 263 dataFile = SD.open(filename); 264 if(!dataFile){ 265 Serial.println("could not open file!"); 266 while(1); 267 } 268 269 player.play(); //this will automatically fill the first buffer and get the channel info 270 271 if(player.numChannels == 1) 272 initMonoDMA(); //this is a mono file 273 274 else if(player.numChannels == 2) 275 initStereoDMA(); //this is a stereo file 276 277 else{ 278 Serial.println("only mono and stereo files are supported"); 279 while(1); 280 } 281 282 rightDMA.startJob(); 283 leftDMA.startJob(); 284 } 285 delay(20); 286 } 287