Xylophon, Marimba und Vibraphon sind richtig teure Instrumente. Und Platz benötigen sie auch. Es gibt auch elektronische Varianten, die sich dann aber auch preislich immer noch im unteren Tausender-Bereich bewegen.

Zum Antesten des Instruments, zum einfachen Üben und auch als Arduino-Projekt hat sich das jetzt einfach angeboten.

Als Basis dienten mir hier zahlreiche Quellen zu E-Drum-Sets, deren Hardware und deren Programmcode.

Ich habe dazu mal ein paar Quellen zusammengestellt.

MIDI Drum Kit:
https://todbot.com/arduino/sketches/midi_drum_kit/midi_drum_kit.pde

E-Drum Set:
http://blog.georgmill.de/2011/03/22/e-drumset-selbst-gebaut/

Drumkit:
https://www.spikenzielabs.com/learn/drumkitkit.html

Und es gibt auch bereits ein kleines Xylophone. Ich wollte dann aber eher Holz als Kunststoff benutzen, wobei das im Endeffekt nur eine optische Sache ist.

Arduino Xylophone:
https://jdeboi.com/projects/2011/xylophone.html

Und der Quellcode:
https://github.com/jdeboi/xylophone/blob/master/new_code/Processing_toPD/Processing_toPD.pde

Für Arduino und MIDI gibt es auch zahlreiche Quellen.

MIDI Tutorial:
https://learn.sparkfun.com/tutorials/midi-tutorial/advanced-messages

Midi:

https://fortyseveneffects.github.io/arduino_midi_library/a00032.html#ga87eb10a0b528a55fb30b1152d34a6f2f

https://newt.phys.unsw.edu.au/jw/notes.html

http://www.philrees.co.uk/articles/midimode.htm

 https://www.youtube.com/playlist?list=PL4_gPbvyebyH2xfPXePHtx8gK5zPBrVkg

https://www.instructables.com/id/Send-and-Receive-MIDI-with-Arduino/

Im Endeffekt nutze ich keine spezielle MIDI-Library, da dies meiner Meinung nach zu viel “Overhead” für ein paar Befehle sind. Ich wollte den Code möglichst schlank halten und das geht auch mit einfachen Send-Befehlen über die serielle Schnittstelle.

Eine Herausforderung war der geplante Tonumfang des Instruments von 3 1/2 Oktaven. Ist der Arduino schnell genug, um diese Anzahl der Sensoren abzufragen? Ja, ist er. Ich habe den Analog-Wandler noch etwas ge-tuned, damit er schneller wird (damit auch etwas ungenauer, aber das spielt hier eher keine Rolle).

Im Prinzip werden Piezo-Wandler über Analog-Multiplexer abgefragt und ausgewertet. Das Feintuning der Sensoren erfolgt nicht über die Hardware, sondern über die Software. Hier sind die Faktoren Eingangspegel und Verzögerung beim Auslesen des schwingenden Sensors wichtig.

Zum Aufbau:

Mein Ziel ist ein Xylophon mit 3 1/2 Oktaven. Dazu habe ich ein Grundbrett mit 120 x 30 cm genommen. Das ist ein Standard-Maß bei Regalbrettern. Ein einfaches Fichtenbrett gibt es für ein paar Euro.

Für die Plättchen habe ich Buchenleisten genommen. 4 cm breit, 5 mm stark. Die Leisten sind auch recht preiswert. Diese habe ich auf 12,5 cm lange Plättchen geschnitten und dann mit dem Schleifer abgerundet und die Oberfläche geglättet. Das ist wichtig, wenn man das Instrument später mit Marimba-Schlägeln spielen möchte. Diese sind mir Garn umsponnen und bei einer rauhen Oberfläche würden diese schnell aufgearbeitet. Alternativ könnte man die Plättchen auch lackieren und schleifen. Das war mir jetzt zu aufwändig. Für eine bessere Optik habe ich das Grundbrett dunkel lasiert.

Die Piezo-Sensoren werden mit Zwei-Komponenten-Kleber an die Buchenleisten geklebt. Diese werden über Moosgummi gedämpft. Hier gibt es Moosgummi zum Kleben auf Rolle, das lässt sich einfach verarbeiten.

Bei 3 1/2 Oktaven das ganze dann 42 mal…

Die Plättchen müssen jetzt angeordnet werden. Mit 5 mm Abstand zueinander geht das recht gut.

Dann werden die Löcher für die Kabeldurchführungen gebohrt.

So schaut das ganze dann komplett verklebt von vorne …

… und von hinten aus.

Dies war der erste Testaufbau für den Piezo-Sensor mit MIDI-Ausgang.

Jetzt geht es ans Löten. Es müssen 42 Plättchen mit je zwei Kabeln an die Ananlog-Multiplexer angeschlossen werden. (Schaltplan weiter unten)

 

Dann wird das Bedien-Panel aufgebaut. Ein 1,8″ TFT Display mit vier Tastern. Diese habe ich über Hardware entprellt.

Und hier die Anschlüsse: Sustain-Pedal, Stromversorgung am Arduino und MIDI-Buchse (von links nach rechts)

Der Blick von Oben auf die Haupt-Elektronik.

Das Display verbaut und im Einsatz.

Schaltplan

Hier die Verkabelungen als Schema. Das ist keine Hexerei, nur viel Aufwand. Es empfiehlt sich, die einzelnen Plättchen von unten zu beschriften, dann hat man es beim Zusammenbau leichter.

Des Schema für den Piezo-Anschluss muss dann 42 mal aufgebaut werden, die Taster fünf mal. Das Sustain-Pedal wird wie ein Taster angeschlossen.

Jetzt zum Programm-Code.

Das Hauptprogramm gliedert sich in mehrere Unterprogramme:

// midiLoopback();

Wird momenatn noch nicht benutzt. Es fehlt noch ein MIDI-In über einen Optokoppler.

readSensors();

Hier werden alle Sensoren in Arrays gelesen. Dabei werden die Eingänge der Multiplexer gleichzeitig angesteuert, und der Analog-Wert über drei Eingänge gelesen und gespeichert.

checkSensors();

Hier werden die gelesenen Werte ausgewertet. Wurde ein Sensor bereits ausgelöst, dann wird eine bestimmte Zeit gewartet, bevor ein weiterer Sensorwert ausgewertet wird. Dadurch kann der Piezo ausschwingen. AUch wird die Anschlagdauer festgelegt und dann der entsprechende MIDI-Ton gesendet. Über die Oktave als Offset wird das Ton-Array entsprechend ausgelesen.

Der dynamische Anschlag hat sich für mich als noch nicht zweckmäßig herausgestellt. Aus diesem Grund habe ich ihn erst einmal deaktiviert.

checkPedal();

Das Sustain-Pedal wird ausgewertet. Dies geschieht unabhängig von der gespielten Note.

InstrumentChange();

Ich verwende zur Tonausgabe den MIDI-Prozessor Ketron SD4. Hier habe ich in die User-Bank ein paar Instrumente gespeichert, die ich dann über den Program-Change ausrufe. Die Taste toggelt durch die verschiedenen Instrumente. Hier kann man die Liste beliebig kürzen oder erweitern.

Je nach MIDI-Prozessor ist hier Anpassung angesagt.

checkOctave();

Die Octave-Taste wird abgefragt und toggelt MID-LOW-MID-HIGH-MID-…

Hier der komplette Programmcode:

/* ******************************************************************************
 * X Y L U I N O
 * v0.75
 * using MIDI Library
 * using 1,7" TFT
 * 
 * History:
 * --------
 * 
 * 2020/10/03 Erster Code-Entwurf
 * 
 * 2020/11/24 Aktivierung für 3x 8-fach AD-Multiplexer
 *            Code-Eergänzung für schnellere AD-Wandlung
 *     
 * 2020/12/02 Test-Routine Output zur Kalibrierung der Anschlagdynamik
 * 
 * 2020/12/06 TFT-Display und Keypad 
 * ******************************************************************************
 */

/* 
 * Pin-Belegung für Multiplexer
 *    Multiplex1 D2
 *    Multiolex2 D3
 *    Multiplex3 D4
 *    Multiplex4 D5
 *  
 * Analog Input der Multiplexer
 *    AnalogIn1 A0
 *    AnalogIn2 A1
 *    AnalogIn3 A2
 *     
 * Taster-Belegung an den IO-Ports
 *    Taster1 D6
 *    Taster2 D7
 *    Taster3 D12
 *    Taster4 A3
 *  
 * Display (Pin) - Arduino (Nano)
 *  GND  (1) - GND  
 *  VCC  (2) - 5V
 *  SCK  (3) - D13
 *  SDA  (4) - D11
 *  RES  (5) - D8
 *  RS   (6) - D9
 *  CS   (7) - D10
 *  LEDA (8) - 3.3V
 *  
 * Sustain
 *    A4
 *
 * Zu freien Verfügung
 *    Multiplexer 3, Kanäle 11,12,13,14,15
 * 
 */

//*******************************************************************************************************************
// Definitions    
//*******************************************************************************************************************

#include <TFT.h>                // TFT Library
#include <SPI.h>                // SPI für die Kommunikation

#define midichannel 0;   

#define TFTcs 10
#define TFTdc 9
#define TFTrst 8

TFT TFTscreen = TFT(TFTcs, TFTdc, TFTrst);

/*
 * Midi-Noten
 * C2: 36
 * C4: 60
 * A4 (440Hz) : 69
 * C7: 96 (nicht mehr spielbar)
 * 
 * normal: C3, C4, C5, C6 (->H7)
 * tiefer: C2, C3, C4, C5 (->H6)
 */

 // Midi-Notenwerte
unsigned char PadNote[72] = {36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107};

// Threshold Werte, Anschlagsempfindlichkeit
// Tuning-Bedarf ! Ch 10
int PadCutOff[48] = {300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300};

// time each note remains on after being hit 
int MaxPlayTime[48] = {50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50};

// array of flags of pad currently playing
boolean activePad[48] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

// counter since pad started to play
int PinPlayTime[48] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 

int thisPlayTime = 0;

// Anschlagdynamik = true
boolean VelocityFlag = false;
                                                 
int hitavg = 0;
int pad = 0;
unsigned char status;

// Sustain-Pedal
boolean SustainStatus = false;
boolean LastSustainStatus = false;

// Instrumente
boolean InstrumentStatus = false;
boolean LastInstrumentStatus = false;
int InstrumentCounter = 0;
int Instrument[8] = {99, 98, 100, 3, 102, 103, 104, 105}; 
char* InstrumentName[] = {"Marimba      ", "Vibraphon    ", "Xylophon     ", "Glockenspiel", "Hackbrett   ", "Kalimba     ", "Wood Mallet ", "Yellow      "};

byte byte1;
byte byte2;
byte byte3;

int r0 = 0;      //value of select pin at the 4051 (s0)
int r1 = 0;      //value of select pin at the 4051 (s1)
int r2 = 0;      //value of select pin at the 4051 (s2)
int r3 = 0;      //value of select pin at the 4051 (s3)

int count = 0;   //which y pin we are selecting

// multiplex-Array für die gelesenen Analog-Werte
int multiplex[48];

// octave in Halbtonschritten, steuerbar über Taster
// std.: 12, bei shift 0
int octave = 12;
int lastoctave = 24;
boolean OctaveStatus = false;
boolean LastOctaveStatus = false;

int VThreshold = 0;
int VAmp = 0;

int longcount = 0;

//  Making analog readings faster (for drumrolls) works with this code
// read http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1208715493/11 for more info
#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

  
//*******************************************************************************************************************
// Setup     
//*******************************************************************************************************************
 
void setup() {
  // put your setup code here, to run once:
  
  /***
   * ST7735-Chip initialisieren (INITR_BLACKTAB / INITR_REDTAB / INITR_GREENTAB)
   * Muss bei AZ-Delivery 1.77'' 160x128px RGB TFT INITR_GREENTAB sein ansonsten Pixelfehler rechts und unten.
   * Hinweis: https://github.com/adafruit/Adafruit-ST7735-Library/blob/master/examples/soft_spitftbitmap/soft_spitftbitmap.ino#L52
   * Zeile 52-65  
   ***/

   // Start TFT Display
  TFTscreen.begin();
  TFTscreen.background(0, 0, 0);
  TFTscreen.stroke(255, 255, 255);
  TFTscreen.fill(0, 0, 0);
  TFTscreen.setRotation(3);
  TFTscreen.setTextSize(2);
  TFTscreen.text("Xyluino V0.75", 5, 5);
  TFTscreen.setTextSize(1);
  TFTscreen.text("50ms", 5, 25);
  // TFTscreen.text("Octave: 0", 5, 35);
  TFTscreen.text("Instrument: Marimba", 5, 45);
  TFTscreen.text("Sustain active", 5, 55);
  TFTscreen.text("Octave Mid  ", 5, 65);
  

  // Control Multiplexer
  pinMode(2,OUTPUT);    // s0
  pinMode(3,OUTPUT);    // s1
  pinMode(4,OUTPUT);    // s2
  pinMode(5,OUTPUT);    // s2

  // Analog Input
  // A0, A1, A2
  
  // Taster
  pinMode(6, INPUT);    // Taster blau
  pinMode(7, INPUT);    // Taster grau
  pinMode(12, INPUT);   // Taster grün
  pinMode(A3, INPUT);   // Taster gelb
  pinMode(A4, INPUT);   // Taster braun (Sustain-Pedal)
  
  // Serial.begin(115200);
  Serial.begin(31250);                                 // connect to the serial port (31250) //midi standard

 // Set Standard Program Number
 MIDI_PC(99);  // Marimba
 
#if FASTADC
  // set prescale to 16
  sbi(ADCSRA,ADPS2) ;
  cbi(ADCSRA,ADPS1) ;
  cbi(ADCSRA,ADPS0) ;
#endif

}

//*******************************************************************************************************************
// Main Program     
//*******************************************************************************************************************
 
void loop() {
  
  // midiLoopback();
  readSensors();
  checkSensors();
  checkPedal();
  InstrumentChange();
  checkOctave();
}

//*******************************************************************************************************************
// Read Sensors     
//*******************************************************************************************************************

void readSensors () {
  
  for (int count=0; count<=15; count++) {
  
    // select the bit for digital out sent to multiplexer to cycle through 16 inputs
    r0 = bitRead(count,0);    // code that assigns values to digital pins
    r1 = bitRead(count,1);     
    r2 = bitRead(count,2); 
    r3 = bitRead(count,3); 
         
    digitalWrite(2,r0);       //sets the digital output pins to high or low (from values above)
    digitalWrite(3,r1);
    digitalWrite(4,r2);
    digitalWrite(5,r3);
    
    // Read and store the input value at a location in the array

    multiplex[count] = analogRead(A0);
    multiplex[count+16] = analogRead(A1);
    multiplex[count+32] = analogRead(A2);  
  }
}

//*******************************************************************************************************************
// Check Sensors   
//*******************************************************************************************************************

void checkSensors(){   //function to get values of each piezo; only checking a single analog input pin

// Serial.println("check");
  for(int sensecount=0; sensecount<=2; sensecount++){
    //int sensecount = 0;

    for(int pin=0; pin <=15; pin++){
      
      pad=pin+(sensecount*16);
      hitavg = multiplex[pad];                //variable hitavg equals the voltage (0-1023) of the piezo

      if(hitavg > PadCutOff[pad]){            //if the voltage of the piezo is higher than the value of the 
                                              //"threshold" element in array PadCutOff, then:
        if(activePad[pad] == false){          //and if the pad wasn't already on or "active"  
      
          if(VelocityFlag == true){
          //hitavg = 127 / ((1023 - PadCutOff[pin]) / (hitavg - PadCutOff[pin]));    // With full range (Too sensitive ?)
            hitavg = (hitavg / 8) -1;         //and if you want force to correspond to volume (VelocityFlag=true), 
          }                                   //set voltage of piezo into volume (or "velocity") range of MIDI note (0-127)
          else{
            hitavg = 127;                     //if you don't care, set velocity to max value (127)
          }
    
          MIDI_TX(144,PadNote[pad + octave],hitavg); //put together MIDI message; note / velocity / channel
          // Serial.print(sensecount*16 + pin);
          // Serial.print(" - ");
          // Serial.print("Value: ");
          // Serial.print(PadNote[pad]);
          // Serial.print(" - ");
          // int avgvolt;
          // avgvolt = (hitavg / 8) -1;
          // Serial.print(hitavg);
          // Serial.print("  :  ");
          // Serial.println(avgvolt);
          // Serial.println();
          // Serial.println();
          
          PinPlayTime[pad] = 0;               //reset the pin play time
          activePad[pad] = true;              //make the pin active (was inactive)
        }
      
        else {                                //if the pad was already active when it was hit, increment its play time
          PinPlayTime[pad] = PinPlayTime[pad] + 1;
        }
      }
  
      else if(activePad[pad] == true){        //the pad is active, but it is not greater than cutoff, increment play time
        PinPlayTime[pad] = PinPlayTime[pad] + 1;
        
      if(PinPlayTime[pad] > MaxPlayTime[pad]){  //but if it's already been on for the amount set in the MaxPlayTime array, 
        activePad[pad] = false;                 //turn it off
        MIDI_TX(128,PadNote[pad + octave],127); //velocity 0: OFF state
        }
      }
    }
  }
}

//*******************************************************************************************************************
// Check Pedal   
//*******************************************************************************************************************

// Sustain-Pedal abfragen und ControlChange senden
void checkPedal(){
  if(digitalRead(A4) == LOW) {
    SustainStatus = true;
  }
  else {
    SustainStatus = false;
  }
  if ((SustainStatus == true) && (LastSustainStatus == false)) {
    MIDI_CC(127);
    LastSustainStatus = true;
  }
  if ((SustainStatus == false) && (LastSustainStatus == true)){
    MIDI_CC(0);
    LastSustainStatus = false;
  }
}

//*******************************************************************************************************************
// Check Instrument   
//*******************************************************************************************************************

// Taster checken, Instrumentenwechsel GELB
void InstrumentChange(){
  if(digitalRead(A3) == LOW) {
    InstrumentStatus = true;
  }
  else {
    InstrumentStatus = false;
  }
  if ((InstrumentStatus == true) && (LastInstrumentStatus == false)) {
    InstrumentCounter += 1;
    if (InstrumentCounter > 7) {
      InstrumentCounter = 0;
    }
    MIDI_PC(Instrument[InstrumentCounter]);
    if (InstrumentCounter == 0) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Marimba       ", 5, 45);
    }
     if (InstrumentCounter == 1) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Vibraphon     ", 5, 45);
    }
    if (InstrumentCounter == 2) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Xylophon      ", 5, 45);
    }
    if (InstrumentCounter == 3) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Glockenspiel  ", 5, 45);
    }
    if (InstrumentCounter == 4) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Hackbrett     ", 5, 45);
    }
    if (InstrumentCounter == 5) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Kalimba       ", 5, 45);
    }
    if (InstrumentCounter == 6) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Wood Mallet   ", 5, 45);
    }
    if (InstrumentCounter == 7) {
        TFTscreen.stroke(0, 0, 0);
        TFTscreen.rect(5, 45, 150, 10);
        TFTscreen.stroke(255, 255, 255);
        TFTscreen.text("Instrument: Yellow        ", 5, 45);
    }
   
    //TFTscreen.text("Instrument: " + InstrumentName[InstrumentCounter], 5, 45);
    LastInstrumentStatus = true;
  }
  if ((InstrumentStatus == false) && (LastInstrumentStatus == true)){
    LastInstrumentStatus = false;
  }
}

//*******************************************************************************************************************
// Check Octave  
//*******************************************************************************************************************

// Octave Taster GRÜN
// Taster checken - tut nicht
void checkOctave(){
  if(digitalRead(12) == LOW) {
    OctaveStatus = true;
  }
  else {
    OctaveStatus = false;
  }
  if ((OctaveStatus == true) && (LastOctaveStatus == false)) {
    if (octave == 0) {
      octave = 12;
      lastoctave = 0;
      TFTscreen.stroke(0, 0, 0);
      TFTscreen.rect(5, 65, 150, 10);
      TFTscreen.stroke(255, 255, 255);
      TFTscreen.text("Octave Mid  ", 5, 65);
    }
    else if (octave == 24) {
      octave = 12;
      lastoctave = 24;
      TFTscreen.stroke(0, 0, 0);
      TFTscreen.rect(5, 65, 150, 10);
      TFTscreen.stroke(255, 255, 255);
      TFTscreen.text("Octave Mid  ", 5, 65);
    }
    else if ((octave == 12) && (lastoctave == 0)){
      octave = 24;
      lastoctave = 12;
      TFTscreen.stroke(0, 0, 0);
      TFTscreen.rect(5, 65, 150, 10);
      TFTscreen.stroke(255, 255, 255);
      TFTscreen.text("Octave High ", 5, 65);
    }
    else if ((octave == 12) && (lastoctave == 24)) {
      octave = 0;
      lastoctave = 12;
      TFTscreen.stroke(0, 0, 0);
      TFTscreen.rect(5, 65, 150, 10);
      TFTscreen.stroke(255, 255, 255);
      TFTscreen.text("Octave Low  ", 5, 65);
    }
    LastOctaveStatus = true;
  }
  if ((OctaveStatus == false) && (LastOctaveStatus == true)){
    LastOctaveStatus = false;
  }
}

//*******************************************************************************************************************
// Transmit MIDI Message      
//*******************************************************************************************************************
void MIDI_TX(unsigned char MESSAGE, unsigned char PITCH, unsigned char VELOCITY) 
{
  status = MESSAGE + midichannel;
  Serial.write(status);
  Serial.write(PITCH);
  Serial.write(VELOCITY);
}

// MIDI Program Change
void MIDI_PC(unsigned char PROGRAMNUMBER) 
{
  status = 192 + midichannel;
  Serial.write(status);
  Serial.write(PROGRAMNUMBER);
}

// MIDI Control Change
void MIDI_CC(unsigned char State) 
{
  status = 176 + midichannel;
  Serial.write(status);
  Serial.write(64);
  Serial.write(State);
}

//*************** MIDI LOOPBACK ******************//
void midiLoopback(){
  if(Serial.available() > 0){
      byte1 = Serial.read();
      byte2 = Serial.read();
      byte3 = Serial.read();
      
      MIDI_TX(byte1, byte2, byte3);
    }
  }

Kurz zur Erklärung der einzelnen Programmteile:

Damit der Code flüssig läuft und auch schnelle Anschläge hintereinander detektiert werden können, wird der AD-Wandler etwas beschleunigt. Dies erfolgt über folgende Programmteile:

// Making analog readings faster (for drumrolls) works with this code
// read http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1208715493/11 for more info
#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


#if FASTADC
// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif

Die Daten der Noten, die Anschlagsempfindlichkeit und die Abklingzeiten der Piezo-Sensoren können einzeln justiert werden. Ich habe jetzt alle auf einem Wert gelassen, hier kann man aber richtig gut fein-tunen.

 // Midi-Notenwerte
unsigned char PadNote[72] = {36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107};

// Threshold Werte, Anschlagsempfindlichkeit
// Tuning-Bedarf ! Ch 10
int PadCutOff[48] = {300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300,300};

// time each note remains on after being hit 
int MaxPlayTime[48] = {50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50};

// array of flags of pad currently playing
boolean activePad[48] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

// counter since pad started to play
int PinPlayTime[48] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 

Die MIDI-Controls erfolgen im letzten Abschnitt. Es wird eine Übertragungsrate von 31.250 Baud genutzt.

//*******************************************************************************************************************
// Transmit MIDI Message      
//*******************************************************************************************************************
void MIDI_TX(unsigned char MESSAGE, unsigned char PITCH, unsigned char VELOCITY) 
{
  status = MESSAGE + midichannel;
  Serial.write(status);
  Serial.write(PITCH);
  Serial.write(VELOCITY);
}

// MIDI Program Change
void MIDI_PC(unsigned char PROGRAMNUMBER) 
{
  status = 192 + midichannel;
  Serial.write(status);
  Serial.write(PROGRAMNUMBER);
}

// MIDI Control Change
void MIDI_CC(unsigned char State) 
{
  status = 176 + midichannel;
  Serial.write(status);
  Serial.write(64);
  Serial.write(State);
}

Hier werden die entsprechenden Codes zusammengebaut. Eine komplexe Library ist dafür nicht notwendig.

Folgende Probleme / offene Punkte gibt es noch:

Das Gehäuse muss noch fertig gestellt werden (nach Corona dann).

Die Sensoren sind sehr empfindlich gegenüber statischer Aufladung. Manchmal genügt es, mit der Handfläche über die Plättchen zu streicheln und es werden Töne gespielt. Vielleicht hat hier jemand eine gute und einfache Lösung.

Manche Plättchen reagieren nicht gleich. Ich habe drei Stück, da muss man einfach ein wenig mehr draufschlagen. Das könnte ich jetzt mit der Software anpassen oder ich muss die Sensoren tauschen.

Zum Spielen kann man Schlegel mit Garn oder Gummi benutzen. Garn ist mir lieber, da die Gummischlegel starke Schlaggeräusche verursachen, die im Spiel etwas störend sein können.

Es sind jetzt noch zwei Taster unbelegt. Geplant ist: Anschlagdynamik AN / AUS und eine Art von Setup.
Mal sehen, es ist noch Programmspeicher frei.

Für Anregungen gerne eine Email an mich (xyluino@intuitiv.de).

Und wer Lust hat: viel Spaß beim Nachbauen!

Xyluino – ein Midi Xylophon mit einem Arduino

Beitragsnavigation


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert