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 :
Prototype V2:
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









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 minJ’ai ajouté deux autre photo 😉 le montage sur shield c’est pour la facilité de codage, c’est toujours plus pratique que d’avoir tout une planche avec les truc dessus à ce trimballer.
Publié par skywodd | 7 juillet 2011, 11 h 56 min