Difference between revisions of "SD card audio streaming"

From Gamebuino Wiki
Jump to: navigation, search
(Created page with "The following code will stream an audio file off the SD card and play it out the Gambuino speaker. The comments at the top of the source file explain how to convert any regula...")
 
m
 
(4 intermediate revisions by the same user not shown)
Line 7: Line 7:
 
//
 
//
 
// Requires TimerOne library from http://playground.arduino.cc/Code/Timer1
 
// Requires TimerOne library from http://playground.arduino.cc/Code/Timer1
// Uses 5110 display code from http://playground.arduino.cc/Code/PCD8544#.U0EYgvmSyDs
 
 
//
 
//
 
// This demo will look for a file called SAMPLE.RAW on the SD card containing raw
 
// This demo will look for a file called SAMPLE.RAW on the SD card containing raw
Line 21: Line 20:
  
 
#define PLAYBACK_FREQ 20000
 
#define PLAYBACK_FREQ 20000
 
 
#define SPEAKER 3
 
#define SPEAKER 3
#define BACKLIGHT 5
 
 
#define PIN_SCE  15
 
#define PIN_RESET 14
 
#define PIN_DC    16
 
#define PIN_SDIN  11
 
#define PIN_SCLK  13
 
#define LCD_C    LOW
 
#define LCD_D    HIGH
 
#define LCD_X    84
 
#define LCD_Y    48
 
 
static const byte ASCII[][5] =
 
{
 
{0x00, 0x00, 0x00, 0x00, 0x00} // 20 
 
,{0x00, 0x00, 0x5f, 0x00, 0x00} // 21 !
 
,{0x00, 0x07, 0x00, 0x07, 0x00} // 22 "
 
,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 23 #
 
,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 24 $
 
,{0x23, 0x13, 0x08, 0x64, 0x62} // 25 %
 
,{0x36, 0x49, 0x55, 0x22, 0x50} // 26 &
 
,{0x00, 0x05, 0x03, 0x00, 0x00} // 27 '
 
,{0x00, 0x1c, 0x22, 0x41, 0x00} // 28 (
 
,{0x00, 0x41, 0x22, 0x1c, 0x00} // 29 )
 
,{0x14, 0x08, 0x3e, 0x08, 0x14} // 2a *
 
,{0x08, 0x08, 0x3e, 0x08, 0x08} // 2b +
 
,{0x00, 0x50, 0x30, 0x00, 0x00} // 2c ,
 
,{0x08, 0x08, 0x08, 0x08, 0x08} // 2d -
 
,{0x00, 0x60, 0x60, 0x00, 0x00} // 2e .
 
,{0x20, 0x10, 0x08, 0x04, 0x02} // 2f /
 
,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 30 0
 
,{0x00, 0x42, 0x7f, 0x40, 0x00} // 31 1
 
,{0x42, 0x61, 0x51, 0x49, 0x46} // 32 2
 
,{0x21, 0x41, 0x45, 0x4b, 0x31} // 33 3
 
,{0x18, 0x14, 0x12, 0x7f, 0x10} // 34 4
 
,{0x27, 0x45, 0x45, 0x45, 0x39} // 35 5
 
,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 36 6
 
,{0x01, 0x71, 0x09, 0x05, 0x03} // 37 7
 
,{0x36, 0x49, 0x49, 0x49, 0x36} // 38 8
 
,{0x06, 0x49, 0x49, 0x29, 0x1e} // 39 9
 
,{0x00, 0x36, 0x36, 0x00, 0x00} // 3a :
 
,{0x00, 0x56, 0x36, 0x00, 0x00} // 3b ;
 
,{0x08, 0x14, 0x22, 0x41, 0x00} // 3c <
 
,{0x14, 0x14, 0x14, 0x14, 0x14} // 3d =
 
,{0x00, 0x41, 0x22, 0x14, 0x08} // 3e >
 
,{0x02, 0x01, 0x51, 0x09, 0x06} // 3f ?
 
,{0x32, 0x49, 0x79, 0x41, 0x3e} // 40 @
 
,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 41 A
 
,{0x7f, 0x49, 0x49, 0x49, 0x36} // 42 B
 
,{0x3e, 0x41, 0x41, 0x41, 0x22} // 43 C
 
,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 44 D
 
,{0x7f, 0x49, 0x49, 0x49, 0x41} // 45 E
 
,{0x7f, 0x09, 0x09, 0x09, 0x01} // 46 F
 
,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 47 G
 
,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 48 H
 
,{0x00, 0x41, 0x7f, 0x41, 0x00} // 49 I
 
,{0x20, 0x40, 0x41, 0x3f, 0x01} // 4a J
 
,{0x7f, 0x08, 0x14, 0x22, 0x41} // 4b K
 
,{0x7f, 0x40, 0x40, 0x40, 0x40} // 4c L
 
,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 4d M
 
,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 4e N
 
,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 4f O
 
,{0x7f, 0x09, 0x09, 0x09, 0x06} // 50 P
 
,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 51 Q
 
,{0x7f, 0x09, 0x19, 0x29, 0x46} // 52 R
 
,{0x46, 0x49, 0x49, 0x49, 0x31} // 53 S
 
,{0x01, 0x01, 0x7f, 0x01, 0x01} // 54 T
 
,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 55 U
 
,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 56 V
 
,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 57 W
 
,{0x63, 0x14, 0x08, 0x14, 0x63} // 58 X
 
,{0x07, 0x08, 0x70, 0x08, 0x07} // 59 Y
 
,{0x61, 0x51, 0x49, 0x45, 0x43} // 5a Z
 
,{0x00, 0x7f, 0x41, 0x41, 0x00} // 5b [
 
,{0x02, 0x04, 0x08, 0x10, 0x20} // 5c ¥
 
,{0x00, 0x41, 0x41, 0x7f, 0x00} // 5d ]
 
,{0x04, 0x02, 0x01, 0x02, 0x04} // 5e ^
 
,{0x40, 0x40, 0x40, 0x40, 0x40} // 5f _
 
,{0x00, 0x01, 0x02, 0x04, 0x00} // 60 `
 
,{0x20, 0x54, 0x54, 0x54, 0x78} // 61 a
 
,{0x7f, 0x48, 0x44, 0x44, 0x38} // 62 b
 
,{0x38, 0x44, 0x44, 0x44, 0x20} // 63 c
 
,{0x38, 0x44, 0x44, 0x48, 0x7f} // 64 d
 
,{0x38, 0x54, 0x54, 0x54, 0x18} // 65 e
 
,{0x08, 0x7e, 0x09, 0x01, 0x02} // 66 f
 
,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 67 g
 
,{0x7f, 0x08, 0x04, 0x04, 0x78} // 68 h
 
,{0x00, 0x44, 0x7d, 0x40, 0x00} // 69 i
 
,{0x20, 0x40, 0x44, 0x3d, 0x00} // 6a j
 
,{0x7f, 0x10, 0x28, 0x44, 0x00} // 6b k
 
,{0x00, 0x41, 0x7f, 0x40, 0x00} // 6c l
 
,{0x7c, 0x04, 0x18, 0x04, 0x78} // 6d m
 
,{0x7c, 0x08, 0x04, 0x04, 0x78} // 6e n
 
,{0x38, 0x44, 0x44, 0x44, 0x38} // 6f o
 
,{0x7c, 0x14, 0x14, 0x14, 0x08} // 70 p
 
,{0x08, 0x14, 0x14, 0x18, 0x7c} // 71 q
 
,{0x7c, 0x08, 0x04, 0x04, 0x08} // 72 r
 
,{0x48, 0x54, 0x54, 0x54, 0x20} // 73 s
 
,{0x04, 0x3f, 0x44, 0x40, 0x20} // 74 t
 
,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 75 u
 
,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 76 v
 
,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 77 w
 
,{0x44, 0x28, 0x10, 0x28, 0x44} // 78 x
 
,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 79 y
 
,{0x44, 0x64, 0x54, 0x4c, 0x44} // 7a z
 
,{0x00, 0x08, 0x36, 0x41, 0x00} // 7b {
 
,{0x00, 0x00, 0x7f, 0x00, 0x00} // 7c |
 
,{0x00, 0x41, 0x36, 0x08, 0x00} // 7d }
 
,{0x10, 0x08, 0x08, 0x10, 0x08} // 7e ←
 
,{0x78, 0x46, 0x41, 0x46, 0x78} // 7f →
 
};
 
 
 
#define BUFFER_SIZE 128
 
#define BUFFER_SIZE 128
volatile unsigned char buffers[2][BUFFER_SIZE];
+
unsigned char buffers[2][BUFFER_SIZE];
 
volatile int currentBuffer = 0;
 
volatile int currentBuffer = 0;
 
volatile unsigned char * currentPtr;
 
volatile unsigned char * currentPtr;
 
volatile unsigned char * endPtr;
 
volatile unsigned char * endPtr;
 
 
File dataFile;
 
File dataFile;
 
const int chipSelect = 10;
 
const int chipSelect = 10;
  
 
void setup() {   
 
void setup() {   
  LcdInitialise();
 
  LcdClear();
 
  LcdString("=Media Test=");
 
  gotoXY(0, 2);
 
 
 
  pinMode(BACKLIGHT, OUTPUT);
 
  digitalWrite(BACKLIGHT, HIGH);
 
 
 
 
   pinMode(10, OUTPUT);   
 
   pinMode(10, OUTPUT);   
   if (!SD.begin(chipSelect)) {   
+
   if (!SD.begin(chipSelect))
    LcdString("Card failed!");
 
 
     return;
 
     return;
  }
 
 
   dataFile = SD.open("SAMPLE.RAW");
 
   dataFile = SD.open("SAMPLE.RAW");
 
 
 
   if (dataFile) {
 
   if (dataFile) {
     loadBuffer(0);
+
     dataFile.read((void *)&buffers[0], BUFFER_SIZE);
 
     currentBuffer = 0;
 
     currentBuffer = 0;
 
     currentPtr = buffers[currentBuffer];
 
     currentPtr = buffers[currentBuffer];
 
     endPtr = currentPtr + BUFFER_SIZE;
 
     endPtr = currentPtr + BUFFER_SIZE;
 
     pinMode(SPEAKER, OUTPUT);
 
     pinMode(SPEAKER, OUTPUT);
     setPwmFrequency(SPEAKER, 1);
+
     TCCR2B = (TCCR2B & 0b11111000) | 0x01; // set max PWM frequency
 
     Timer1.initialize(1000000/PLAYBACK_FREQ);
 
     Timer1.initialize(1000000/PLAYBACK_FREQ);
 
     Timer1.attachInterrupt(callback);
 
     Timer1.attachInterrupt(callback);
 
   }   
 
   }   
   else {
+
}
    LcdString("file error");
+
 
   }   
+
void loop() {
 +
   dataFile.read(&buffers[1], BUFFER_SIZE);
 +
  while (currentBuffer != 1);
 +
  dataFile.read(&buffers[0], BUFFER_SIZE);
 +
   while (currentBuffer != 0);
 
}
 
}
  
 
void callback()
 
void callback()
 
{
 
{
   analogWrite(SPEAKER, (*currentPtr++) +128);
+
   analogWrite(SPEAKER, (*currentPtr++) + 128);
 
   if (currentPtr >= endPtr)
 
   if (currentPtr >= endPtr)
 
   {
 
   {
Line 184: Line 62:
 
     endPtr = currentPtr + BUFFER_SIZE;
 
     endPtr = currentPtr + BUFFER_SIZE;
 
   }
 
   }
}
 
 
void loop() {
 
  loadBuffer(1);
 
  while (currentBuffer != 1);
 
  loadBuffer(0);
 
  while (currentBuffer != 0);
 
}
 
 
void loadBuffer(int bufferNum)
 
{
 
  for (int i=0; i<BUFFER_SIZE; i++)
 
    buffers[bufferNum][i] = dataFile.read();
 
}
 
 
void setPwmFrequency(int pin, int divisor) {
 
  byte mode;
 
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
 
    switch(divisor) {
 
      case 1: mode = 0x01; break;
 
      case 8: mode = 0x02; break;
 
      case 64: mode = 0x03; break;
 
      case 256: mode = 0x04; break;
 
      case 1024: mode = 0x05; break;
 
      default: return;
 
    }
 
    if(pin == 5 || pin == 6) {
 
      TCCR0B = TCCR0B & 0b11111000 | mode;
 
    } else {
 
      TCCR1B = TCCR1B & 0b11111000 | mode;
 
    }
 
  } else if(pin == 3 || pin == 11) {
 
    switch(divisor) {
 
      case 1: mode = 0x01; break;
 
      case 8: mode = 0x02; break;
 
      case 32: mode = 0x03; break;
 
      case 64: mode = 0x04; break;
 
      case 128: mode = 0x05; break;
 
      case 256: mode = 0x06; break;
 
      case 1024: mode = 0x7; break;
 
      default: return;
 
    }
 
    TCCR2B = TCCR2B & 0b11111000 | mode;
 
  }
 
}
 
 
void LcdCharacter(char character)
 
{
 
  LcdWrite(LCD_D, 0x00);
 
  for (int index = 0; index < 5; index++)
 
  {
 
    LcdWrite(LCD_D, ASCII[character - 0x20][index]);
 
  }
 
  LcdWrite(LCD_D, 0x00);
 
}
 
 
void LcdClear(void)
 
{
 
  for (int index = 0; index < LCD_X * LCD_Y / 8; index++)
 
  {
 
    LcdWrite(LCD_D, 0x00);
 
  }
 
}
 
 
void LcdInitialise(void)
 
{
 
  pinMode(PIN_SCE, OUTPUT);
 
  pinMode(PIN_RESET, OUTPUT);
 
  pinMode(PIN_DC, OUTPUT);
 
  pinMode(PIN_SDIN, OUTPUT);
 
  pinMode(PIN_SCLK, OUTPUT);
 
  digitalWrite(PIN_RESET, LOW);
 
  digitalWrite(PIN_RESET, HIGH);
 
  LcdWrite(LCD_C, 0x21 );  // LCD Extended Commands.
 
  LcdWrite(LCD_C, 0xB9 );  // Set LCD Vop (Contrast).
 
  LcdWrite(LCD_C, 0x04 );  // Set Temp coefficent. //0x04
 
  LcdWrite(LCD_C, 0x14 );  // LCD bias mode 1:48. //0x13
 
  LcdWrite(LCD_C, 0x0C );  // LCD in normal mode.
 
  LcdWrite(LCD_C, 0x20 );
 
  LcdWrite(LCD_C, 0x0C );
 
}
 
 
void LcdString(char *characters)
 
{
 
  while (*characters)
 
  {
 
    LcdCharacter(*characters++);
 
  }
 
}
 
 
void LcdWrite(byte dc, byte data)
 
{
 
  digitalWrite(PIN_DC, dc);
 
  digitalWrite(PIN_SCE, LOW);
 
  shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
 
  digitalWrite(PIN_SCE, HIGH);
 
}
 
 
void gotoXY(int x, int y)
 
{
 
  LcdWrite( 0, 0x80 | x);  // Column.
 
  LcdWrite( 0, 0x40 | y);  // Row. 
 
 
}
 
}
 
</pre>
 
</pre>

Latest revision as of 2014-04-06T13:05:47

The following code will stream an audio file off the SD card and play it out the Gambuino speaker. The comments at the top of the source file explain how to convert any regular audio file (WAV, MP3 etc) into a stream of raw bytes ready for play.

A video of this demo in action can be found at http://youtu.be/4ZI_CQeKIio

// Gamebuino sound streaming sample - by Mark Feldman (aka "Myndale")
//
// Requires TimerOne library from http://playground.arduino.cc/Code/Timer1
//
// This demo will look for a file called SAMPLE.RAW on the SD card containing raw
// 8-bit signed samples intended to be played back at a frequency of 20kHz.
// These files can be easily generated using the ffmpeg utility (which can be
// downloaded at http://www.ffmpeg.org/) using the following command line parameters:
//
// ffmpeg -i your_sound_file.mp3 -f s8 -acodec pcm_s8 -ar 20000 -ac 1 -af "volume=5dB" SAMPLE.RAW


#include <TimerOne.h>
#include <SD.h>

#define PLAYBACK_FREQ 20000
#define SPEAKER 3
#define BUFFER_SIZE 128
unsigned char buffers[2][BUFFER_SIZE];
volatile int currentBuffer = 0;
volatile unsigned char * currentPtr;
volatile unsigned char * endPtr;
File dataFile;
const int chipSelect = 10;

void setup() {  
  pinMode(10, OUTPUT);  
  if (!SD.begin(chipSelect))
    return;
  dataFile = SD.open("SAMPLE.RAW");
  if (dataFile) {
    dataFile.read((void *)&buffers[0], BUFFER_SIZE);
    currentBuffer = 0;
    currentPtr = buffers[currentBuffer];
    endPtr = currentPtr + BUFFER_SIZE;
    pinMode(SPEAKER, OUTPUT);
    TCCR2B = (TCCR2B & 0b11111000) | 0x01;  // set max PWM frequency
    Timer1.initialize(1000000/PLAYBACK_FREQ);
    Timer1.attachInterrupt(callback);
  }  
}

void loop() {
  dataFile.read(&buffers[1], BUFFER_SIZE);
  while (currentBuffer != 1);
  dataFile.read(&buffers[0], BUFFER_SIZE);
  while (currentBuffer != 0);
}

void callback()
{
  analogWrite(SPEAKER, (*currentPtr++) + 128);
  if (currentPtr >= endPtr)
  {
    currentBuffer = 1-currentBuffer;
    currentPtr = buffers[currentBuffer];
    endPtr = currentPtr + BUFFER_SIZE;
  }
}