Skyduino:~#
Articles
arduino, programmation, projet

Lecteur midi arduino

Le but :
Le but de ce projet est de faire un lecteur de fichier midi autonome, avec lecture depuis une carte sd, sur une base arduino.

Description :
Le but de ce projet est de réaliser un lecteur de fichier .mid autonome (dans le sens ou il n’as pas besoin d’ordinateur pour la lecture).
Le fichier .mid est lu par le parseur puis transféré avec le bon timing au séquenceur midi (ici mon synthétiseur).

News La version 2 amélioré !
Plus « pro » et plus simple à faire soit même, pour plus d’info :
https://skyduino.wordpress.com/2011/08/27/miduino-le-lecteur-de-mid-sur-base-arduino-v2/

Photo Prototype :

Projet - Player Midi Arduino

Arduino + carte SD (shield mSD)

Projet - Player Midi Arduino - 2

Vu général du prototype

Projet - Player Midi Arduino - 3

Le connecteur Midi-OUT

Projet - Player Midi Arduino - 4

L'écran lcd 2x16 (en mode 4bit)

Prototype V2:

Ligne de boutons pour la V2b


Ligne de boutons pour la V2b


Prototype V2


La carte sd sur son support bien caché


Vue arduino+SD et lcd+boutons

Video de démonstration :

Fichiers :
Le projet étant encore en développement actif pause, le zip contenant la dernière version est disponible sur le forum arduino.cc
http://arduino.cc/forum/index.php/topic,64900.msg475472.html

Voici la derniére version du projet, celui ci comporte plusieurs bug que je n’arrive pas à résoudre donc à prendre uniquement comme exemple. Le parseur midi quand à lui fonctionne parfaitement.

SkyMidiPlayer.pde (Fonction principal)

/*
 * SkyWodd Midi File Player
 * General Code Section
 * Compatible with Mr.Midi 2 Hardware (Request by PapyDuino)
 */

/* SD-Card */
#include <SD.h>
File midiFile;

/* Button */
#include "AnyPinInterrupt.h"
#include "PinMapping.h"

/* LCD-Screen */
#include <LiquidCrystal.h>
LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7);

/* PROGMEM String */
#include <avr/pgmspace.h>
#define PSTR2(s) (__extension__({static unsigned char __c[] PROGMEM = (s); &__c[0];}))

/* Midi Parseur */
byte headerBuffer[14]; // Byte Array For Storing Midi Header
byte trackBuffer[8]; // Byte Array For Storing Track Header
byte eventBuffer[8]; // Byte Array For Storing Track Event
unsigned long headerLength = 0; // integer For Storing Header Length
unsigned long trackLength = 0; // integer For Storing Track Length
unsigned int fileFormat = 0; // integer For Storing File Format
unsigned long trackNumber = 0; // integer For Storing Track Number
unsigned long deltaTime = 1; // integer For Storing Delta Time Ticks Per Quarter Note
unsigned long tempo = 500000; // integer For Storing Quarter Note Duration - Default 120 beats/minute
String playFile = ""; // String For Storing Played FileName

/* Enable or Disable Debug Serial  */
//boolean debugEnable = false; 

/* Flag For Button */
volatile boolean flagButtonMenu = false;
volatile boolean flagButtonUp = false;
volatile boolean flagButtonDown = false;
volatile boolean flagButtonLeft = false;
volatile boolean flagButtonRight = false;

/* Flag For Menu */
volatile boolean flagInMenu = false;
volatile boolean flagInPause = false;
volatile boolean flagInStop = false;
volatile boolean flagTempoUp = false;
volatile boolean flagTempoDown = false;
volatile boolean flagNextFile = false;
volatile boolean flagPreviousFile = false;

/* integer For Storing Inter Note Duration */
unsigned long waitTime = 1;

/* long for No delay timing */
long previousMillis = 0;
volatile boolean flagWeelTurn = false;
int weelMode = 0; // if true playback while play until weel turn
int weelLess = 0; // delay beteewn note - velocity weel
int weelRotate = 0;
String remaintime = "";
String strTempsTotal = "";

/* File Management */
int fileIndex = 1; // Current file on scan/parse
const int fileLimit = 50; // Limit of file scanning
char buffer[10]; // stock index as text 
char indexedFile[20]; // stock config file name as text
int fileAvailable = 0; // number of file available
long tempsTotal = 0; // remaining time (in ms)
long tempsTotalDp = 0; // duplicate
byte data[255]; // meta raw data
int fileReadDone = 0;

/* LCD */
char LCDbuffer[16];

void setup(){

  /* Start LCD Library */
  lcd.begin(LCD_COL, LCD_ROW);

  /* Show Welcome Message*/
  homescreen();

  /* Start Midi Library */
  //if(debugEnable){
#if defined(__SDEBUG__)
    Serial.begin(9600); // Debug Serial
    Serial.println("Sky Midi Player");
  //}
  //else{
#else
    Serial.begin(31250); // Midi Serial
  //}
#endif

  /* Set Button Pin As Output */
  pinMode(BUTTON_MENU, INPUT);
  pinMode(BUTTON_UP, INPUT);
  pinMode(BUTTON_DOWN, INPUT);
  pinMode(BUTTON_LEFT, INPUT);
  pinMode(BUTTON_RIGHT, INPUT);
  pinMode(BUTTON_PLAY, INPUT);
  pinMode(BUTTON_STOP, INPUT);
  pinMode(SPEED_WEEL, INPUT);

  /* Enable software pull-up */
  digitalWrite(BUTTON_MENU, HIGH);
  digitalWrite(BUTTON_UP, HIGH);
  digitalWrite(BUTTON_DOWN, HIGH);
  digitalWrite(BUTTON_LEFT, HIGH);
  digitalWrite(BUTTON_RIGHT, HIGH);
  digitalWrite(BUTTON_PLAY, HIGH);
  digitalWrite(BUTTON_STOP, HIGH);
#if !defined(__PAPYDUINO__)
  digitalWrite(SPEED_WEEL, HIGH);
#endif
  
  /* Attach Interruption On Each Button */
  //if(!debugEnable){
#if !defined(__SDEBUG__)
    PCattachInterrupt(BUTTON_MENU, buttonMenu, FALLING);
    PCattachInterrupt(BUTTON_UP, buttonUp, FALLING);
    PCattachInterrupt(BUTTON_DOWN, buttonDown, FALLING);
    PCattachInterrupt(BUTTON_LEFT, buttonLeft, FALLING);
    PCattachInterrupt(BUTTON_RIGHT, buttonRight, FALLING);
    PCattachInterrupt(BUTTON_PLAY, buttonPlay, FALLING);
    PCattachInterrupt(BUTTON_STOP, buttonStop, FALLING);

    attachInterrupt(SPEED_WEEL, speedWell, FALLING);
  //}
#endif

  pinMode(10, OUTPUT);
  pinMode(SD_SS, OUTPUT);
  if (!SD.begin(SD_SS)) {
    cardNotFound();
    while(1);
  }

  //playMidiFile("test.mid");

  fileAvailable = 0;
  for(fileIndex = 1; fileIndex < fileLimit; fileIndex++){ // check 1.mid - 2.mid - 3.mid ...
    scanFileExist(fileIndex);
  }
  fileIndex = 1;
}

void loop(){
  if(fileReadDone == fileAvailable) fileIndex = 0; 
  
  fileIndex++; // play 1.mid - 2.mid - 3.mid ...
  if(fileIndex < fileLimit)
    scanFile(fileIndex); // get time & play
}

void scanFile(int index){

  midiFile.close(); // close file
  
  for(int i=0; i<10; i++) buffer[i] = '\0';

  itoa(index, buffer, 10); // int -> text
  strcpy(indexedFile, buffer);
  strcat(indexedFile, ".mid");

  if(!SD.exists(indexedFile)){
    playFile = indexedFile;
    fileNotFound(); // If Not Drop Error Message
  }
  else{
    //playFile = indexedFile;
    flagInStop = false;
    playMidiFile(indexedFile, true); // get time
    tempsTotalDp = tempsTotal;
    //ltoa(tempsTotalDp, LCDbuffer, 10);
    //itoa((int)(tempsTotalDp / 1000), LCDbuffer, 10);
    strTempsTotal = tempsTotalDp /*LCDbuffer*/;
    flagInPause = true;
    playMidiFile(indexedFile, false); // play
    fileReadDone ++;
  }
}

void scanFileExist(int index){

  itoa(index, buffer, 10); // int -> text
  strcpy(indexedFile, buffer);
  strcat(indexedFile, ".mid");

  if(SD.exists(indexedFile)){
    fileAvailable ++;
  }
}

Midi.pde (Parseur de .mid)

/*
 * SkyWodd Midi File Player
 * Midi File Parser And Player
 * Compatible with Mr.Midi 2 Hardware (Request by PapyDuino)
 */

/* Parse And Play A .mid File */
void playMidiFile(char* fileName, boolean getTime){

  /* Check If File Exist */
  //if(!SD.exists(fileName))
  //  fileNotFound(); // If Not Drop Error Message

  /* Open Specified File */
  midiFile = SD.open(fileName,FILE_READ);

  /* Show File Rank */
  if(!getTime){
    playFile = fileName;
    playFile = playFile + " / " + fileAvailable;
  }

  if(getTime)
    tempsTotal = 0;

  /* Check For Openning Error */
  if (midiFile){

    /* Check For A Valid MIDI Header */
    if(midiFile.available() > 14){ // Standard Header Chunk is 14 byte

      for(int i=0;i<14;i++) // For All Header Chunk
        headerBuffer[i] = midiFile.read(); // Dump Byte Data

      if(validateHeader()){ // Parse And Validate Header

          for(int i=0;i<8;i++) // For All Track Chunk
          trackBuffer[i] = midiFile.read(); // Dump Byte Data

        if(validateTrack()){

          flagInStop = false;
          while((trackLength > 0) && (!flagInStop || getTime)){ // Handle Stop

            if(midiFile.available() > 0)
              processEvent(getTime);
          }

          midiFile.close();
          flagInStop = false;
          trackLength = 0;
        }
      }
    }
  }
  else{
    fileOpenError(); // If Openning Fail Drop Error Message
  }

}

/* Validate The Header Buffer If Correct Return TRUE */
boolean validateHeader(){
  int byteValidated = 0;

  /* Parse Magic File Signature */
  if(headerBuffer[0] = 0x4D) byteValidated++;
  if(headerBuffer[1] = 0x54) byteValidated++;
  if(headerBuffer[2] = 0x68) byteValidated++;
  if(headerBuffer[3] = 0x64) byteValidated++;

  if(byteValidated == 4){

    /* Compute Header Length */
    headerLength = 0;
    headerLength += headerBuffer[7];
    headerLength += headerBuffer[6] << 8;
    headerLength += headerBuffer[5] << 16;
    headerLength += headerBuffer[4] << 24;

    /* Check Header Length */
    if(headerLength == 0x06){

      /* Compute File Format */
      fileFormat = 0;
      fileFormat += headerBuffer[9];
      fileFormat += headerBuffer[8] << 8;

      /* Check File Format */
      if((fileFormat == 0x00) || (fileFormat == 0x01)){ // Parser Support Midi Type 0 & 1 
        // Midi type 0 is play in REAL TIME
        // Midi type 1 is play TRACK by TRACK

        /* Compute Track Number */
        trackNumber = 0;
        trackNumber += headerBuffer[11];
        trackNumber += headerBuffer[10] << 8;

        /* Check Track Number */
        if(trackNumber != 0x00){

          /* Compute Delta Time */
          deltaTime = 0;
          deltaTime += headerBuffer[13];
          deltaTime += (headerBuffer[12] << 8);

#if defined(__SDEBUG__)
          onScreenDebug("deltaTime",deltaTime);
#endif

          if(bitRead(deltaTime,15)){ // Division Type: Absolute or Delta-Timed
#if defined(__SDEBUG__)
            onScreenDebug("Absolute Time",1);
#endif
            fileUnsupportedFormat();  // Only Delta-Timed Supported 
            return false;
          }

          /* Check Delta Time */
          if(deltaTime > 0){

            return true;
          }
          else{
            fileBadDelta(); 
            return false;
          }

        }
        else{
          fileNoTrack(); 
          return false;
        }

      }
      else{
        fileUnsupportedFormat(); 
        return false;
      }
    }
    else{
      fileBadSizeHeader(); 
      return false;
    }
  }
  else{
    fileBadHeader();
    return false;
  }
}

/* Validate The Track Buffer If Correct Return TRUE */
boolean validateTrack(){
  int byteValidated = 0;

  /* Parse Magic File Signature */
  if(trackBuffer[0] = 0x4D) byteValidated++;
  if(trackBuffer[1] = 0x54) byteValidated++;
  if(trackBuffer[2] = 0x72) byteValidated++;
  if(trackBuffer[3] = 0x6B) byteValidated++;

  if(byteValidated == 4){

    /* Compute Track Length */
    trackLength = 0;
    trackLength += trackBuffer[3];
    trackLength += trackBuffer[2] << 8;
    trackLength += trackBuffer[1] << 16;
    trackLength += trackBuffer[0] << 24;

    /* Check Track Length */
    if(trackLength != 0x00){

      return true;
    }
    else{
      fileBadTrack();
      return false;
    }
  }
  else{
    fileBadHeader();
    return false;
  }
}

void processEvent(boolean getTime){

  if(!getTime){
    if(flagInPause)
      playerPaused();
    while(flagInPause); // Handle Pause
  }

  unsigned long deltaSpend = ReadVarLen();
  byte command = midiRead();
  byte arg1 = 0;
  byte arg2 = 0;
  if(command){
    byte nybble = (command & B11110000) >> 4;
    byte channel = (command & B1111);

    if(command<0x80){ // running status
      arg1 = midiRead();
      //if(arg1 == 0x00)
      midiCmd(command, arg1, getTime);// TODO: check what is running status !
    }
    else{
      switch(nybble){
      case 0x8: // note off
        arg1 = midiRead();
        arg2 = midiRead();
#if defined(__SDEBUG__)
        onScreenDebug("Note Off",arg1);
        onScreenDebug("Note Off",arg2);
#endif
        midiCmd(command, arg1, arg2, getTime);
        break; 
      case 0x9: // note on
        arg1 = midiRead();
        arg2 = midiRead();
#if defined(__SDEBUG__)
        onScreenDebug("Note On",arg1);
        onScreenDebug("Note On",arg2);
#endif
        midiCmd(command, arg1, arg2, getTime);
        break; 
      case 0xA: // key after-touch
        arg1 = midiRead();
        arg2 = midiRead();
#if defined(__SDEBUG__)
        onScreenDebug("Key After Touch",arg1);
        onScreenDebug("Key After Touch",arg2);
#endif
        midiCmd(command, arg1, arg2, getTime);
        break; 
      case 0xB: // control change
        arg1 = midiRead();
        arg2 = midiRead();
#if defined(__SDEBUG__)
        onScreenDebug("Control Change",arg1);
        onScreenDebug("Control Change",arg2);
#endif
        midiCmd(command, arg1, arg2, getTime);
        break;
      case 0xC: // program change
        arg1 = midiRead();
#if defined(__SDEBUG__)
        onScreenDebug("Program Change",arg1);
#endif
        midiCmd(command, arg1, getTime);
        break; 
      case 0xD: // channel after touch
        arg1 = midiRead();
#if defined(__SDEBUG__)
        onScreenDebug("Ch After Touch",arg1);
#endif
        midiCmd(command, arg1, getTime);
        break;   
      case 0xE: // pitch weel change
        arg1 = midiRead();
        arg2 = midiRead();
#if defined(__SDEBUG__)
        onScreenDebug("Pitch Weel",arg1);
        onScreenDebug("Pitch Weel",arg2);
#endif
        midiCmd(command, arg1, arg2, getTime);
        break;   
      case 0xF : // meta
#if defined(__SDEBUG__)
        onScreenDebug("Meta Cmd",command);
#endif
        processMeta(command, getTime);
        break;
      }
    }

    /* delta -> us */
    waitTime = ((((double)deltaSpend / (double)deltaTime) * (double)tempo) / 1000.0); // Problem: tempo for test.mid need to be 365854
    /* EPIK WIN (or not) - FINAL DELAY CALCUL
     deltaTime -> delta by quar note
     deltaSpend -> delta to wait beteewn note
     tempo -> number of us by quar note
     TIME TO WAIT IN US = (deltaSpend / deltaTime) * tempo
     */

    /* update remaining time*/
    if(!getTime)
      tempsTotal -= waitTime;

#if defined(__SDEBUG__)
    onScreenDebug("deltaSpend",deltaSpend);
    onScreenDebug("tempo",tempo);
    onScreenDebug("waitTime",waitTime);
#endif

    if(getTime){
      if(waitTime > 0)
        tempsTotal += waitTime;
      else
        if(fileFormat == 0x01) tempsTotal += 5;
    }
    else{
      if(waitTime > 0)
        //delayMicroseconds(waitTime); 
        delay(waitTime - weelLess); 
      else
        if(fileFormat == 0x01) delay(5); // Midi Min Frame Time
    }

    /* Refresh */
    if(!getTime){
      unsigned long currentMillis = millis();

      if(currentMillis - previousMillis > 1000) {
        previousMillis = currentMillis; 
        fileShowInfo();

        if(weelMode == 1){
          flagWeelTurn = false; // pause playback until weel restart turn
          while(!flagWeelTurn);
        }

        if(weelMode == 2){
          // increase delay until weel restart turn

          if(!flagWeelTurn){
            if(weelLess >= 1000){ // weel stop >5sec - pause playback
              flagWeelTurn = false; 
              while(!flagWeelTurn);
              delay(1000); // start tempo
              weelLess = 0;
            }
            else{
              weelLess += 200;
            }
          }
          else{
            weelLess = 0;
          }

          flagWeelTurn = false;
        }

        if(weelMode == 3){
          // tempo start - tempo stop

          if(!flagWeelTurn){
            if(weelRotate >= 5){
              flagWeelTurn = false; 
              while(!flagWeelTurn);
              delay(1000); // start tempo
              weelRotate = 0;
            }
            else{
              weelRotate ++;
            }
          }
          else{
            weelRotate = 0;
          }

          flagWeelTurn = false;
        }
      }
    }
  }
  else{
    fileNoCommand(); // if trigerred = problem !
  }
}

/* Delta Time - Variable Size - 4 Byte Max */
unsigned long ReadVarLen(){
  unsigned long value = 0;
  byte c;
  if ((value = midiRead()) & 0x80){
    value &= 0x7f;
    do
    {
      value = (value <<7) + ((c = midiRead()) & 0x7f);
    } 
    while (c & 0x80);
  }
  return (value);
}

byte midiRead(){
  trackLength --;
  return midiFile.read();
}

void midiCmd(byte cmd, byte arg1, byte arg2, boolean getTime) {
  if(getTime)return;
  //if(debugEnable){
#if defined(__SDEBUG__)
  Serial.print("PIANO: ");
  Serial.print(cmd, HEX);//BYTE);
  Serial.print("-");
  Serial.print(arg1, HEX);//BYTE);
  Serial.print("-");
  Serial.print(arg2, HEX);//BYTE);
  Serial.println();
  /*}
   else{*/
#else
  Serial.print(cmd, BYTE);
  Serial.print(arg1, BYTE);
  Serial.print(arg2, BYTE);
  //}
#endif
}

void midiCmd(byte cmd, byte arg1, boolean getTime) {
  if(getTime)return;
  //if(debugEnable){
#if defined(__SDEBUG__)
  Serial.print("PIANO: ");
  Serial.print(cmd, HEX);//BYTE);
  Serial.print("-");
  Serial.print(arg1, HEX); //BYTE);
  Serial.println();
  /*}
   else{*/
#else
  Serial.print(cmd, BYTE);
  Serial.print(arg1, BYTE);
  //}
#endif
}

void processMeta(byte cmd, boolean getTime) {
  //byte data[255]; // risk of buffer/memory overflow
  if (cmd == 0xFF){
    byte metaCmd = midiRead();
    unsigned long dataLen = ReadVarLen();

    if(dataLen > 0){
      int i;
      for(i = 0; i < dataLen; i++)
        data[i] = midiRead();
      data[i++] = '\0'; // String escape
    }

#if defined(__SDEBUG__)
    onScreenDebug("Meta-Cmd",metaCmd);
    onScreenDebug("Meta-Len",dataLen);
#endif

    switch(metaCmd){
    case 0x00: // Sets the track's sequence number
#if defined(__SDEBUG__)
      onScreenDebug("Track NB",0);
#endif
      if(dataLen == 0x02){
        unsigned int sequenceNumber = (data[0] << 8) & data[1];
        // TODO: do something with this !
#if defined(__SDEBUG__)
        onScreenDebug("Sequence NB",sequenceNumber);
#endif
      }
      break;
    case 0x01: // Text event- any text you want.
#if defined(__SDEBUG__)
      onScreenDebug("Text Event",0);
#endif
      if(dataLen > 0){
        // TODO: do something with this !
#if defined(__SDEBUG__)
        onScreenDebug("Data",data, dataLen);
#endif
        if(!getTime)
          showMetaText(PSTR2("Text Event"),data, dataLen);
      }
      break;
    case 0x02: // Same as text event, but used for copyright info.
#if defined(__SDEBUG__)
      onScreenDebug("Copyright",0);
#endif
      if(dataLen > 0){
        // TODO: do something with this !
#if defined(__SDEBUG__)
        onScreenDebug("Copyright",data, dataLen);
#endif
        if(!getTime)
          showMetaText(PSTR2("Copyright"),data, dataLen);
      }
      break;
    case 0x03: // Sequence or Track name
#if defined(__SDEBUG__)
      onScreenDebug("Track Name",0);
#endif
      if(dataLen > 0){
        // TODO: do something with this !
#if defined(__SDEBUG__)
        onScreenDebug("Track Name",data, dataLen);
#endif
        if(!getTime)
          showMetaText(PSTR2("Track Name"),data, dataLen);
      }
      break;
    case 0x04: // Track instrument name
#if defined(__SDEBUG__)
      onScreenDebug("Track Instr",0);
#endif
      if(dataLen > 0){
        // TODO: do something with this !
#if defined(__SDEBUG__)
        onScreenDebug("Track Instr",data, dataLen);
#endif
        if(!getTime)
          showMetaText(PSTR2("Instrument"),data, dataLen);
      }
      break;
    case 0x05: // Lyric
#if defined(__SDEBUG__)
      onScreenDebug("Lyric",0);
#endif
      if(dataLen > 0){
#if defined(__SDEBUG__)
        onScreenDebug("Lyric",data, dataLen);
#endif
        // TODO: do something with this !
        if(!getTime)
          showMetaText(PSTR2("Lyric"),data, dataLen);
      }
      break;
    case 0x06: // Marker
#if defined(__SDEBUG__)
      onScreenDebug("Marker",0);
#endif
      if(dataLen > 0){
#if defined(__SDEBUG__)
        onScreenDebug("Marker",0);
#endif
        // TODO: do something with this !
        if(!getTime)
          showMetaText(PSTR2("Marker"),data, dataLen);
      }
      break;
    case 0x07: // Cue point
#if defined(__SDEBUG__)
      onScreenDebug("Cue Point",0);
#endif
      if(dataLen > 0){
#if defined(__SDEBUG__)
        onScreenDebug("Cue Point",data, dataLen);
#endif
        // TODO: do something with this !
        if(!getTime)
          showMetaText(PSTR2("Cue Point"),data, dataLen);
      }
      break;
    case 0x20: // Midi Channel Prefix
#if defined(__SDEBUG__)
      onScreenDebug("Midi Channel",0);
#endif
      if(dataLen == 0x01){
#if defined(__SDEBUG__)
        onScreenDebug("Midi Channel",(int)data[0]);
#endif
        // TODO: do something with this !
        if(!getTime)
          showMetaText(PSTR2("Midi Channel"),data, dataLen, DEC);
      }
      break;
    case 0x2F: // This event must come at the end of each track
#if defined(__SDEBUG__)
      onScreenDebug("Track End",0);
#endif
      if(dataLen == 0x00){
#if defined(__SDEBUG__)
        onScreenDebug("Track End",1);
#endif
        if(midiFile.available() > 8){
          for(int i=0;i<8;i++)
            trackBuffer[i] = midiFile.read();
          if(!validateTrack()){ // No more track
#if defined(__SDEBUG__)
            onScreenDebug("Track End",2);
#endif
            trackLength = 0;
            flagInStop = true;
          }
        }
        else{ // No more track
#if defined(__SDEBUG__)
          onScreenDebug("Track End",2);
#endif
          trackLength = 0;
          flagInStop = true;
        }
      }
      break;
    case 0x51: // Set tempoSet tempo
#if defined(__SDEBUG__)
      onScreenDebug("Tempo",0);
#endif
      if(dataLen == 0x03){ 
        tempo = 1; 
        tempo += data[0];
        tempo = tempo << 8;
        tempo = tempo << 8;

        tempo += data[1] << 8;
        tempo += data[2];
#if defined(__SDEBUG__)
        onScreenDebug("Tempo", tempo);
        onScreenDebug("Tempo A", (unsigned int)data[2]);
        onScreenDebug("Tempo B", (unsigned int)data[1]);
        onScreenDebug("Tempo C", (unsigned int)data[0]);
#endif
        //midiCmd(0xFF,0x51,0x03); // Disabled for test 
        //midiCmd((tempo >> 16) & 0xFF,(tempo >> 8) & 0xFF,tempo & 0xFF); // replay tempo setup for sequencer
      }
      break;
    case 0x54: // SMTPE Offset - smtpe not supported !
#if defined(__SDEBUG__)
      onScreenDebug("SMTPE Offset",0);
#endif
      if(dataLen == 0x05){
        byte hours = data[0];
        byte minutes = data[1];
        byte seconds = data[2];
        byte frames = data[3];
        byte fractional = data[4];
#if defined(__SDEBUG__)
        onScreenDebug("hours",hours);
        onScreenDebug("minutes",minutes);
        onScreenDebug("seconds",seconds);
        onScreenDebug("frames",frames);
        onScreenDebug("fractional",fractional);
#endif
        // TODO: do something with this !
      }
      break;
    case 0x58: // Time Signature
#if defined(__SDEBUG__)
      onScreenDebug("Track Time",0);
#endif
      if(dataLen == 0x04){
        byte numerator = data[0];
        byte denominator = data[1];
        byte metronomeTick = data[2];
        byte note32NdNumber = data[3];

#if defined(__SDEBUG__)
        onScreenDebug("numerator",numerator);
        onScreenDebug("denominator",denominator);
        onScreenDebug("metronomeTick",metronomeTick);
        onScreenDebug("note32NdNumber",note32NdNumber);
#endif
        // TODO: do something with this !
      }
      break;
    case 0x59: // Key signature
#if defined(__SDEBUG__)
      onScreenDebug("Track Key",0);
#endif
      if(dataLen == 0x02){
        byte sharpsFlats = data[0];
        byte majorMinor = data[1];
#if defined(__SDEBUG__)
        onScreenDebug("sharpsFlats",sharpsFlats);
        onScreenDebug("majorMinor",majorMinor);
#endif
        // TODO: do something with this !
      }
      break;
    case 0x7F: // Sequencer specific information
#if defined(__SDEBUG__)
      onScreenDebug("sequencer Spec",0);
#endif
      if(dataLen > 0){
#if defined(__SDEBUG__)
        onScreenDebug("Data",data, dataLen);
#endif
        // TODO: do something with this !
        if(!getTime)
          showMetaText(PSTR2("Sequencer"),data, dataLen);
      }
      break;
    }
  }
  else{
    word dataLen =0;
    switch(cmd){
    case 0xF8: // Timing Clock Request
      // TODO: nothing xD
#if defined(__SDEBUG__)
      onScreenDebug("Timing Rq",0);
#endif
      break;
    case 0xFA: // Start Sequence
#if defined(__SDEBUG__)
      onScreenDebug("Start Sequence",0);
#endif
      flagInPause = false;
      break;
    case 0xFB: // Continue Stopped Sequence
#if defined(__SDEBUG__)
      onScreenDebug("Resume Sequence",0);
#endif
      flagInPause = false;
      break;
    case 0xFC: // Stop Sequence
#if defined(__SDEBUG__)
      onScreenDebug("Stop Sequence",0);
#endif
      flagInPause = true;
      break;
      /* SySex Midi Event - Not Supported */
    case 0xF0: // sysex event
      dataLen = ReadVarLen();
      if(dataLen > 0){
        int i;
        for(i = 0; i < dataLen; i++) // POSSIBLE ERROR ! MSB/LSB
          data[i] = midiRead();
        data[i++] = '\0'; // String escape
      }
#if defined(__SDEBUG__)
      onScreenDebug("sysSex 1",dataLen);
      onScreenDebug("sysSex 1",data, dataLen);
#endif
      break;
    case 0xF7: // sysex event
      dataLen = ReadVarLen();
      if(dataLen > 0){
        int i;
        for(i = 0; i < dataLen; i++) // POSSIBLE ERROR ! MSB/LSB
          data[i] = midiRead();
        data[i++] = '\0'; // String escape
      }
#if defined(__SDEBUG__)
      onScreenDebug("sysSex 2",dataLen);
      onScreenDebug("sysSex 2",data, dataLen);
#endif
      break;

    }
    fileBadCommand();
  }
}

LCD.pde (Gestion de l’écran lcd)

/*
 * SkyWodd Midi File Player
 * LCD Handle Manager
 * Compatible with Mr.Midi 2 Hardware (Request by PapyDuino)
 */

void homescreen(){
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print(".Midi Player");
  lcd_print(PSTR2(".Midi Player"));
  lcd.setCursor(0, 1);
  //lcd.print("by SkyWodd DSC");
  lcd_print(PSTR2("by SkyWodd DSC"));
  delay(1000);
}

void cardNotFound(){
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("SkyWodd DSC");
  lcd_print(PSTR2("SkyWodd DSC"));
  lcd.setCursor(0, 1);
  //lcd.print("No SD Card");
  lcd_print(PSTR2("No SD Card"));
}

void fileNotFound(){
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("SD Card");
  lcd_print(PSTR2("SD Card"));
  lcd.setCursor(0, 1);
  //lcd.print("File Not Found");
  lcd_print(PSTR2("File Not Found"));
  midiFile.close();
}

void fileOpenError(){
  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print("SD Card");
  lcd_print(PSTR2("SD Card"));
  lcd.setCursor(0, 1);
  //lcd.print("File Open Error");
  lcd_print(PSTR2("File Open Error"));
  midiFile.close();
}

void fileBadHeader(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("Bad File Header");
  lcd_print(PSTR2("Bad File Header"));
  midiFile.close();
}

void fileBadSizeHeader(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("Bad Header Size");
  lcd_print(PSTR2("Bad Header Size"));
  midiFile.close();
}

void fileUnsupportedFormat(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("Bad Format");
  lcd_print(PSTR2("Bad Format"));
  midiFile.close();
}

void fileNoTrack(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("No Track");
  lcd_print(PSTR2("No Track"));
  midiFile.close();
}

void fileBadDelta(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("Bad Delta"); 
  lcd_print(PSTR2("Bad Delta")); 
  midiFile.close();
}

void fileBadTrack(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("Bad Track");
  lcd_print(PSTR2("Bad Track"));
  midiFile.close();
}

void fileBadCommand(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("Bad Command");
  lcd_print(PSTR2("Bad Command"));
}

void fileNoCommand(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("No Command");
  lcd_print(PSTR2("No Command"));
}

void fileShowInfo(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //ltoa(tempsTotal, LCDbuffer, 10);
  itoa((int)(tempsTotal / 1000), LCDbuffer, 10);
  remaintime = LCDbuffer;
  remaintime += " / " + strTempsTotal;
  lcd.print(remaintime);
}

void playerPaused(){
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(playFile);
  lcd.setCursor(0, 1);
  //lcd.print("PAUSE");
  lcd_print(PSTR2("PAUSE"));
}

void lcd_print(prog_uchar *data){
  byte i =0;
  for(int i =0; i<16; i++)
    LCDbuffer[i] = ' '; // clear buffer

  LCDbuffer[0] = (char)pgm_read_byte(data);
  while(pgm_read_byte(data) != 0x00){
    if(i >=16) break;
    LCDbuffer[i++] = (char)(pgm_read_byte(data++));
  }
  lcd.print(LCDbuffer);
}

void showMetaText(/*char**/ prog_uchar *type, byte* text, int len, int style = 15){
  if(len <= 0) return; 
  if(len > 16) len = 16;

  for (int i = 0; i < len; i++)
    LCDbuffer[i] =(char)text[i];


  lcd.clear();
  lcd.setCursor(0, 0);
  //lcd.print(type);
  lcd_print(type);

  lcd.setCursor(0, 1);
  if(style == 15)
    lcd.print(LCDbuffer);
  else
    for (int i = 0; i < len; i++)
      lcd.print(LCDbuffer[i], style);

}

#if defined(__SDEBUG__)
/* Sequencer Not Connected Debug */
void onScreenDebug(char* variable, byte* content , int len){
  //if(debugEnable){
  Serial.println(variable);
  for(int i =0; i < len; i++){
    Serial.println(content[i],HEX);
  }
  //}
}

void onScreenDebug(char* variable, unsigned long content){
  //if(debugEnable){
  Serial.println(variable);
  Serial.println(content);
  //}
}

void onScreenDebug(char* variable, unsigned int content){
  //if(debugEnable){
  Serial.println(variable);
  Serial.println(content);
  //}
}

void onScreenDebug(char* variable, int content){
  //if(debugEnable){
  //Serial.println("BUG");
  //Serial.println(content/*,DEC);
  //}
}
#endif

HandleButton.pde (Gestion des boutons de menu)

/*
 * SkyWodd Midi File Player
 * Button Manager
 * Compatible with Mr.Midi 2 Hardware (Request by PapyDuino)
 */

//char* wellModeMsg[] = {
//  "OFF","1sec","tempo","delay"};

void buttonMenu(){
  //flagInMenu = true;
  if(weelMode == 3)
    weelMode = 0;
    
  if(weelMode == 2)
    weelMode = 3;
    
  if(weelMode == 1)
    weelMode = 2;
    
  if(weelMode == 0)
    weelMode = 1;

  flagInPause = true;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd_print(PSTR2("Weel Mode"));
  lcd.setCursor(0, 1);
  switch(weelMode){
  case 3:
    lcd_print(PSTR2("delay"));
    break;
  case 2:
    lcd_print(PSTR2("tempo"));
    break;
  case 1:
    lcd_print(PSTR2("1sec"));
    break;
  case 0:
    lcd_print(PSTR2("OFF"));
    break;
  }
  for(int i=0; i < 40000; i++) asm("NOP"); // debounce
  //lcd.print(wellModeMsg[weelMode]);
}

void buttonUp(){
  if(!flagInMenu)
    flagTempoUp = true;
  else
    flagButtonUp = true;
  queueButton();
}

void buttonDown(){
  if(!flagInMenu)
    flagTempoDown = true;
  else
    flagButtonDown = true;
  queueButton();
}

void buttonLeft(){
  if(!flagInMenu)
    flagPreviousFile = true;
  else
    flagButtonLeft = true;
  queueButton();
}

void buttonRight(){
  if(!flagInMenu)
    flagNextFile = true;
  else
    flagButtonRight = true;
  queueButton();
}

void buttonPlay(){
  flagInPause = !flagInPause;
}

void buttonStop(){
  flagInStop = !flagInStop;
}

void speedWell(){
  if(weelMode > 0){
    flagWeelTurn = true;
  }
}

void queueButton(){
  if(!flagInMenu){
    if(flagTempoUp){
      tempo -= 50; // speed up music
      flagTempoUp = false;
    }
    if(flagTempoDown){
      tempo += 50; // speed up music
      flagTempoDown = false;
    }
    if(flagNextFile){
      flagInStop = true; // stop current file and let loop start next
      flagNextFile = false;
    }
    if(flagPreviousFile){
      if(fileIndex > 2){
        fileIndex -= 2; // decrease by 2 file index, stop current file and let loop start next (-2+1 = -1 - previous file)
        flagInStop = true;
        flagPreviousFile = false;
      }
    }
  }
  for(int i=0; i < 40000; i++) asm("NOP"); // debounce
}

AnypinInterrupt.h (Librairie PCint)

#include "pins_arduino.h"

volatile uint8_t *port_to_pcmask[] = {
  &PCMSK0,
  &PCMSK1,
  &PCMSK2
};

static int PCintMode[24];

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = { 
  NULL };

volatile static uint8_t PCintLast[3];

/*
 * attach an interrupt to a specific pin using pin change interrupts.
 */
 void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t slot;
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

// -- Fix by Baziki. In the original sources it was a little bug, which cause analog ports to work incorrectly.
  if (port == 1) {
     slot = port * 8 + (pin - 14);
  }
  else {
     slot = port * 8 + (pin % 8);
  }
// --Fix end
  PCintMode[slot] = mode;
  PCintFunc[slot] = userFunc;
  // set the mask
  *pcmask |= bit;
  // enable the interrupt
  PCICR |= 0x01 << port;
}

void PCdetachInterrupt(uint8_t pin) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // disable the mask.
  *pcmask &= ~bit;
  // if that's the last one, disable the interrupt.
  if (*pcmask == 0) {
    PCICR &= ~(0x01 << port);
  }
}

// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port];
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      // Trigger interrupt if mode is CHANGE, or if mode is RISING and
      // the bit is currently high, or if mode is FALLING and bit is low.
      if ((PCintMode[pin] == CHANGE
          || ((PCintMode[pin] == RISING) && (curr & bit))
          || ((PCintMode[pin] == FALLING) && !(curr & bit)))
          && (PCintFunc[pin] != NULL)) {
        PCintFunc[pin]();
      }
    }
  }
}


SIGNAL(PCINT0_vect) {
  PCint(0);
}
SIGNAL(PCINT1_vect) {
  PCint(1);
}
SIGNAL(PCINT2_vect) {
  PCint(2);
}

PinMapping.h (Définition hardware)

/*
 * SkyWodd Midi File Player
 * PinMap Definition
 * Compatible with Mr.Midi 2 Hardware (Request by PapyDuino)
 */
 
//#define __PAPYDUINO__ 1 // uncomment if your using papyduino hardware
 
//#define __SDEBUG__ 1 // uncomment to run in debug mode << DON'T CONNECT TO ANY MIDI DEVICE >>

/* SD Card PinMap */
#if defined(__PAPYDUINO__)
#define SD_SS 10 // papyduino hardware
#else
#define SD_SS 4 // skywodd hardware
#endif

/* LCD PinMap */
#if defined(__PAPYDUINO__) // papyduino hardware
#define LCD_RS A4
#define LCD_E A5
#define LCD_D4 A0
#define LCD_D5 A1
#define LCD_D6 A2
#define LCD_D7 A3
#else // skywodd hardware
#define LCD_RS A5
#define LCD_E A4
#define LCD_D4 A3
#define LCD_D5 A2
#define LCD_D6 A1
#define LCD_D7 A0
#endif

/* Button PinMap */
#if defined(__PAPYDUINO__)
#define BUTTON_MENU 4 // papyduino hardware
#define BUTTON_UP 6
#define BUTTON_DOWN 5
#define BUTTON_LEFT 8
#define BUTTON_RIGHT 9
#define BUTTON_PLAY 7
#define BUTTON_STOP 3
#define SPEED_WEEL 2
#else
#define BUTTON_MENU 2 // skywodd hardware
#define BUTTON_UP 3
#define BUTTON_DOWN 5
#define BUTTON_LEFT 6
#define BUTTON_RIGHT 7
#define BUTTON_PLAY 8
#define BUTTON_STOP 9
#define SPEED_WEEL 10
#endif

/* LCD PROPERTIES */
#define LCD_ROW 2
#define LCD_COL 16

Discussion

2 réflexions sur “Lecteur midi arduino

  1. Joli montage!
    C’est sur, avec des shields, çà simplifie le cablage. Ou est cachée la carte SD, je ne la vois pas.
    Moi, pour le moment, pour les accessoires, c’est de la récup et du détournement. Il faut que j’apprenne à mettre des photos!
    PapyDuino

    Publié par PapyDuino | 7 juillet 2011, 4 h 53 min

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

Skyduino devient Carnet du Maker

Le site Carnet du Maker remplace désormais le blog Skyduino pour tout ce qui touche à l'Arduino, l'informatique et au DIY.