Touch clamp drum machine: the software
And now we come to the fun part: the software.
For the TouchClamp click I used the MPR121 library from Adafruit, with a small change: in the Adafruit_MPR121.cpp file.
1 2 3 4 5 6 7 8 9 10 11 | /**************************************************************************/ /*! @brief Writes 8-bits to the specified destination register */ /**************************************************************************/ void Adafruit_MPR121::writeRegister(uint8_t reg, uint8_t value) { Wire.beginTransmission(_i2caddr); Wire.write((uint8_t)reg); Wire.write((uint8_t)(value)); Wire.endTransmission(); } |
has to be changed to:
1 2 3 4 5 6 7 8 9 10 11 | /**************************************************************************/ /*! @brief Writes 8-bits to the specified destination register */ /**************************************************************************/ void Adafruit_MPR121::writeRegister(uint8_t reg, uint8_t value) { Wire.beginTransmission(_i2caddr); Wire.write((uint8_t)reg); Wire.write((uint8_t)(value)); Wire.endTransmission(true); } |
Without this little change there’s no stop condition sent after writing data to the MPR121.
The MP3 code for real-time MIDI is based on the MP3_Shield_RealtimeMIDI.ino. It’s the same code I used as a source of inspiration when I created the Flip & Click Theremin, and I know it works fine.
There are several particularities in the way percussion instruments work in VS1053. First, the instrument bank is 0x78. One must select an instrument, no matter which one. There are no notes, each note code corresponds to a percussion instrument – a list of percussion MIDI codes can be found on http://soundprogramming.net/file-formats/general-midi-drum-note-numbers/. So, to hit the bass drum one must play note code 36. The snare drum is 38. Low tom is 43, mid tom is 47 and high tom is 50. The ride cymbal is 51, and the pedal hi-hat is 44. The open Hi-hat is not implemented. I have mapped it to the H button on the TouchClamp click, but is very hard to play it this way.
With the above considerations, the code is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | /********************************************************* VS1053 DRUM MACHINE HARDWARE: CHIPKIT UNO32 http://chipkit.net/wpcproduct/chipkit-uno32/ ARDUINO UNO CLICK SHIELD http://www.mikroe.com/click/arduino-uno-shield/ TOUCH CLAMP CLICK http://www.mikroe.com/click/touchclamp/ MP3 CLICK http://www.mikroe.com/click/mp3/ SOFTWARE: ARDUINO IDE V.1.6.9 ADAFRUIT MPR121 LIBRARY https://github.com/adafruit/Adafruit_MPR121 Note: in Adafruit_MPR121.cpp, in function void Adafruit_MPR121::writeRegister(uint8_t reg, uint8_t value) change Wire.endTransmission(); to Wire.endTransmission(true); VS1053 CODE INSPIRED FROM https://gist.github.com/microtherion/2636608 PROJECT PAGE: http://electronza.com/ ******************************************************************* This is a library for the MPR121 12-channel Capacitive touch sensor Designed specifically to work with the MPR121 Breakout in the Adafruit shop ----> https://www.adafruit.com/products/ These sensors use I2C communicate, at least 2 pins are required to interface Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! Written by Limor Fried/Ladyada for Adafruit Industries. BSD license, all text above must be included in any redistribution **********************************************************/ #include <Wire.h> #include <SPI.h> #include "Adafruit_MPR121.h" // TouchClamp click uses address 0x5A // A second touch clamp click can be added, address being changed // by moving the jumpers onto the TouchClamp click // One TouchClamp click allows for seven percussion instruments Adafruit_MPR121 cap = Adafruit_MPR121(); // Keeps track of the last pins touched // so we know when buttons are 'released' uint16_t lasttouched = 0; uint16_t currtouched = 0; // Here we define the percussion instruments played // For a complete list see // http://soundprogramming.net/file-formats/general-midi-drum-note-numbers/ // TouchClamp mapping // A = 7 (Bass drum 1 - note 36) // B = 6 (Snare Drum 1 - note 38) // C = 5 (Low Tom 1 - note 43) // D = 3 (Mid Tom 1 - note 47) // E = 2 (High Tom 1 - note 50) // F = 1 (Ride Cymbal 1 - note 51) // G = 0 (Pedal Hi-hat - note 44) // H = 4 (on the touch clamp, not used) // I will set it to 46 Open Hi-hat byte drums[] = {44, 51, 50, 47, 46, 43, 38, 36}; // Pin definitions for Arduino Click shield socket B #define VS_XCS 9 // Control Chip Select Pin (SPI Control/Status registers) #define VS_XDCS 3 // Data Chip Select / BSYNC Pin #define VS_DREQ A1 // Data Request Pin: Player asks for more data #define VS_RESET A2 // Reset is active low void setup() { // For debugging, comment if running in standalone mode // also comment all other Serial... lines while (!Serial); // needed to keep leonardo/micro from starting too fast! Serial.begin(9600); Serial.println("Adafruit MPR121 Capacitive Touch sensor test"); if (!cap.begin(0x5A)) { Serial.println("MPR121 not found, check wiring?"); while (1); } Serial.println("MPR121 found!"); // debugging end // Set up VS10523 / MP3 click pinMode(VS_DREQ, INPUT); pinMode(VS_XCS, OUTPUT); pinMode(VS_XDCS, OUTPUT); digitalWrite(VS_XCS, HIGH); //Deselect Control digitalWrite(VS_XDCS, HIGH); //Deselect Data pinMode(VS_RESET, OUTPUT); //Initialize VS1053 chip digitalWrite(VS_RESET, LOW); //Put VS1053 into hardware reset // Setup SPI for VS1053 SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); //From page 12 of datasheet, max SCI reads are CLKI/7. Input clock is 12.288MHz. //Internal clock multiplier is 1.0x after power up. //Therefore, max SPI speed is 1.75MHz. // ChipKit UNO32 runs at 80MHz // 80MHz / 1.7MHz = 47.05 // We choose 1/64 as divider. As such the SPI clock is 1.24 MHz SPI.setClockDivider(SPI_CLOCK_DIV64); //Set SPI bus speed SPI.transfer(0xFF); //Throw a dummy byte at the bus delayMicroseconds(20); // just allow for some time to pass digitalWrite(VS_RESET, HIGH); //Bring up VS1053 // Load the realtime MIDI plugin VSLoadUserCode(); delay(50); // Set instrument bank talkMIDI(0xB0, 0, 0x78); // Percussion bank // Set instrument talkMIDI(0xC0, 43, 0); // set the volume to 120 talkMIDI(0xB0, 0x07, 120); } void loop() { // Get the currently touched pads currtouched = cap.touched(); for (uint8_t i=0; i<12; i++) { // it if *is* touched and *wasnt* touched before, alert! if ((currtouched & _BV(i)) && !(lasttouched & _BV(i)) ) { Serial.print(i); Serial.println(" touched"); // Play instrument noteOn(0, drums[i], 127); delay(5); } // if it *was* touched and now *isnt*, alert! if (!(currtouched & _BV(i)) && (lasttouched & _BV(i)) ) { Serial.print(i); Serial.println(" released"); // do nothing with the MIDI } } // reset last state lasttouched = currtouched; } /********************************************************* * VS1053 functions * ******************************************************/ // Write to VS10xx register // SCI: Data transfers are always 16bit. When a new SCI operation comes in // DREQ goes low. We then have to wait for DREQ to go high again. // XCS should be low for the full duration of operation. void VSWriteRegister(unsigned char addressbyte, unsigned char highbyte, unsigned char lowbyte) { while (!digitalRead(VS_DREQ)) ; //Wait for DREQ to go high indicating IC is available digitalWrite(VS_XCS, LOW); //Select control //SCI consists of instruction byte, address byte, and 16-bit data word. SPI.transfer(0x02); //Write instruction SPI.transfer(addressbyte); SPI.transfer(highbyte); SPI.transfer(lowbyte); while (!digitalRead(VS_DREQ)) ; //Wait for DREQ to go high indicating command is complete digitalWrite(VS_XCS, HIGH); //Deselect Control } // Plugin to put VS10XX into realtime MIDI mode // Originally from http://www.vlsi.fi/fileadmin/software/VS10XX/vs1053b-rtmidistart.zip // Permission to reproduce here granted by VLSI solution. // const unsigned short sVS1053b_Realtime_MIDI_Plugin[28] = { /* Compressed plugin */ 0x0007, 0x0001, 0x8050, 0x0006, 0x0014, 0x0030, 0x0715, 0xb080, /* 0 */ 0x3400, 0x0007, 0x9255, 0x3d00, 0x0024, 0x0030, 0x0295, 0x6890, /* 8 */ 0x3400, 0x0030, 0x0495, 0x3d00, 0x0024, 0x2908, 0x4d40, 0x0030, /* 10 */ 0x0200, 0x000a, 0x0001, 0x0050, }; // This function loads the user code void VSLoadUserCode(void) { int i = 0; while (i < sizeof(sVS1053b_Realtime_MIDI_Plugin) / sizeof(sVS1053b_Realtime_MIDI_Plugin[0])) { unsigned short addr, n, val; addr = sVS1053b_Realtime_MIDI_Plugin[i++]; n = sVS1053b_Realtime_MIDI_Plugin[i++]; while (n--) { val = sVS1053b_Realtime_MIDI_Plugin[i++]; VSWriteRegister(addr, val >> 8, val & 0xFF); } } } // Send one MIDI byte void sendMIDI(byte data) { SPI.transfer(0); SPI.transfer(data); } // Plays a MIDI note. Doesn't check to see that cmd is greater than 127, or that data values are less than 127 void talkMIDI(byte cmd, byte data1, byte data2) { // Wait for chip to be ready (Unlikely to be an issue with real time MIDI) while (!digitalRead(VS_DREQ)); digitalWrite(VS_XDCS, LOW); sendMIDI(cmd); //Some commands only have one data byte. All cmds less than 0xBn have 2 data bytes //(sort of: http://253.ccarh.org/handout/midiprotocol/) if ( (cmd & 0xF0) <= 0xB0 || (cmd & 0xF0) >= 0xE0) { sendMIDI(data1); sendMIDI(data2); } else { sendMIDI(data1); } } //Send a MIDI note-on message. Like pressing a piano key //channel ranges from 0-15 void noteOn(byte channel, byte note, byte attack_velocity) { talkMIDI( (0x90 | channel), note, attack_velocity); //talkMIDI( (0xA0 | channel), note, 127); } //Send a MIDI note-off message. Like releasing a piano key void noteOff(byte channel, byte note, byte release_velocity) { talkMIDI( (0x80 | channel), note, release_velocity); } |