Rishabh Banga
Published © CC BY-NC-SA

Virtual Tabla powered by Intel Galileo

This project displays, how using Intel Galileo Gen 1 and a Tabla set, one can similar sounds as produced while striking the Tabla.

IntermediateShowcase (no instructions)2,133
Virtual Tabla powered by Intel Galileo

Things used in this project

Hardware components

SparkFun Sensor Kit
SparkFun Sensor Kit
×1
Jumper wires (generic)
Jumper wires (generic)
×5
Portable speakers
Preferably wired JBL ones.
×1
MicroSD Card and Reader
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Masking Tape
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Schematics

Code

VirtualTabla.ino

C/C++
Code for making the interaction between Sensors and Galileo Board
#include <SPI.h>
#include <SD.h>

#define SENSOR_1 A0
#define SENSOR_2 A1
#define SENSOR_3 A2
#define SENSOR_4 A3

#define THRESHOLD 400
 
#define CHUNK_SIZE 32
#define PAGE_SIZE 4096
 
//Create the variables to be used by SdFat Library
File track;
 
//This is the name of the file on the microSD card you would like to play
//Stick with normal 8.3 nomeclature. All lower-case works well.
//Note: you must name the tracks on the SD card with 001, 002, 003, etc. 
//For example, the code is expecting to play 'track002.mp3', not track2.mp3.
char trackName[] = "track001.mp3";
int trackNumber = 1;
 
char errorMsg[100]; //This is a generic array used for sprintf of error messages
 
#define TRUE  0
#define FALSE  1
 
//MP3 Player Shield pin mapping. See the schematic
#define MP3_XCS 6 //Control Chip Select Pin (for accessing SPI Control/Status registers)
#define MP3_XDCS 7 //Data Chip Select / BSYNC Pin
#define MP3_DREQ 2 //Data Request Pin: Player asks for more data
#define MP3_RESET 8 //Reset is active low
//Remember you have to edit the Sd2PinMap.h of the sdfatlib library to correct control the SD card.
 
//VS10xx SCI Registers
#define SCI_MODE 0x00
#define SCI_STATUS 0x01
#define SCI_BASS 0x02
#define SCI_CLOCKF 0x03
#define SCI_DECODE_TIME 0x04
#define SCI_AUDATA 0x05
#define SCI_WRAM 0x06
#define SCI_WRAMADDR 0x07
#define SCI_HDAT0 0x08
#define SCI_HDAT1 0x09
#define SCI_AIADDR 0x0A
#define SCI_VOL 0x0B
#define SCI_AICTRL0 0x0C
#define SCI_AICTRL1 0x0D
#define SCI_AICTRL2 0x0E
#define SCI_AICTRL3 0x0F
 
//Synth
#define notes_len_max 8
#define num_sounds 49
 
int sequence[notes_len_max];
int curr_sound_num;
 
int num_notes=notes_len_max;
float last_note_length=1;
 
void setup() {
  pinMode(MP3_DREQ, INPUT);
  pinMode(MP3_XCS, OUTPUT);
  pinMode(MP3_XDCS, OUTPUT);
  pinMode(MP3_RESET, OUTPUT);
 
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW);
 
  digitalWrite(MP3_XCS, HIGH); //Deselect Control
  digitalWrite(MP3_XDCS, HIGH); //Deselect Data
  digitalWrite(MP3_RESET, LOW); //Put VS1053 into hardware reset
 
  Serial.begin(9600); //Use serial for debugging 
 
  //Serial.println("Type any character to start");
  //while (Serial.read() <= 0) {}
 
  Serial.println("MP3 Testing");
 
  if (!SD.begin(0)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("SD card initialized.");
 
  //From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz. 
  //Internal clock multiplier is 1.0x after power up. 
  //Therefore, max SPI speed is 1.75MHz. We will use 1MHz to be safe.
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV16); //Set SPI bus speed to 1MHz (16MHz / 16 = 1MHz)
  SPI.transfer(0xFF); //Throw a dummy byte at the bus
  //Initialize VS1053 chip 
  delay(10);
  digitalWrite(MP3_RESET, HIGH); //Bring up VS1053
  //delay(10); //We don't need this delay because any register changes will check for a high DREQ
 
  //Mp3SetVolume(20, 20); //Set initial volume (20 = -10dB) LOUD
  Mp3SetVolume(40, 40); //Set initial volume (20 = -10dB) Manageable
  //Mp3SetVolume(80, 80); //Set initial volume (20 = -10dB) More quiet
 
  //Let's check the status of the VS1053
  int MP3Mode = Mp3ReadRegister(SCI_MODE);
  int MP3Status = Mp3ReadRegister(SCI_STATUS);
  int MP3Clock = Mp3ReadRegister(SCI_CLOCKF);
 
  Serial.print("SCI_Mode (0x4800) = 0x");
  Serial.println(MP3Mode, HEX);
 
  Serial.print("SCI_Status (0x48) = 0x");
  Serial.println(MP3Status, HEX);
 
  int vsVersion = (MP3Status >> 4) & 0x000F; //Mask out only the four version bits
  Serial.print("VS Version (VS1053 is 4) = ");
  Serial.println(vsVersion, DEC); //The 1053B should respond with 4. VS1001 = 0, VS1011 = 1, VS1002 = 2, VS1003 = 3
 
  Serial.print("SCI_ClockF = 0x");
  Serial.println(MP3Clock, HEX);
 
  //Now that we have the VS1053 up and running, increase the internal clock multiplier and up our SPI rate
  Mp3WriteRegister(SCI_CLOCKF, 0x60, 0x00); //Set multiplier to 3.0x
 
  //From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz. 
  //Internal clock multiplier is now 3x.
  //Therefore, max SPI speed is 5MHz. 4MHz will be safe.
  SPI.setClockDivider(SPI_CLOCK_DIV4); //Set SPI bus speed to 4MHz (16MHz / 4 = 4MHz)
 
  MP3Clock = Mp3ReadRegister(SCI_CLOCKF);
  Serial.print("SCI_ClockF = 0x");
  Serial.println(MP3Clock, HEX);
 
  //MP3 IC setup complete
  
  // Define Sensors as INPUT
  pinMode(SENSOR_1, INPUT);
  pinMode(SENSOR_2, INPUT);
  pinMode(SENSOR_3, INPUT);
  pinMode(SENSOR_4, INPUT);
}
 
void loop(){
  
  // Read Sensor Values
  boolean value_1 = analogRead(SENSOR_1) >= THRESHOLD ? HIGH : LOW;
  boolean value_2 = analogRead(SENSOR_2) >= THRESHOLD ? HIGH : LOW;
  boolean value_3 = analogRead(SENSOR_3) >= THRESHOLD ? HIGH : LOW;
  boolean value_4 = analogRead(SENSOR_4) >= THRESHOLD ? HIGH : LOW;
  
  if(( value_1 == HIGH || value_2 == HIGH) && ( value_3 == LOW && value_4 == LOW))
  {
    playMP3("track001.mp3");
  }
  else if(( value_1 == HIGH && value_2 == HIGH) && ( value_3 == LOW && value_4 == LOW))
  {
    playMP3("track002.mp3");
  }
  else if(( value_1 == LOW && value_2 == LOW) && ( value_3 == HIGH || value_4 == HIGH))
  {
    playMP3("track005.mp3");
  }
  else if(( value_1 == LOW && value_2 == LOW) && ( value_3 == HIGH && value_4 == HIGH))
  {
    playMP3("track006.mp3");
  }
  else if(( value_1 == HIGH || value_2 == HIGH) && ( value_3 == HIGH || value_4 == HIGH))
  {
    playMP3("track008.mp3");
  }

  
}
 
 
//PlayMP3 pulls 32 byte chunks from the SD card and throws them at the VS1053
//We monitor the DREQ (data request pin). If it goes low then we determine if
//we need new data or not. If yes, pull new from SD card. Then throw the data
//at the VS1053 until it is full.
void playMP3(char* fileName) {
 
  uint8_t *mp3DataBuffer;
  int offset = 0;
  uint32_t size;
 
  //Serial.println("Start MP3 decoding");
 
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File track = SD.open(fileName, FILE_READ);
  if (!track)
  {
    //Serial.print(fileName);
    //Serial.println(" not found");
    // don't do anything more:
    return;
  }
 
   size = track.size();
   
   mp3DataBuffer = (uint8_t *)malloc(size);
   if (!mp3DataBuffer)
   {
      //Serial.println("Failed to alloc mem for data buffer");
      return;
   }
  //Serial.println("Track open");
 
  offset = 0;
  while (offset < size)
  {
    int nbytes = size - offset;
    int ret;
    
    /* Read no more than one page at a time */
    if (nbytes > PAGE_SIZE)
      nbytes = PAGE_SIZE;
 
    ret = track.read(mp3DataBuffer+offset, nbytes);
    if (ret < 0)
    {    
      //Serial.print("Failed to read file, error: ");
      //Serial.println(ret);
      return;
    }
    
    offset += ret;
  }
  
   //Serial.print("Read whole file, size is: ");
   //Serial.println(size);
 
  /* Start feeding data to the VS1053 */
  offset  = 0;
  digitalWrite(MP3_XDCS, LOW); //Select Data
  while(offset < size && !getCommand()) {
    //Once DREQ is released (high) we now feed 32 bytes of data to the VS1053 from our SD read buffer
    while(!digitalRead(MP3_DREQ));
 
    SPI.transferBuffer(mp3DataBuffer + offset, NULL, (size - offset) > CHUNK_SIZE ? CHUNK_SIZE : size - offset); // Send SPI bytes
    offset += CHUNK_SIZE;
    
    //getSynthInput();
  }
  digitalWrite(MP3_XDCS, HIGH); //Deselect Data
 
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating transfer is complete
 
  digitalWrite(MP3_XDCS, HIGH); //Deselect Data
  
  track.close(); //Close out this track
  free(mp3DataBuffer);
 
  //sprintf(errorMsg, "Track %s done!", fileName);
  //Serial.println(errorMsg);
}
 
//Write to VS10xx register
//SCI: Data transfers are always 16bit. When a new SCI operation comes in 
//DREQ goes low. We then have to wait for DREQ to go high again.
void Mp3WriteRegister(unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte){
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating IC is available
  digitalWrite(MP3_XCS, LOW); //Select control
 
  //SCI consists of instruction byte, address byte, and 16-bit data word.
  SPI.transfer(0x02); //Write instruction
  SPI.transfer(addressbyte);
  SPI.transfer(highbyte);
  SPI.transfer(lowbyte);
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete
  digitalWrite(MP3_XCS, HIGH); //Deselect Control
}
 
//Read the 16-bit value of a VS10xx register
unsigned int Mp3ReadRegister (unsigned char addressbyte){
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating IC is available
  digitalWrite(MP3_XCS, LOW); //Select control
 
  //SCI consists of instruction byte, address byte, and 16-bit data word.
  SPI.transfer(0x03);  //Read instruction
  SPI.transfer(addressbyte);
 
  char response1 = SPI.transfer(0xFF); //Read the first byte
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete
  char response2 = SPI.transfer(0xFF); //Read the second byte
  while(!digitalRead(MP3_DREQ)) ; //Wait for DREQ to go high indicating command is complete
 
  digitalWrite(MP3_XCS, HIGH); //Deselect Control
 
  int resultvalue = response1 << 8;
  resultvalue |= response2;
  return resultvalue;
}
 
//Set VS10xx Volume Register
void Mp3SetVolume(unsigned char leftchannel, unsigned char rightchannel){
  Mp3WriteRegister(SCI_VOL, leftchannel, rightchannel);
}
 
char getCommand(){
  return false;//this function is disabled
  
  if(Serial.available()){
    byte b=Serial.read();
    if(b=='s')
      return true;
    else 
      return false;
  }
}

Credits

Rishabh Banga

Rishabh Banga

12 projects • 135 followers
Intel Software Innovator | Microsoft Certified Professional | IoT Evangelist

Comments