├── firmware ├── global.cpp ├── global.h ├── Pad.h ├── Pad.cpp ├── DualDigitDisplay.h ├── Program.h ├── scales.h ├── DualDigitDisplay.cpp ├── Program.cpp ├── firmware.ino ├── MIDI.h └── MIDI.cpp ├── dualMocoLUFA ├── makefile ├── readme.txt ├── Board │ └── LEDs.h ├── dualMoco.h ├── Descriptors.h ├── Lib │ └── LightweightRingBuff.h ├── dualMoco.c ├── HEX │ └── dualMoco.hex └── Descriptors.c ├── LICENSE ├── utilities ├── 7segments.html └── padDebug.html └── README.md /firmware/global.cpp: -------------------------------------------------------------------------------- 1 | #include "global.h" 2 | 3 | byte mainMidiChannel = 4; -------------------------------------------------------------------------------- /dualMocoLUFA/makefile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cstoquer/midiFighter24/HEAD/dualMocoLUFA/makefile -------------------------------------------------------------------------------- /firmware/global.h: -------------------------------------------------------------------------------- 1 | #ifndef GLOBAL__H 2 | #define GLOBAL__H 3 | 4 | #include 5 | #include "Arduino.h" 6 | 7 | extern byte mainMidiChannel; 8 | 9 | 10 | #endif // GLOBAL__H 11 | -------------------------------------------------------------------------------- /firmware/Pad.h: -------------------------------------------------------------------------------- 1 | #ifndef PAD__H 2 | #define PAD__H 3 | 4 | #include "global.h" 5 | 6 | 7 | class Pad { 8 | private: 9 | int note; 10 | 11 | public: 12 | int state; 13 | 14 | Pad(); 15 | void setNote(int); 16 | void trigger(bool); 17 | }; 18 | 19 | #endif // PAD__H 20 | -------------------------------------------------------------------------------- /firmware/Pad.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Arduino.h" 3 | #include "Pad.h" 4 | #include "MIDI.h" 5 | 6 | 7 | Pad::Pad() { 8 | note = 0; 9 | state = -1; 10 | } 11 | 12 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 13 | 14 | void Pad::setNote(int n) { 15 | note = n; 16 | }; 17 | 18 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 19 | 20 | void Pad::trigger(bool value) { 21 | if (value) { 22 | MIDI.sendNoteOn(note, 120, mainMidiChannel); 23 | state = note; 24 | } else if (state >= 0) { 25 | MIDI.sendNoteOff(state, 0, mainMidiChannel); 26 | state = -1; 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /firmware/DualDigitDisplay.h: -------------------------------------------------------------------------------- 1 | #ifndef DUAL_DIGIT_DISPLAY__H 2 | #define DUAL_DIGIT_DISPLAY__H 3 | 4 | #define DISPLAY_LATCH 9 5 | #define DISPLAY_CLOCK 8 6 | #define DISPLAY_DATA 10 7 | 8 | #define BLINK_DURATION 2000 9 | #define CLEAR_DURATION 8000 10 | 11 | #define DOT 128 12 | 13 | 14 | class DualDigitDisplay { 15 | private: 16 | int timer; 17 | bool blink; 18 | 19 | public: 20 | DualDigitDisplay(); 21 | void setup(); 22 | void display(int digitA, int digitB); 23 | void displayNumber(int number); 24 | void displayNumber(int number, int decimal, int quote); 25 | void displayNote(int noteNumber); 26 | void displayString(char* letters); 27 | void clear(); 28 | inline void click() { 29 | if (--timer == 0) { 30 | blink = !blink; 31 | display(blink ? DOT : 0, 0); 32 | timer = BLINK_DURATION; 33 | } 34 | } 35 | }; 36 | 37 | #endif // DUAL_DIGIT_DISPLAY__H 38 | -------------------------------------------------------------------------------- /firmware/Program.h: -------------------------------------------------------------------------------- 1 | #ifndef PROGRAM__H 2 | #define PROGRAM__H 3 | 4 | #include "DualDigitDisplay.h" 5 | #include "Pad.h" 6 | 7 | 8 | class Program { 9 | private: 10 | Pad pads[24]; 11 | int rootPad; 12 | int rootNote; 13 | int initIndex; 14 | int initNote; 15 | int octaveSize; 16 | int currentOctave; 17 | int maxOctave; 18 | byte const *scale; 19 | int scaleSize; 20 | DualDigitDisplay *display; 21 | 22 | void setupPadFromScale(); 23 | void prepare(); 24 | 25 | public: 26 | Program(); 27 | void init(DualDigitDisplay*); 28 | void setupPads(); 29 | void shiftUp(); 30 | void shiftDown(); 31 | void allPadOff(); 32 | 33 | void setRootPad(int); 34 | void setRootNote(int); 35 | void setScaleMode(const byte*, int); 36 | void setChromaticMode(int); 37 | 38 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 39 | inline void triggerPad(int pad, int state) { 40 | pads[pad].trigger(state); 41 | } 42 | }; 43 | 44 | 45 | #endif // PROGRAM__H 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Cedric Stoquer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /firmware/scales.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALES__H 2 | #define SCALES__H 3 | 4 | const byte scale_major[6] = { 2, 2, 1, 2, 2, 2 }; 5 | const byte scale_harmonicMinor[6] = { 2, 1, 2, 2, 1, 3 }; 6 | const byte scale_melodicMinor[6] = { 2, 1, 2, 2, 2, 2 }; 7 | const byte scale_wholeTone[5] = { 2, 2, 2, 2, 2 }; 8 | const byte scale_diminished[7] = { 2, 1, 2, 1, 2, 1, 2 }; 9 | const byte scale_majorPentatonic[4] = { 2, 2, 3, 2 }; 10 | const byte scale_minorPentatonic[4] = { 3, 2, 2, 3 }; 11 | const byte scale_japInSen[4] = { 1, 4, 2, 3 }; 12 | const byte scale_majorBebop[7] = { 2, 2, 1, 2, 1, 1, 2 }; 13 | const byte scale_dominantBebop[7] = { 2, 2, 1, 2, 2, 1, 1 }; 14 | const byte scale_blues[5] = { 3, 2, 1, 1, 3 }; 15 | const byte scale_arabic[6] = { 1, 3, 1, 2, 1, 3 }; 16 | const byte scale_enigmatic[6] = { 1, 3, 2, 2, 2, 1 }; 17 | const byte scale_neapolitan[6] = { 1, 2, 2, 2, 2, 2 }; 18 | const byte scale_neapolitanMinor[6] = { 1, 2, 2, 2, 1, 3 }; 19 | const byte scale_hungarianMinor[6] = { 2, 1, 3, 1, 1, 3 }; 20 | const byte scale_dorian[6] = { 2, 1, 2, 2, 2, 1 }; 21 | const byte scale_phrygian[6] = { 1, 2, 2, 2, 1, 2 }; 22 | const byte scale_lydian[6] = { 2, 2, 2, 1, 2, 2 }; 23 | const byte scale_mixolidian[6] = { 2, 2, 1, 2, 2, 1 }; 24 | const byte scale_aeolidian[6] = { 2, 1, 2, 2, 1, 2 }; 25 | const byte scale_locrian[6] = { 1, 2, 2, 1, 2, 2 }; 26 | 27 | 28 | #endif // SCALES__H 29 | -------------------------------------------------------------------------------- /dualMocoLUFA/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | dualMocoLUFA Project 3 | Copyright (C) 2013 by morecat_lab 4 | 5 | 2013/09/22 6 | 7 | http://morecatlab.akiba.coocan.jp/ 8 | 9 | based on LUFA-100807 10 | 11 | This is dual mode firmware for Arduino Uno. 12 | There are two mode on this firmware, USB-MIDI(MocoLUFA) and Arduino-Serial. 13 | 14 | INSTRUCTIONS 15 | 1. Burn 8u2 (or 16u2) on Arduino Uno. 16 | check original document below. 17 | 2. USB-MIDI formware work as default. 18 | 3. To enable Arduino-Serial, ass jumper to PIN 4(MOSI PB2) and PIN6 (grand) on ICSP connector for 8U2. 19 | Reset is required to change the firmware mode. 20 | 21 | -Yoshi 22 | 23 | see http://arduino.cc/en/Hacking/DFUProgramming8U2 for how to update 8u2/16u2 firmware 24 | 25 | ------------------------------------- 26 | original readme.txt from Arduino-Serial 27 | 28 | To setup the project and upload the Arduino usbserial application firmware to an ATMEGA8U2 using the Arduino USB DFU bootloader: 29 | 1. unpack the source into LUFA's Projects directory 30 | 2. set ARDUINO_MODEL_PID in the makefile as appropriate 31 | 3. do "make clean; make" 32 | 4. put the 8U2 into USB DFU mode: 33 | 4.a. assert and hold the 8U2's RESET line 34 | 4.b. assert and hold the 8U2's HWB line 35 | 4.c. release the 8U2's RESET line 36 | 4.d. release the 8U2's HWB line 37 | 5. confirm that the board enumerates as either "Arduino Uno DFU" or "Arduino Mega 2560 DFU" 38 | 6. do "make dfu" (OS X or Linux - dfu-programmer must be installed first) or "make flip" (Windows - Flip must be installed first) 39 | 40 | Check that the board enumerates as either "Arduino Uno" or "Arduino Mega 2560". Test by uploading a new Arduino sketch from the Arduino IDE. 41 | -------------------------------------------------------------------------------- /utilities/7segments.html: -------------------------------------------------------------------------------- 1 | 2 | 7 segments 3 | 27 | 28 | 29 | 30 |
0
31 |
32 | 33 | 77 | 78 | -------------------------------------------------------------------------------- /firmware/DualDigitDisplay.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Arduino.h" 3 | #include "DualDigitDisplay.h" 4 | 5 | // 0 1 2 3 4 5 6 7 8 9 6 | const byte NUMBERS[10] = {119, 36, 93, 109, 46, 107, 123, 37, 127, 111}; 7 | // A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 8 | const byte LETTERS[26] = {63, 122, 83, 124, 91, 27, 115, 62, 18, 116, 59, 82, 55, 56, 120, 31, 47, 24, 107, 90, 118, 112, 73, 54, 110, 85}; 9 | // C C# D D# E F F# G G# A A# B 10 | const byte NOTES[12] = {83, 83, 124, 124, 91, 27, 27, 115, 115, 63, 63, 122}; 11 | const byte SHARP[12] = {0, DOT, 0, DOT, 0, 0, DOT, 0, DOT, 0, DOT, 0}; 12 | 13 | 14 | DualDigitDisplay::DualDigitDisplay() { 15 | timer = BLINK_DURATION; 16 | blink = false; 17 | } 18 | 19 | 20 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 21 | 22 | void DualDigitDisplay::setup() { 23 | pinMode(DISPLAY_LATCH, OUTPUT); 24 | pinMode(DISPLAY_CLOCK, OUTPUT); 25 | pinMode(DISPLAY_DATA, OUTPUT); 26 | } 27 | 28 | 29 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 30 | 31 | void DualDigitDisplay::display(int digitA, int digitB) { 32 | // take the DISPLAY_LATCH low so 33 | // the LEDs don't change while you're sending in bits: 34 | digitalWrite(DISPLAY_LATCH, LOW); 35 | // shift out the bits: 36 | shiftOut(DISPLAY_DATA, DISPLAY_CLOCK, MSBFIRST, digitB); 37 | shiftOut(DISPLAY_DATA, DISPLAY_CLOCK, MSBFIRST, digitA); 38 | //take the latch pin high so the LEDs will light up: 39 | digitalWrite(DISPLAY_LATCH, HIGH); 40 | timer = CLEAR_DURATION; 41 | } 42 | 43 | 44 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 45 | 46 | void DualDigitDisplay::displayNumber(int number, int decimal, int quote) { 47 | display( 48 | NUMBERS[(number % 100) / 10] | decimal << 7, 49 | NUMBERS[number % 10] | quote << 7 50 | ); 51 | }; 52 | 53 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 54 | 55 | void DualDigitDisplay::displayNumber(int number) { 56 | int firstDigit = (number % 100) / 10; 57 | if (firstDigit != 0) firstDigit = NUMBERS[firstDigit]; 58 | display( 59 | firstDigit, 60 | NUMBERS[number % 10] 61 | ); 62 | }; 63 | 64 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 65 | 66 | void DualDigitDisplay::displayNote(int noteNumber) { 67 | int note = noteNumber % 12; 68 | display(NOTES[note], NUMBERS[noteNumber / 12] | SHARP[note]); 69 | }; 70 | 71 | 72 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 73 | 74 | void DualDigitDisplay::displayString(char* letters) { 75 | display(LETTERS[letters[0] - 65], LETTERS[letters[1] - 65]); 76 | }; 77 | 78 | 79 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 80 | 81 | void DualDigitDisplay::clear() { 82 | display(0, 0); 83 | }; 84 | -------------------------------------------------------------------------------- /firmware/Program.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Arduino.h" 3 | #include "Program.h" 4 | 5 | 6 | Program::Program() { 7 | currentOctave = 1; 8 | maxOctave = 5; 9 | octaveSize = 24; 10 | scale = NULL; 11 | scaleSize = 0; 12 | rootPad = 0; 13 | rootNote = 0; 14 | initIndex = 0; 15 | initNote = 0; 16 | }; 17 | 18 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 19 | 20 | void Program::init(DualDigitDisplay* d) { 21 | currentOctave = 1; 22 | setupPads(); 23 | display = d; 24 | }; 25 | 26 | 27 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 28 | 29 | void Program::setupPadFromScale() { 30 | int root = currentOctave * 12 + rootNote; 31 | int note = currentOctave * 12 + initNote; 32 | byte index = initIndex; 33 | for (int i = 0; i < 24; ++i) { 34 | pads[i].setNote(note); 35 | if (index < scaleSize) note += scale[index]; 36 | if (++index > scaleSize) { 37 | root += 12; 38 | note = root; 39 | index = 0; 40 | } 41 | } 42 | }; 43 | 44 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 45 | 46 | void Program::setupPads() { 47 | if (scale != NULL) { 48 | setupPadFromScale(); 49 | return; 50 | } 51 | int note = currentOctave * octaveSize; 52 | for (int i = 0; i < 24; ++i) { 53 | pads[i].setNote(note); 54 | note += 1; 55 | } 56 | }; 57 | 58 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 59 | 60 | void Program::prepare() { 61 | int scaleValues[12]; 62 | scaleValues[0] = 0; 63 | for (int i = 0; i < scaleSize; ++i) { 64 | scaleValues[i + 1] = scaleValues[i] + scale[i]; 65 | } 66 | 67 | int s = scaleSize + 1; 68 | 69 | initIndex = (s - (rootPad % s)) % s; 70 | initNote = (scaleValues[initIndex] + rootNote) % 12; 71 | maxOctave = (127 - initNote - (24 % s)) / 12 - (24 / s); 72 | 73 | if (currentOctave > maxOctave) currentOctave = maxOctave; 74 | 75 | setupPads(); 76 | }; 77 | 78 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 79 | 80 | void Program::setRootPad(int r) { 81 | rootPad = r; 82 | prepare(); 83 | }; 84 | 85 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 86 | 87 | void Program::setRootNote(int r) { 88 | rootNote = r; 89 | prepare(); 90 | }; 91 | 92 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 93 | 94 | void Program::setScaleMode(const byte* _scale, int _scaleSize) { 95 | scale = _scale; 96 | scaleSize = _scaleSize; 97 | prepare(); 98 | }; 99 | 100 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 101 | 102 | void Program::setChromaticMode(int _octaveSize) { 103 | scale = NULL; 104 | octaveSize = _octaveSize; 105 | maxOctave = 5; // TODO 106 | setupPads(); 107 | }; 108 | 109 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 110 | 111 | void Program::shiftUp() { 112 | if (++currentOctave >= maxOctave) currentOctave = 0; 113 | setupPads(); 114 | display->displayNumber(currentOctave); 115 | }; 116 | 117 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 118 | 119 | void Program::shiftDown() { 120 | if (--currentOctave < 0) currentOctave = maxOctave - 1; 121 | setupPads(); 122 | display->displayNumber(currentOctave); 123 | }; 124 | 125 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 126 | 127 | void Program::allPadOff() { 128 | // cancel all pad notes 129 | for (int i = 0; i < 24; ++i) { 130 | pads[i].trigger(0); 131 | } 132 | }; 133 | 134 | -------------------------------------------------------------------------------- /dualMocoLUFA/Board/LEDs.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2010. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.fourwalledcubicle.com 7 | */ 8 | 9 | /* 10 | Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaim all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /* 32 | Board LEDs driver for the Benito board, from www.dorkbotpdx.org. 33 | */ 34 | 35 | #ifndef __LEDS_ARDUINOUNO_H__ 36 | #define __LEDS_ARDUINOUNO_H__ 37 | 38 | /* Includes: */ 39 | #include 40 | 41 | /* Enable C linkage for C++ Compilers: */ 42 | #if defined(__cplusplus) 43 | extern "C" { 44 | #endif 45 | 46 | /* Preprocessor Checks: */ 47 | #if !defined(INCLUDE_FROM_LEDS_H) 48 | #error Do not include this file directly. Include LUFA/Drivers/Board/LEDS.h instead. 49 | #endif 50 | 51 | /* Public Interface - May be used in end-application: */ 52 | /* Macros: */ 53 | /** LED mask for the first LED on the board. */ 54 | #define LEDS_LED1 (1 << 5) 55 | 56 | /** LED mask for the second LED on the board. */ 57 | #define LEDS_LED2 (1 << 4) 58 | 59 | /** LED mask for all the LEDs on the board. */ 60 | #define LEDS_ALL_LEDS (LEDS_LED1 | LEDS_LED2) 61 | 62 | /** LED mask for the none of the board LEDs */ 63 | #define LEDS_NO_LEDS 0 64 | 65 | /* Inline Functions: */ 66 | #if !defined(__DOXYGEN__) 67 | static inline void LEDs_Init(void) 68 | { 69 | DDRD |= LEDS_ALL_LEDS; 70 | PORTD |= LEDS_ALL_LEDS; 71 | } 72 | 73 | static inline void LEDs_TurnOnLEDs(const uint8_t LEDMask) 74 | { 75 | PORTD &= ~LEDMask; 76 | } 77 | 78 | static inline void LEDs_TurnOffLEDs(const uint8_t LEDMask) 79 | { 80 | PORTD |= LEDMask; 81 | } 82 | 83 | static inline void LEDs_SetAllLEDs(const uint8_t LEDMask) 84 | { 85 | PORTD = ((PORTD | LEDS_ALL_LEDS) & ~LEDMask); 86 | } 87 | 88 | static inline void LEDs_ChangeLEDs(const uint8_t LEDMask, const uint8_t ActiveMask) 89 | { 90 | PORTD = ((PORTD | ActiveMask) & ~LEDMask); 91 | } 92 | 93 | static inline void LEDs_ToggleLEDs(const uint8_t LEDMask) 94 | { 95 | PORTD ^= LEDMask; 96 | } 97 | 98 | static inline uint8_t LEDs_GetLEDs(void) ATTR_WARN_UNUSED_RESULT; 99 | static inline uint8_t LEDs_GetLEDs(void) 100 | { 101 | return (PORTD & LEDS_ALL_LEDS); 102 | } 103 | #endif 104 | 105 | /* Disable C linkage for C++ Compilers: */ 106 | #if defined(__cplusplus) 107 | } 108 | #endif 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /dualMocoLUFA/dualMoco.h: -------------------------------------------------------------------------------- 1 | /* 2 | dualMocoLUFA Project 3 | Copyright (C) 2013 by morecat_lab 4 | 5 | 2013/09/22 6 | 7 | http://morecatlab.akiba.coocan.jp/ 8 | 9 | based on LUFA-100807 10 | */ 11 | /* 12 | LUFA Library 13 | Copyright (C) Dean Camera, 2010. 14 | 15 | dean [at] fourwalledcubicle [dot] com 16 | www.fourwalledcubicle.com 17 | */ 18 | 19 | /* 20 | Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) 21 | 22 | Permission to use, copy, modify, distribute, and sell this 23 | software and its documentation for any purpose is hereby granted 24 | without fee, provided that the above copyright notice appear in 25 | all copies and that both that the copyright notice and this 26 | permission notice and warranty disclaimer appear in supporting 27 | documentation, and that the name of the author not be used in 28 | advertising or publicity pertaining to distribution of the 29 | software without specific, written prior permission. 30 | 31 | The author disclaim all warranties with regard to this 32 | software, including all implied warranties of merchantability 33 | and fitness. In no event shall the author be liable for any 34 | special, indirect or consequential damages or any damages 35 | whatsoever resulting from loss of use, data or profits, whether 36 | in an action of contract, negligence or other tortious action, 37 | arising out of or in connection with the use or performance of 38 | this software. 39 | */ 40 | 41 | /** \file 42 | * 43 | * Header file for doubleMonaka.c 44 | */ 45 | 46 | #ifndef _DUAL_MOCO_H_ 47 | #define _DUAL_MOCO_H_ 48 | 49 | /* Includes: */ 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include "Descriptors.h" 56 | 57 | #include "Lib/LightweightRingBuff.h" 58 | 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | /* Macros: */ 66 | /** LED mask for the library LED driver, to indicate TX activity. */ 67 | #define LEDMASK_TX LEDS_LED1 68 | 69 | /** LED mask for the library LED driver, to indicate RX activity. */ 70 | #define LEDMASK_RX LEDS_LED2 71 | 72 | /** LED mask for the library LED driver, to indicate that an error has occurred in the USB interface. */ 73 | #define LEDMASK_ERROR (LEDS_LED1 | LEDS_LED2) 74 | 75 | /** LED mask for the library LED driver, to indicate that the USB interface is busy. */ 76 | #define LEDMASK_BUSY (LEDS_LED1 | LEDS_LED2) 77 | 78 | typedef uint8_t uchar; 79 | #define HW_CDC_BULK_OUT_SIZE 8 80 | #define HW_CDC_BULK_IN_SIZE 8 81 | 82 | #define TRUE 1 83 | #define FALSE 0 84 | 85 | /* Function Prototypes: */ 86 | void SetupHardware(void); 87 | void processSerial(void); 88 | void processMIDI(void); 89 | 90 | void EVENT_USB_Device_Connect(void); 91 | void EVENT_USB_Device_Disconnect(void); 92 | void EVENT_USB_Device_ConfigurationChanged(void); 93 | void EVENT_USB_Device_UnhandledControlRequest(void); 94 | 95 | void EVENT_CDC_Device_LineEncodingChanged(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo); 96 | void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo); 97 | 98 | uchar parseSerialMidiMessage(uchar); 99 | void parseUSBMidiMessage(uchar *, uchar); 100 | /* shared variable */ 101 | extern uchar mocoMode; 102 | 103 | #endif /* _DUAL_MOCO_H_ */ 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Midi Fighter 24 2 | 3 | Firmware and tools for Arduino based 24 buttons midi controller 4 | 5 | ![front](https://cloud.githubusercontent.com/assets/2462139/13602789/2c77cb92-e57c-11e5-8c30-129de1032203.JPG) 6 | 7 | see it in action [here](https://www.youtube.com/watch?v=FosVgqp6nJg) 8 | 9 | # Setup Arduino to send Midi via USB 10 | 11 | Install DualMocoLUFA on the Arduino 8U2's chip 12 | (look inside the `dualMocoLUFA` folder in this repository to find code and instructions) 13 | 14 | dualMocoLUFA Project is Copyright (C) 2013 by morecat_lab 15 | http://morecatlab.akiba.coocan.jp/ 16 | 17 | 18 | # Schematics 19 | 20 | ## In a nutshell 21 | 22 | - the 24 Arcade buttons are read by 3 `74HC165` shift registers connected on Arduino's pin 2(LOAD), 3(CLOCK), and 4, 5, 6 (shift registers DATA) 23 | - 2 x 7 led digit display are controlled by 2 `74HC595` shift registers connected on Arduino's pin 9(LATCH), 8(CLOCK) and 10(DATA). These shift registers are set in series. 24 | - two menu button on the side are connected to Arduino's pin 11 and 12 (with a pullup resistor) 25 | 26 | ## Buttons module 27 | Push buttons are connected to the arduino by using `74HC165` shift registers. 28 | 29 | Here is the schematic of one module for 8 buttons. I used 2 5x1 headers for the connection to the buttons. In the schematic, this is the labels `A`, `B`, `C`, `D` and `E`, `F`, `G`, `H`. The last pin of each headers are connected to `VCC`. Pull-up resistors are 10KΩ. 30 | 31 | ![button1](https://cloud.githubusercontent.com/assets/2462139/19406542/e7c0f1be-92c2-11e6-920d-a341865de7c5.jpg) 32 | 33 | A group of 4 buttons are connected to one header like this: 34 | 35 | ![button_connect](https://cloud.githubusercontent.com/assets/2462139/19406545/f8fe480a-92c2-11e6-8eab-4497147f179a.jpg) 36 | 37 | This module is duplicated for each group of push 8 buttons. 38 | For 24 buttons, 3 of these modules are connected together like this: 39 | 40 | ![buttons2](https://cloud.githubusercontent.com/assets/2462139/19406543/f2f55520-92c2-11e6-8f8c-428234ba1f08.JPG) 41 | 42 | And here are some photos of how I implemented this module using perf boards. 43 | 44 | ![button module 1](https://cloud.githubusercontent.com/assets/2462139/19406548/01a3aaf4-92c3-11e6-976f-0342468acd63.JPG) 45 | 46 | ![button module under](https://cloud.githubusercontent.com/assets/2462139/19406550/06a73f70-92c3-11e6-94d2-ddd80137cba9.JPG) 47 | 48 | ## Display module (optional) 49 | This module is **optional**, and is for displaying information using 2 seven segments LED. 50 | It is using `74HC595` shift registers to control the 16 LED using 3 pins of the Arduino. 16 LED consume quite a lot of power for the Arduino, so in the code, I take care of turning ED off after a delay. 51 | 52 | If I had to put more LED, I would use a `MAX7219` LED matrix controller as this chip use way less power. 53 | 54 | This is the schematic for one `74HC595`. Resistors are 4.7KΩ. 55 | 56 | ![display1](https://cloud.githubusercontent.com/assets/2462139/19406554/15fb5ef2-92c3-11e6-9b94-cd431faec68f.jpg) 57 | 58 | Here is how you connect the seven segments display 59 | 60 | ![7segmets](https://cloud.githubusercontent.com/assets/2462139/19406556/1ef6ce88-92c3-11e6-875f-9897b7435bdc.JPG) 61 | 62 | 2 of these modules are daisy chained together like so: 63 | 64 | ![display2](https://cloud.githubusercontent.com/assets/2462139/19406555/1a168bd8-92c3-11e6-89ca-1d1284efc49e.JPG) 65 | 66 | Some photos of how I implemented this module using perf boards: 67 | 68 | ![display module 5](https://cloud.githubusercontent.com/assets/2462139/19406573/68eb7b9c-92c3-11e6-96cb-42bfdfc0f404.JPG) 69 | 70 | ![display module 4](https://cloud.githubusercontent.com/assets/2462139/19406574/6d1817a2-92c3-11e6-92b1-232b7bd2afd0.JPG) 71 | 72 | ![display module 2](https://cloud.githubusercontent.com/assets/2462139/19406575/753c9b4c-92c3-11e6-90c1-774cb24555da.JPG) 73 | 74 | 75 | ## Connection to the Arduino 76 | Finally, here is how the two modules are connected to the Arduino 77 | 78 | ![arduino](https://cloud.githubusercontent.com/assets/2462139/19406562/34e74b1e-92c3-11e6-8588-e99ed15ccaef.JPG) 79 | 80 | Photos of how everything connects together: 81 | 82 | ![both modules](https://cloud.githubusercontent.com/assets/2462139/19406576/7f4d8f9c-92c3-11e6-8f71-dd0cbaf506c4.JPG) 83 | 84 | ![inside](https://cloud.githubusercontent.com/assets/2462139/19406563/3fc1a296-92c3-11e6-9dc8-861a3707acb7.JPG) 85 | 86 | ![inside empty](https://cloud.githubusercontent.com/assets/2462139/19406567/4eb5b3b4-92c3-11e6-9a1c-7389d86cff76.JPG) 87 | 88 | 89 | # More photos 90 | 91 | ![left](https://cloud.githubusercontent.com/assets/2462139/13602793/3979264c-e57c-11e5-9047-d57a520f9afd.JPG) 92 | 93 | ![back](https://cloud.githubusercontent.com/assets/2462139/13602795/3fc23138-e57c-11e5-8a2c-f87e0d586652.JPG) 94 | 95 | ![right](https://cloud.githubusercontent.com/assets/2462139/13602798/446ac42a-e57c-11e5-96e5-483d790f61e0.JPG) 96 | 97 | ![shiftregisters](https://cloud.githubusercontent.com/assets/2462139/13602802/4a83c0d2-e57c-11e5-9add-57483b9590d8.JPG) 98 | -------------------------------------------------------------------------------- /utilities/padDebug.html: -------------------------------------------------------------------------------- 1 | 2 | Pad debug 3 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 188 | 189 | -------------------------------------------------------------------------------- /dualMocoLUFA/Descriptors.h: -------------------------------------------------------------------------------- 1 | /* 2 | dualMocoLUFA Project 3 | Copyright (C) 2013 by morecat_lab 4 | 5 | 2013/09/22 6 | 7 | http://morecatlab.akiba.coocan.jp/ 8 | 9 | based on LUFA-100807 10 | */ 11 | /* 12 | LUFA Library 13 | Copyright (C) Dean Camera, 2010. 14 | 15 | dean [at] fourwalledcubicle [dot] com 16 | www.fourwalledcubicle.com 17 | */ 18 | 19 | /* 20 | Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) 21 | 22 | Permission to use, copy, modify, distribute, and sell this 23 | software and its documentation for any purpose is hereby granted 24 | without fee, provided that the above copyright notice appear in 25 | all copies and that both that the copyright notice and this 26 | permission notice and warranty disclaimer appear in supporting 27 | documentation, and that the name of the author not be used in 28 | advertising or publicity pertaining to distribution of the 29 | software without specific, written prior permission. 30 | 31 | The author disclaim all warranties with regard to this 32 | software, including all implied warranties of merchantability 33 | and fitness. In no event shall the author be liable for any 34 | special, indirect or consequential damages or any damages 35 | whatsoever resulting from loss of use, data or profits, whether 36 | in an action of contract, negligence or other tortious action, 37 | arising out of or in connection with the use or performance of 38 | this software. 39 | */ 40 | 41 | /** \file 42 | * 43 | * Header file for Descriptors.c. 44 | */ 45 | 46 | #ifndef _DESCRIPTORS_H_ 47 | #define _DESCRIPTORS_H_ 48 | 49 | /* Includes: */ 50 | #include 51 | 52 | #include 53 | #include 54 | #include 55 | 56 | /* Product-specific definitions: */ 57 | #define ARDUINO_UNO_PID 0x0001 58 | #define ARDUINO_MEGA2560_PID 0x0010 59 | 60 | /* Macros for CDC */ 61 | /** Endpoint number of the CDC device-to-host notification IN endpoint. */ 62 | #define CDC_NOTIFICATION_EPNUM 2 63 | /** Endpoint number of the CDC device-to-host data IN endpoint. */ 64 | #define CDC_TX_EPNUM 3 65 | /** Endpoint number of the CDC host-to-device data OUT endpoint. */ 66 | #define CDC_RX_EPNUM 4 67 | /** Size in bytes of the CDC device-to-host notification IN endpoint. */ 68 | #define CDC_NOTIFICATION_EPSIZE 8 69 | /** Size in bytes of the CDC data IN and OUT endpoints. */ 70 | #define CDC_TXRX_EPSIZE 64 71 | 72 | /* Macros for MIDI */ 73 | /** Endpoint number of the MIDI streaming data IN endpoint, for device-to-host data transfers. */ 74 | #define MIDI_STREAM_IN_EPNUM 2 75 | /** Endpoint number of the MIDI streaming data OUT endpoint, for host-to-device data transfers. */ 76 | #define MIDI_STREAM_OUT_EPNUM 1 77 | /** Endpoint size in bytes of the Audio isochronous streaming data IN and OUT endpoints. */ 78 | #define MIDI_STREAM_EPSIZE 64 79 | 80 | /* Type Defines for CDC */ 81 | /** Type define for the device configuration descriptor structure. This must be defined in the 82 | * application code, as the configuration descriptor contains several sub-descriptors which 83 | * vary between devices, and which describe the device's usage to the host. 84 | */ 85 | typedef struct 86 | { 87 | USB_Descriptor_Configuration_Header_t Config; 88 | USB_Descriptor_Interface_t CDC_CCI_Interface; 89 | CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_IntHeader; 90 | CDC_FUNCTIONAL_DESCRIPTOR(1) CDC_Functional_AbstractControlManagement; 91 | CDC_FUNCTIONAL_DESCRIPTOR(2) CDC_Functional_Union; 92 | USB_Descriptor_Endpoint_t CDC_NotificationEndpoint; 93 | USB_Descriptor_Interface_t CDC_DCI_Interface; 94 | USB_Descriptor_Endpoint_t CDC_DataOutEndpoint; 95 | USB_Descriptor_Endpoint_t CDC_DataInEndpoint; 96 | } USB_Descriptor_ConfigurationCDC_t; 97 | 98 | /* Type Defines for MIDI */ 99 | typedef struct 100 | { 101 | USB_Descriptor_Configuration_Header_t Config; 102 | USB_Descriptor_Interface_t Audio_ControlInterface; 103 | USB_Audio_Interface_AC_t Audio_ControlInterface_SPC; 104 | USB_Descriptor_Interface_t Audio_StreamInterface; 105 | USB_MIDI_AudioInterface_AS_t Audio_StreamInterface_SPC; 106 | USB_MIDI_In_Jack_t MIDI_In_Jack_Emb; 107 | USB_MIDI_In_Jack_t MIDI_In_Jack_Ext; 108 | USB_MIDI_Out_Jack_t MIDI_Out_Jack_Emb; 109 | USB_MIDI_Out_Jack_t MIDI_Out_Jack_Ext; 110 | USB_Audio_StreamEndpoint_Std_t MIDI_In_Jack_Endpoint; 111 | USB_MIDI_Jack_Endpoint_t MIDI_In_Jack_Endpoint_SPC; 112 | USB_Audio_StreamEndpoint_Std_t MIDI_Out_Jack_Endpoint; 113 | USB_MIDI_Jack_Endpoint_t MIDI_Out_Jack_Endpoint_SPC; 114 | } USB_Descriptor_ConfigurationMIDI_t; 115 | 116 | /* Function Prototypes: */ 117 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 118 | const uint8_t wIndex, 119 | void** const DescriptorAddress) ATTR_WARN_UNUSED_RESULT ATTR_NON_NULL_PTR_ARG(3); 120 | #endif 121 | -------------------------------------------------------------------------------- /dualMocoLUFA/Lib/LightweightRingBuff.h: -------------------------------------------------------------------------------- 1 | /* 2 | LUFA Library 3 | Copyright (C) Dean Camera, 2010. 4 | 5 | dean [at] fourwalledcubicle [dot] com 6 | www.fourwalledcubicle.com 7 | */ 8 | 9 | /* 10 | Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) 11 | 12 | Permission to use, copy, modify, distribute, and sell this 13 | software and its documentation for any purpose is hereby granted 14 | without fee, provided that the above copyright notice appear in 15 | all copies and that both that the copyright notice and this 16 | permission notice and warranty disclaimer appear in supporting 17 | documentation, and that the name of the author not be used in 18 | advertising or publicity pertaining to distribution of the 19 | software without specific, written prior permission. 20 | 21 | The author disclaim all warranties with regard to this 22 | software, including all implied warranties of merchantability 23 | and fitness. In no event shall the author be liable for any 24 | special, indirect or consequential damages or any damages 25 | whatsoever resulting from loss of use, data or profits, whether 26 | in an action of contract, negligence or other tortious action, 27 | arising out of or in connection with the use or performance of 28 | this software. 29 | */ 30 | 31 | /** \file 32 | * 33 | * Ultra lightweight ring buffer, for fast insertion/deletion. 34 | */ 35 | 36 | #ifndef _ULW_RING_BUFF_H_ 37 | #define _ULW_RING_BUFF_H_ 38 | 39 | /* Includes: */ 40 | #include 41 | 42 | #include 43 | #include 44 | 45 | /* Defines: */ 46 | /** Size of each ring buffer, in data elements - must be between 1 and 255. */ 47 | #define BUFFER_SIZE 128 48 | 49 | /** Maximum number of data elements to buffer before forcing a flush. 50 | * Must be less than BUFFER_SIZE 51 | */ 52 | #define BUFFER_NEARLY_FULL 96 53 | 54 | /** Type of data to store into the buffer. */ 55 | #define RingBuff_Data_t uint8_t 56 | 57 | /** Datatype which may be used to store the count of data stored in a buffer, retrieved 58 | * via a call to \ref RingBuffer_GetCount(). 59 | */ 60 | #if (BUFFER_SIZE <= 0xFF) 61 | #define RingBuff_Count_t uint8_t 62 | #else 63 | #define RingBuff_Count_t uint16_t 64 | #endif 65 | 66 | /* Type Defines: */ 67 | /** Type define for a new ring buffer object. Buffers should be initialized via a call to 68 | * \ref RingBuffer_InitBuffer() before use. 69 | */ 70 | typedef struct 71 | { 72 | RingBuff_Data_t Buffer[BUFFER_SIZE]; /**< Internal ring buffer data, referenced by the buffer pointers. */ 73 | RingBuff_Data_t* In; /**< Current storage location in the circular buffer */ 74 | RingBuff_Data_t* Out; /**< Current retrieval location in the circular buffer */ 75 | RingBuff_Count_t Count; 76 | } RingBuff_t; 77 | 78 | /* Inline Functions: */ 79 | /** Initializes a ring buffer ready for use. Buffers must be initialized via this function 80 | * before any operations are called upon them. Already initialized buffers may be reset 81 | * by re-initializing them using this function. 82 | * 83 | * \param[out] Buffer Pointer to a ring buffer structure to initialize 84 | */ 85 | static inline void RingBuffer_InitBuffer(RingBuff_t* const Buffer) 86 | { 87 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 88 | { 89 | Buffer->In = Buffer->Buffer; 90 | Buffer->Out = Buffer->Buffer; 91 | } 92 | } 93 | 94 | /** Retrieves the minimum number of bytes stored in a particular buffer. This value is computed 95 | * by entering an atomic lock on the buffer while the IN and OUT locations are fetched, so that 96 | * the buffer cannot be modified while the computation takes place. This value should be cached 97 | * when reading out the contents of the buffer, so that as small a time as possible is spent 98 | * in an atomic lock. 99 | * 100 | * \note The value returned by this function is guaranteed to only be the minimum number of bytes 101 | * stored in the given buffer; this value may change as other threads write new data and so 102 | * the returned number should be used only to determine how many successive reads may safely 103 | * be performed on the buffer. 104 | * 105 | * \param[in] Buffer Pointer to a ring buffer structure whose count is to be computed 106 | */ 107 | static inline RingBuff_Count_t RingBuffer_GetCount(RingBuff_t* const Buffer) 108 | { 109 | RingBuff_Count_t Count; 110 | 111 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 112 | { 113 | Count = Buffer->Count; 114 | } 115 | 116 | return Count; 117 | } 118 | 119 | /** Atomically determines if the specified ring buffer contains any free space. This should 120 | * be tested before storing data to the buffer, to ensure that no data is lost due to a 121 | * buffer overrun. 122 | * 123 | * \param[in,out] Buffer Pointer to a ring buffer structure to insert into 124 | * 125 | * \return Boolean true if the buffer contains no free space, false otherwise 126 | */ 127 | static inline bool RingBuffer_IsFull(RingBuff_t* const Buffer) 128 | { 129 | return (RingBuffer_GetCount(Buffer) == BUFFER_SIZE); 130 | } 131 | 132 | /** Atomically determines if the specified ring buffer contains any data. This should 133 | * be tested before removing data from the buffer, to ensure that the buffer does not 134 | * underflow. 135 | * 136 | * If the data is to be removed in a loop, store the total number of bytes stored in the 137 | * buffer (via a call to the \ref RingBuffer_GetCount() function) in a temporary variable 138 | * to reduce the time spent in atomicity locks. 139 | * 140 | * \param[in,out] Buffer Pointer to a ring buffer structure to insert into 141 | * 142 | * \return Boolean true if the buffer contains no free space, false otherwise 143 | */ 144 | static inline bool RingBuffer_IsEmpty(RingBuff_t* const Buffer) 145 | { 146 | return (RingBuffer_GetCount(Buffer) == 0); 147 | } 148 | 149 | /** Inserts an element into the ring buffer. 150 | * 151 | * \note Only one execution thread (main program thread or an ISR) may insert into a single buffer 152 | * otherwise data corruption may occur. Insertion and removal may occur from different execution 153 | * threads. 154 | * 155 | * \param[in,out] Buffer Pointer to a ring buffer structure to insert into 156 | * \param[in] Data Data element to insert into the buffer 157 | */ 158 | static inline void RingBuffer_Insert(RingBuff_t* const Buffer, 159 | const RingBuff_Data_t Data) 160 | { 161 | *Buffer->In = Data; 162 | 163 | if (++Buffer->In == &Buffer->Buffer[BUFFER_SIZE]) 164 | Buffer->In = Buffer->Buffer; 165 | 166 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 167 | { 168 | Buffer->Count++; 169 | } 170 | } 171 | 172 | /** Removes an element from the ring buffer. 173 | * 174 | * \note Only one execution thread (main program thread or an ISR) may remove from a single buffer 175 | * otherwise data corruption may occur. Insertion and removal may occur from different execution 176 | * threads. 177 | * 178 | * \param[in,out] Buffer Pointer to a ring buffer structure to retrieve from 179 | * 180 | * \return Next data element stored in the buffer 181 | */ 182 | static inline RingBuff_Data_t RingBuffer_Remove(RingBuff_t* const Buffer) 183 | { 184 | RingBuff_Data_t Data = *Buffer->Out; 185 | 186 | if (++Buffer->Out == &Buffer->Buffer[BUFFER_SIZE]) 187 | Buffer->Out = Buffer->Buffer; 188 | 189 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 190 | { 191 | Buffer->Count--; 192 | } 193 | 194 | return Data; 195 | } 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /firmware/firmware.ino: -------------------------------------------------------------------------------- 1 | #include "MIDI.h" 2 | #include "DualDigitDisplay.h" 3 | #include "Program.h" 4 | #include "scales.h" 5 | #include "global.h" 6 | 7 | #define PULSE_WIDTH_USEC 5 8 | #define ANALOG_READ_RATE 50 9 | #define DEBOUNCE_TRIGGER 8 10 | 11 | #define LOAD 2 12 | #define CLOCK 3 13 | #define READ1 4 14 | #define READ2 5 15 | #define READ3 6 16 | 17 | #define FOOT_SWITCH 7 18 | #define SHIFT_A 12 19 | #define SHIFT_B 11 20 | 21 | #define SHIFT_A_BIT 24 22 | #define SHIFT_B_BIT 25 23 | 24 | int pinStates[26]; // state values of all 24 button pins 25 | int changed[26]; // a list of button with changed state in the current loop 26 | int debounce[26]; // debounce counter values of all 24 pins 27 | bool debouncing[26]; // pins currently in debounce mode 28 | 29 | // long footControlRead; // analog read value of connected foot controller 30 | // long footControlValue; // analog current value of connected foot controller 31 | // int footTimer; 32 | 33 | DualDigitDisplay display; 34 | Program program; 35 | 36 | const int pinToPadMap[26] = { 37 | 2, 14, 23, 3, 15, 19, 6, 10, 22, 7, 11, 18, 38 | 0, 13, 17, 1, 12, 21, 4, 9, 16, 5, 8, 20, 39 | 24, 25 // shift buttons 40 | }; 41 | 42 | 43 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 44 | 45 | void setup() { 46 | pinMode(LOAD, OUTPUT); 47 | pinMode(CLOCK, OUTPUT); 48 | pinMode(READ1, INPUT); 49 | pinMode(READ2, INPUT); 50 | pinMode(FOOT_SWITCH, INPUT); 51 | pinMode(SHIFT_A, INPUT); 52 | pinMode(SHIFT_B, INPUT); 53 | 54 | for (int i = 0; i < 24; ++i) { 55 | pinStates[i] = 0; 56 | changed[i] = 0; 57 | debounce[i] = 0; 58 | debouncing[i] = false; 59 | } 60 | 61 | // footControlRead = 0; 62 | // footControlValue = 0; 63 | // footTimer = ANALOG_READ_RATE; 64 | 65 | display.setup(); 66 | program.init(&display); 67 | MIDI.begin(); 68 | mainMidiChannel = 4; 69 | 70 | display.clear(); 71 | } 72 | 73 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 74 | 75 | int readButtons() { 76 | int nChanged = 0; 77 | //------------------------------------- 78 | // store pin values for all shift register 79 | digitalWrite(LOAD, LOW); 80 | 81 | delayMicroseconds(PULSE_WIDTH_USEC); // TODO: here, insert some computation 82 | display.click(); 83 | 84 | digitalWrite(LOAD, HIGH); 85 | 86 | for (int pin = 0; pin < 24; ++pin) { 87 | //------------------------------------- 88 | // read shift registers values 89 | int values[3]; 90 | 91 | values[0] = digitalRead(READ1); 92 | values[1] = digitalRead(READ2); 93 | values[2] = digitalRead(READ3); 94 | 95 | digitalWrite(CLOCK, HIGH); 96 | // delayMicroseconds(PULSE_WIDTH_USEC); 97 | 98 | for (int i = 0; i < 3; ++i) { 99 | int value = values[i]; 100 | //------------------------------------- 101 | // update and debounce value 1 102 | if (pinStates[pin] != value) { 103 | // pin changed value 104 | pinStates[pin] = value; 105 | // switch debouncing 106 | // - if previously bebouncing, then it was an artefact 107 | // - if not, then debouncing start 108 | debouncing[pin] = !debouncing[pin]; 109 | // initiate debounce counter for this pin 110 | debounce[pin] = 0; 111 | } else if (debouncing[pin]) { 112 | // increment debouncing counter value for this pin 113 | if (debounce[pin]++ == DEBOUNCE_TRIGGER) { 114 | // debounce is finished, button state has changed 115 | changed[nChanged++] = pin; 116 | debouncing[pin] = false; 117 | } 118 | } 119 | } 120 | 121 | //------------------------------------- 122 | // update and debounce value 2 123 | /*if (pinStates[++pin] != value2) { 124 | pinStates[pin] = value2; 125 | debouncing[pin] = !debouncing[pin]; 126 | debounce[pin] = 0; 127 | } else if (debouncing[pin]) { 128 | if (debounce[pin]++ == DEBOUNCE_TRIGGER) { 129 | changed[nChanged++] = pin; 130 | debouncing[pin] = false; 131 | } 132 | } 133 | 134 | //------------------------------------- 135 | // update and debounce value 3 136 | if (pinStates[++pin] != value3) { 137 | pinStates[pin] = value3; 138 | debouncing[pin] = !debouncing[pin]; 139 | debounce[pin] = 0; 140 | } else if (debouncing[pin]) { 141 | if (debounce[pin]++ == DEBOUNCE_TRIGGER) { 142 | changed[nChanged++] = pin; 143 | debouncing[pin] = false; 144 | } 145 | }*/ 146 | 147 | //------------------------------------- 148 | digitalWrite(CLOCK, LOW); 149 | } 150 | 151 | 152 | // TODO: refactor this 153 | 154 | int shiftA = digitalRead(SHIFT_A); 155 | if (pinStates[SHIFT_A_BIT] != shiftA) { 156 | pinStates[SHIFT_A_BIT] = shiftA; 157 | debouncing[SHIFT_A_BIT] = !debouncing[SHIFT_A_BIT]; 158 | debounce[SHIFT_A_BIT] = 0; 159 | } else if (debouncing[SHIFT_A_BIT]) { 160 | if (debounce[SHIFT_A_BIT]++ == DEBOUNCE_TRIGGER) { 161 | changed[nChanged++] = SHIFT_A_BIT; 162 | debouncing[SHIFT_A_BIT] = false; 163 | } 164 | } 165 | 166 | int shiftB = digitalRead(SHIFT_B); 167 | if (pinStates[SHIFT_B_BIT] != shiftB) { 168 | pinStates[SHIFT_B_BIT] = shiftB; 169 | debouncing[SHIFT_B_BIT] = !debouncing[SHIFT_B_BIT]; 170 | debounce[SHIFT_B_BIT] = 0; 171 | } else if (debouncing[SHIFT_B_BIT]) { 172 | if (debounce[SHIFT_B_BIT]++ == DEBOUNCE_TRIGGER) { 173 | changed[nChanged++] = SHIFT_B_BIT; 174 | debouncing[SHIFT_B_BIT] = false; 175 | } 176 | } 177 | 178 | return nChanged; 179 | } 180 | 181 | 182 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 183 | 184 | void loop() { 185 | int nChanged = readButtons(); 186 | 187 | // foot controler 188 | 189 | /*if (--footTimer == 0) { 190 | // only one read every 50 cycles for stability 191 | footControlRead = analogRead(A0); 192 | footTimer = ANALOG_READ_RATE; 193 | if (abs(footControlRead - footControlValue) > 60) { 194 | footControlValue = footControlRead; 195 | long c = (footControlRead - 10) * 128 / 990; 196 | if (c < 0) c = 0; 197 | else if (c > 127) c = 127; 198 | byte cc = (byte) c; 199 | MIDI.sendControlChange(24, cc, 1); 200 | } 201 | }*/ 202 | 203 | if (nChanged == 0) return; 204 | 205 | for (int i = 0; i < nChanged; ++i) { 206 | if (changed[i] < 24) { 207 | program.triggerPad(pinToPadMap[changed[i]], pinStates[changed[i]]); 208 | } else if (changed[i] == SHIFT_A_BIT) { 209 | // shift A + shift B 210 | if (pinStates[SHIFT_A_BIT] && pinStates[SHIFT_B_BIT]) { 211 | mainMenu(); 212 | return; 213 | } 214 | // shift A, down edge 215 | if (!pinStates[SHIFT_A_BIT]) program.shiftUp(); 216 | } else if (changed[i] == SHIFT_B_BIT) { 217 | // shift B + shift A 218 | if (pinStates[SHIFT_A_BIT] && pinStates[SHIFT_B_BIT]) { 219 | mainMenu(); 220 | return; 221 | } 222 | // shift B, down edge 223 | if (!pinStates[SHIFT_B_BIT]) program.shiftDown(); 224 | } 225 | } 226 | } 227 | 228 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 229 | 230 | void mainMenu() { 231 | display.displayString("MN"); 232 | program.allPadOff(); 233 | while (true) { 234 | int nChanged = readButtons(); 235 | if (nChanged == 0) continue; 236 | for (int i = 0; i < nChanged; ++i) { 237 | // int state = pinStates[changed[i]]; 238 | if (pinStates[changed[i]]) continue; // down edge only 239 | int button = pinToPadMap[changed[i]]; 240 | switch (button) { 241 | case 16: channelMenu(); break; 242 | case 20: scaleMenu(); break; 243 | case 21: rootNoteMenu(); break; 244 | case 22: rootPadMenu(); break; 245 | case 23: display.displayString("GO"); return; // exit menu 246 | } 247 | } 248 | } 249 | } 250 | 251 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 252 | 253 | void scaleMenu() { 254 | display.displayString("SC"); 255 | while (true) { 256 | int nChanged = readButtons(); 257 | if (nChanged == 0) continue; 258 | for (int i = 0; i < nChanged; ++i) { 259 | // int state = pinStates[changed[i]]; 260 | if (pinStates[changed[i]]) continue; // down edge only 261 | int button = pinToPadMap[changed[i]]; 262 | switch (button) { 263 | case 0: display.displayString("MA"); program.setScaleMode(scale_major, 6); return; 264 | case 1: display.displayString("HI"); program.setScaleMode(scale_harmonicMinor, 6); return; 265 | case 2: display.displayString("MM"); program.setScaleMode(scale_melodicMinor, 6); return; 266 | case 3: display.displayString("WT"); program.setScaleMode(scale_wholeTone, 5); return; 267 | case 4: display.displayString("DI"); program.setScaleMode(scale_diminished, 7); return; 268 | case 5: display.displayString("PA"); program.setScaleMode(scale_majorPentatonic, 4); return; 269 | case 6: display.displayString("PI"); program.setScaleMode(scale_minorPentatonic, 4); return; 270 | case 7: display.displayString("JP"); program.setScaleMode(scale_japInSen, 4); return; 271 | case 8: display.displayString("BM"); program.setScaleMode(scale_majorBebop, 7); return; 272 | case 9: display.displayString("BD"); program.setScaleMode(scale_dominantBebop, 7); return; 273 | case 10: display.displayString("BL"); program.setScaleMode(scale_blues, 5); return; 274 | case 11: display.displayString("AR"); program.setScaleMode(scale_arabic, 6); return; 275 | case 12: display.displayString("EN"); program.setScaleMode(scale_enigmatic, 6); return; 276 | case 13: display.displayString("NE"); program.setScaleMode(scale_neapolitan, 6); return; 277 | case 14: display.displayString("NI"); program.setScaleMode(scale_neapolitanMinor, 6); return; 278 | case 15: display.displayString("HU"); program.setScaleMode(scale_hungarianMinor, 6); return; 279 | case 16: display.displayString("DO"); program.setScaleMode(scale_dorian, 6); return; 280 | case 17: display.displayString("PH"); program.setScaleMode(scale_phrygian, 6); return; 281 | case 18: display.displayString("LY"); program.setScaleMode(scale_lydian, 6); return; 282 | case 19: display.displayString("MI"); program.setScaleMode(scale_mixolidian, 6); return; 283 | case 20: display.displayString("AE"); program.setScaleMode(scale_aeolidian, 6); return; 284 | case 21: display.displayString("LO"); program.setScaleMode(scale_locrian, 6); return; 285 | case 22: display.displayString("CR"); program.setChromaticMode(12); return; 286 | case 23: display.displayString("MP"); program.setChromaticMode(24); return; 287 | case 24: /*shift A*/ 288 | case 25: /*shift B*/ return; 289 | } 290 | } 291 | } 292 | } 293 | 294 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 295 | 296 | void rootNoteMenu() { 297 | display.displayString("RN"); 298 | while (true) { 299 | int nChanged = readButtons(); 300 | if (nChanged == 0) continue; 301 | for (int i = 0; i < nChanged; ++i) { 302 | // int state = pinStates[changed[i]]; 303 | if (pinStates[changed[i]]) continue; // down edge only 304 | int button = pinToPadMap[changed[i]]; 305 | if (button < 12) { 306 | display.displayNote(button); 307 | program.setRootNote(button); 308 | return; 309 | } 310 | } 311 | } 312 | } 313 | 314 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 315 | 316 | void rootPadMenu() { 317 | display.displayString("RP"); 318 | while (true) { 319 | int nChanged = readButtons(); 320 | if (nChanged == 0) continue; 321 | for (int i = 0; i < nChanged; ++i) { 322 | // int state = pinStates[changed[i]]; 323 | if (pinStates[changed[i]]) continue; // down edge only 324 | int button = pinToPadMap[changed[i]]; 325 | if (button < 24) { 326 | display.displayNumber(button); 327 | program.setRootPad(button); 328 | return; 329 | } 330 | } 331 | } 332 | } 333 | 334 | //▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ 335 | 336 | void channelMenu() { 337 | display.displayString("CH"); 338 | while (true) { 339 | int nChanged = readButtons(); 340 | if (nChanged == 0) continue; 341 | for (int i = 0; i < nChanged; ++i) { 342 | // int state = pinStates[changed[i]]; 343 | if (pinStates[changed[i]]) continue; // down edge only 344 | int button = pinToPadMap[changed[i]]; 345 | if (button < 16) { 346 | mainMidiChannel = button + 1; 347 | display.displayNumber(button + 1); 348 | return; 349 | } 350 | } 351 | } 352 | } -------------------------------------------------------------------------------- /firmware/MIDI.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file MIDI.h 3 | * Project MIDI Library 4 | * @brief MIDI Library for the Arduino 5 | * Version 3.2 6 | * @author Francois Best 7 | * @date 24/02/11 8 | * License GPL Forty Seven Effects - 2011 9 | */ 10 | 11 | #ifndef LIB_MIDI_H_ 12 | #define LIB_MIDI_H_ 13 | 14 | #include 15 | 16 | 17 | /* 18 | ############################################################### 19 | # # 20 | # CONFIGURATION AREA # 21 | # # 22 | # Here are a few settings you can change to customize # 23 | # the library for your own project. You can for example # 24 | # choose to compile only parts of it so you gain flash # 25 | # space and optimise the speed of your sketch. # 26 | # # 27 | ############################################################### 28 | */ 29 | 30 | 31 | #define COMPILE_MIDI_IN 0 // Set this setting to 1 to use the MIDI input. 32 | #define COMPILE_MIDI_OUT 1 // Set this setting to 1 to use the MIDI output. 33 | #define COMPILE_MIDI_THRU 0 // Set this setting to 1 to use the MIDI Soft Thru feature 34 | // Please note that the Thru will work only when both COMPILE_MIDI_IN and COMPILE_MIDI_OUT set to 1. 35 | 36 | 37 | #define USE_SERIAL_PORT Serial // Change the number (to Serial1 for example) if you want 38 | // to use a different serial port for MIDI I/O. 39 | 40 | 41 | #define USE_RUNNING_STATUS 0 // Running status enables short messages when sending multiple values 42 | // of the same type and channel. 43 | // Set to 0 if you have troubles with controlling you hardware. 44 | 45 | 46 | #define USE_CALLBACKS 0 // Set this to 1 if you want to use callback handlers (to bind your functions to the library). 47 | // To use the callbacks, you need to have COMPILE_MIDI_IN set to 1 48 | 49 | #define USE_1BYTE_PARSING 1 // Each call to MIDI.read will only parse one byte (might be faster). 50 | 51 | 52 | // END OF CONFIGURATION AREA 53 | // (do not modify anything under this line unless you know what you are doing) 54 | 55 | 56 | #define MIDI_BAUDRATE 31250 57 | 58 | #define MIDI_CHANNEL_OMNI 0 59 | #define MIDI_CHANNEL_OFF 17 // and over 60 | 61 | #define MIDI_SYSEX_ARRAY_SIZE 255 // Maximum size is 65535 bytes. 62 | 63 | /*! Type definition for practical use (because "unsigned char" is a bit long to write.. )*/ 64 | typedef uint8_t byte; 65 | typedef uint16_t word; 66 | 67 | /*! Enumeration of MIDI types */ 68 | enum kMIDIType { 69 | NoteOff = 0x80, ///< Note Off 70 | NoteOn = 0x90, ///< Note On 71 | AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch 72 | ControlChange = 0xB0, ///< Control Change / Channel Mode 73 | ProgramChange = 0xC0, ///< Program Change 74 | AfterTouchChannel = 0xD0, ///< Channel (monophonic) AfterTouch 75 | PitchBend = 0xE0, ///< Pitch Bend 76 | SystemExclusive = 0xF0, ///< System Exclusive 77 | TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame 78 | SongPosition = 0xF2, ///< System Common - Song Position Pointer 79 | SongSelect = 0xF3, ///< System Common - Song Select 80 | TuneRequest = 0xF6, ///< System Common - Tune Request 81 | Clock = 0xF8, ///< System Real Time - Timing Clock 82 | Start = 0xFA, ///< System Real Time - Start 83 | Continue = 0xFB, ///< System Real Time - Continue 84 | Stop = 0xFC, ///< System Real Time - Stop 85 | ActiveSensing = 0xFE, ///< System Real Time - Active Sensing 86 | SystemReset = 0xFF, ///< System Real Time - System Reset 87 | InvalidType = 0x00 ///< For notifying errors 88 | }; 89 | 90 | /*! Enumeration of Thru filter modes */ 91 | enum kThruFilterMode { 92 | Off = 0, ///< Thru disabled (nothing passes through). 93 | Full = 1, ///< Fully enabled Thru (every incoming message is sent back). 94 | SameChannel = 2, ///< Only the messages on the Input Channel will be sent back. 95 | DifferentChannel = 3 ///< All the messages but the ones on the Input Channel will be sent back. 96 | }; 97 | 98 | 99 | /*! The midimsg structure contains decoded data of a MIDI message read from the serial port with read() or thru(). \n */ 100 | struct midimsg { 101 | /*! The MIDI channel on which the message was recieved. \n Value goes from 1 to 16. */ 102 | byte channel; 103 | /*! The type of the message (see the define section for types reference) */ 104 | kMIDIType type; 105 | /*! The first data byte.\n Value goes from 0 to 127.\n */ 106 | byte data1; 107 | /*! The second data byte. If the message is only 2 bytes long, this one is null.\n Value goes from 0 to 127. */ 108 | byte data2; 109 | /*! System Exclusive dedicated byte array. \n Array length is stocked on 16 bits, in data1 (LSB) and data2 (MSB) */ 110 | byte sysex_array[MIDI_SYSEX_ARRAY_SIZE]; 111 | /*! This boolean indicates if the message is valid or not. There is no channel consideration here, validity means the message respects the MIDI norm. */ 112 | bool valid; 113 | }; 114 | 115 | 116 | 117 | 118 | /*! \brief The main class for MIDI handling.\n 119 | See member descriptions to know how to use it, 120 | or check out the examples supplied with the library. 121 | */ 122 | class MIDI_Class { 123 | 124 | 125 | public: 126 | // Constructor and Destructor 127 | MIDI_Class(); 128 | ~MIDI_Class(); 129 | 130 | 131 | void begin(const byte inChannel = 1); 132 | 133 | 134 | 135 | 136 | /* ####### OUTPUT COMPILATION BLOCK ####### */ 137 | #if COMPILE_MIDI_OUT 138 | 139 | public: 140 | 141 | void sendNoteOn(byte NoteNumber,byte Velocity,byte Channel); 142 | void sendNoteOff(byte NoteNumber,byte Velocity,byte Channel); 143 | void sendProgramChange(byte ProgramNumber,byte Channel); 144 | void sendControlChange(byte ControlNumber, byte ControlValue,byte Channel); 145 | void sendPitchBend(int PitchValue,byte Channel); 146 | void sendPitchBend(unsigned int PitchValue,byte Channel); 147 | void sendPitchBend(double PitchValue,byte Channel); 148 | void sendPolyPressure(byte NoteNumber,byte Pressure,byte Channel); 149 | void sendAfterTouch(byte Pressure,byte Channel); 150 | void sendSysEx(int length, const byte *const array,bool ArrayContainsBoundaries = false); 151 | void sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble); 152 | void sendTimeCodeQuarterFrame(byte data); 153 | void sendSongPosition(unsigned int Beats); 154 | void sendSongSelect(byte SongNumber); 155 | void sendTuneRequest(); 156 | void sendRealTime(kMIDIType Type); 157 | 158 | void send(kMIDIType type, byte param1, byte param2, byte channel); 159 | 160 | private: 161 | 162 | const byte genstatus(const kMIDIType inType,const byte inChannel) const; 163 | 164 | 165 | // Attributes 166 | #if USE_RUNNING_STATUS 167 | byte mRunningStatus_TX; 168 | #endif // USE_RUNNING_STATUS 169 | 170 | #endif // COMPILE_MIDI_OUT 171 | 172 | 173 | 174 | /* ####### INPUT COMPILATION BLOCK ####### */ 175 | #if COMPILE_MIDI_IN 176 | 177 | public: 178 | 179 | bool read(); 180 | bool read(const byte Channel); 181 | 182 | // Getters 183 | kMIDIType getType() const; 184 | byte getChannel() const; 185 | byte getData1() const; 186 | byte getData2() const; 187 | const byte * getSysExArray() const; 188 | unsigned int getSysExArrayLength() const; 189 | bool check() const; 190 | 191 | byte getInputChannel() const 192 | { 193 | return mInputChannel; 194 | } 195 | 196 | // Setters 197 | void setInputChannel(const byte Channel); 198 | 199 | /*! \brief Extract an enumerated MIDI type from a status byte. 200 | 201 | This is a utility static method, used internally, made public so you can handle kMIDITypes more easily. 202 | */ 203 | static inline const kMIDIType getTypeFromStatusByte(const byte inStatus) 204 | { 205 | if ((inStatus < 0x80) 206 | || (inStatus == 0xF4) 207 | || (inStatus == 0xF5) 208 | || (inStatus == 0xF9) 209 | || (inStatus == 0xFD)) return InvalidType; // data bytes and undefined. 210 | if (inStatus < 0xF0) return (kMIDIType)(inStatus & 0xF0); // Channel message, remove channel nibble. 211 | else return (kMIDIType)inStatus; 212 | } 213 | 214 | 215 | #if USE_CALLBACKS 216 | 217 | void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)); 218 | void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)); 219 | void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)); 220 | void setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)); 221 | void setHandleProgramChange(void (*fptr)(byte channel, byte number)); 222 | void setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)); 223 | void setHandlePitchBend(void (*fptr)(byte channel, int bend)); 224 | void setHandleSystemExclusive(void (*fptr)(byte * array, byte size)); 225 | void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)); 226 | void setHandleSongPosition(void (*fptr)(unsigned int beats)); 227 | void setHandleSongSelect(void (*fptr)(byte songnumber)); 228 | void setHandleTuneRequest(void (*fptr)(void)); 229 | void setHandleClock(void (*fptr)(void)); 230 | void setHandleStart(void (*fptr)(void)); 231 | void setHandleContinue(void (*fptr)(void)); 232 | void setHandleStop(void (*fptr)(void)); 233 | void setHandleActiveSensing(void (*fptr)(void)); 234 | void setHandleSystemReset(void (*fptr)(void)); 235 | 236 | void disconnectCallbackFromType(kMIDIType Type); 237 | 238 | #endif // USE_CALLBACKS 239 | 240 | 241 | private: 242 | 243 | bool input_filter(byte inChannel); 244 | bool parse(byte inChannel); 245 | void reset_input_attributes(); 246 | 247 | // Attributes 248 | byte mRunningStatus_RX; 249 | byte mInputChannel; 250 | 251 | byte mPendingMessage[MIDI_SYSEX_ARRAY_SIZE]; 252 | unsigned int mPendingMessageExpectedLenght; 253 | unsigned int mPendingMessageIndex; // Extended to unsigned int for larger sysex payloads. 254 | 255 | midimsg mMessage; 256 | 257 | #if USE_CALLBACKS 258 | 259 | void launchCallback(); 260 | 261 | void (*mNoteOffCallback)(byte channel, byte note, byte velocity); 262 | void (*mNoteOnCallback)(byte channel, byte note, byte velocity); 263 | void (*mAfterTouchPolyCallback)(byte channel, byte note, byte velocity); 264 | void (*mControlChangeCallback)(byte channel, byte, byte); 265 | void (*mProgramChangeCallback)(byte channel, byte); 266 | void (*mAfterTouchChannelCallback)(byte channel, byte); 267 | void (*mPitchBendCallback)(byte channel, int); 268 | void (*mSystemExclusiveCallback)(byte * array, byte size); 269 | void (*mTimeCodeQuarterFrameCallback)(byte data); 270 | void (*mSongPositionCallback)(unsigned int beats); 271 | void (*mSongSelectCallback)(byte songnumber); 272 | void (*mTuneRequestCallback)(void); 273 | void (*mClockCallback)(void); 274 | void (*mStartCallback)(void); 275 | void (*mContinueCallback)(void); 276 | void (*mStopCallback)(void); 277 | void (*mActiveSensingCallback)(void); 278 | void (*mSystemResetCallback)(void); 279 | 280 | #endif // USE_CALLBACKS 281 | 282 | 283 | #endif // COMPILE_MIDI_IN 284 | 285 | 286 | /* ####### THRU COMPILATION BLOCK ####### */ 287 | #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru 288 | 289 | public: 290 | 291 | // Getters 292 | kThruFilterMode getFilterMode() const { return mThruFilterMode; } 293 | bool getThruState() const { return mThruActivated; } 294 | 295 | 296 | // Setters 297 | void turnThruOn(kThruFilterMode inThruFilterMode = Full); 298 | void turnThruOff(); 299 | 300 | void setThruFilterMode(const kThruFilterMode inThruFilterMode); 301 | 302 | 303 | private: 304 | 305 | void thru_filter(byte inChannel); 306 | 307 | bool mThruActivated; 308 | kThruFilterMode mThruFilterMode; 309 | 310 | #endif // Thru 311 | 312 | }; 313 | 314 | extern MIDI_Class MIDI; 315 | 316 | #endif // LIB_MIDI_H_ 317 | -------------------------------------------------------------------------------- /dualMocoLUFA/dualMoco.c: -------------------------------------------------------------------------------- 1 | /* 2 | dualMocoLUFA Project 3 | Copyright (C) 2013 by morecat_lab 4 | 5 | 2013/09/22 6 | 7 | http://morecatlab.akiba.coocan.jp/ 8 | 9 | based on LUFA-100807 10 | */ 11 | /* 12 | LUFA Library 13 | Copyright (C) Dean Camera, 2010. 14 | 15 | dean [at] fourwalledcubicle [dot] com 16 | www.fourwalledcubicle.com 17 | */ 18 | /* 19 | Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) 20 | 21 | Permission to use, copy, modify, distribute, and sell this 22 | software and its documentation for any purpose is hereby granted 23 | without fee, provided that the above copyright notice appear in 24 | all copies and that both that the copyright notice and this 25 | permission notice and warranty disclaimer appear in supporting 26 | documentation, and that the name of the author not be used in 27 | advertising or publicity pertaining to distribution of the 28 | software without specific, written prior permission. 29 | 30 | The author disclaim all warranties with regard to this 31 | software, including all implied warranties of merchantability 32 | and fitness. In no event shall the author be liable for any 33 | special, indirect or consequential damages or any damages 34 | whatsoever resulting from loss of use, data or profits, whether 35 | in an action of contract, negligence or other tortious action, 36 | arising out of or in connection with the use or performance of 37 | this software. 38 | */ 39 | 40 | /** \file 41 | * 42 | * Main source file for the dualMocoLUFA project. 43 | * This file contains the main tasks of the project and is responsible for the initial application hardware configuration. 44 | */ 45 | 46 | #include "dualMoco.h" 47 | 48 | uchar mocoMode = 1; /* 0: Serial , 1: MIDI */ 49 | 50 | /** Circular buffer to hold data from the host before it is sent to the device via the serial port. */ 51 | RingBuff_t USBtoUSART_Buffer; 52 | 53 | /** Circular buffer to hold data from the serial port before it is sent to the host. */ 54 | RingBuff_t USARTtoUSB_Buffer; 55 | 56 | /** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */ 57 | volatile struct 58 | { 59 | uint8_t TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */ 60 | uint8_t RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */ 61 | uint8_t PingPongLEDPulse; /**< Milliseconds remaining for enumeration Tx/Rx ping-pong LED pulse */ 62 | } PulseMSRemaining; 63 | 64 | #define RX_SIZE (HW_CDC_BULK_IN_SIZE) 65 | static uchar utxrdy = FALSE; /* USB Packet ready in utx_buf */ 66 | static uchar rx_buf[RX_SIZE]; /* tempory buffer */ 67 | static uchar utx_buf[RX_SIZE]; /* BULK_IN buffer */ 68 | 69 | #define TX_SIZE (HW_CDC_BULK_OUT_SIZE<<2) 70 | #define TX_MASK (TX_SIZE-1) 71 | static uchar uwptr = 0, irptr = 0; 72 | static uchar tx_buf[TX_SIZE]; 73 | 74 | /** LUFA CDC Class driver interface configuration and state information. This structure is 75 | * passed to all CDC Class driver functions, so that multiple instances of the same class 76 | * within a device can be differentiated from one another. 77 | */ 78 | /* for serial */ 79 | USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface = 80 | { 81 | .Config = 82 | { 83 | .ControlInterfaceNumber = 0, 84 | 85 | .DataINEndpointNumber = CDC_TX_EPNUM, 86 | .DataINEndpointSize = CDC_TXRX_EPSIZE, 87 | .DataINEndpointDoubleBank = false, 88 | 89 | .DataOUTEndpointNumber = CDC_RX_EPNUM, 90 | .DataOUTEndpointSize = CDC_TXRX_EPSIZE, 91 | .DataOUTEndpointDoubleBank = false, 92 | 93 | .NotificationEndpointNumber = CDC_NOTIFICATION_EPNUM, 94 | .NotificationEndpointSize = CDC_NOTIFICATION_EPSIZE, 95 | .NotificationEndpointDoubleBank = false, 96 | }, 97 | }; 98 | /* for MIDI */ 99 | USB_ClassInfo_MIDI_Device_t Keyboard_MIDI_Interface = 100 | { 101 | .Config = 102 | { 103 | .StreamingInterfaceNumber = 1, 104 | 105 | .DataINEndpointNumber = MIDI_STREAM_IN_EPNUM, 106 | .DataINEndpointSize = MIDI_STREAM_EPSIZE, 107 | .DataINEndpointDoubleBank = false, 108 | 109 | .DataOUTEndpointNumber = MIDI_STREAM_OUT_EPNUM, 110 | .DataOUTEndpointSize = MIDI_STREAM_EPSIZE, 111 | .DataOUTEndpointDoubleBank = false, 112 | }, 113 | }; 114 | 115 | void parseUSBMidiMessage(uchar *data, uchar len) { 116 | uchar cin = (*data) & 0x0f; /* CABLE NOを無視する */ 117 | uchar i; 118 | 119 | if (cin > 1) { /* ignore cin == 0 and cin == 1 */ 120 | for (i = 1 ; i < 4 ; i++) { 121 | tx_buf[uwptr++] = *(data + i); /* copy to buffer */ 122 | uwptr &= TX_MASK; 123 | if (i == 1) { 124 | if ((cin == 5) || /* single byte system common */ 125 | (cin == 15)) /* single byte */ 126 | break; 127 | } 128 | if (i == 2) { 129 | if ((cin == 2) || /* two-byte system common */ 130 | (cin == 6) || /* system ex end with 2 bytes */ 131 | (cin == 12) || /* program change */ 132 | (cin == 13)) /* channel pressure */ 133 | break; 134 | } 135 | } 136 | } 137 | 138 | if (len > 4) { 139 | parseUSBMidiMessage(data+4, len-4); 140 | } 141 | } 142 | 143 | uchar parseSerialMidiMessage(uchar RxByte) { 144 | static uchar PC = 0; 145 | static uchar SysEx = FALSE; 146 | static uchar stateTransTable[] = { 147 | 0, /* 0 dummy */ 148 | 0, /* 1 dummy */ 149 | 3, /* 2->3 NOTE OFF (3) */ 150 | 2 | 0x80, /* 3->2 */ 151 | 5, /* 4->5 NOTE ON (3) */ 152 | 4 | 0x80, /* 5->4 */ 153 | 7, /* 6->7 Polyphonic key pressure (3) */ 154 | 6 | 0x80, /* 7->6 */ 155 | 9, /* 8->9 Control Change (3) */ 156 | 8 | 0x80, /* 8->9 */ 157 | 10 | 0x80, /* 10->10 program change (2) */ 158 | 0, /* dummy */ 159 | 12 | 0x80, /* 12->12 Channel Pressure (2) */ 160 | 0, /* 13 dummy */ 161 | 15, /* 14->15 Pitch Bend (3) */ 162 | 14 | 0x80 /* 15->14 */ 163 | }; 164 | 165 | if(SysEx){ /* MIDI System Message */ 166 | if(RxByte == 0xf7){ /* MIDI_EndSysEx */ 167 | SysEx = FALSE; 168 | } 169 | return FALSE; 170 | } 171 | if (RxByte >= 0xF8){ /* Single Byte Message */ 172 | utx_buf[0] = 0x0f; 173 | utx_buf[1] = RxByte; 174 | utx_buf[2] = 0; 175 | utx_buf[3] = 0; 176 | return TRUE; 177 | } 178 | 179 | if(RxByte > 0x7F){ /* Channel message */ 180 | if(RxByte == 0xf0){ /* MIDI_StartSysEx */ 181 | SysEx = TRUE; 182 | return FALSE; 183 | } 184 | PC = 0; 185 | } 186 | 187 | if (PC == 0) { 188 | PC = (((RxByte >> 4) & 0x07) + 1) * 2; 189 | // conversion 190 | // 0x80 -> 2, 0x90 -> 4, 0xa0 -> 6, 0xb0 -> 8, 0xc0 -> 10, 0xd0 -> 12, 0xe0 -> 14 191 | rx_buf[0] = RxByte >> 4; 192 | rx_buf[1] = RxByte; 193 | rx_buf[3] = 0; 194 | } else { 195 | uchar tt = stateTransTable[PC]; 196 | rx_buf[(PC & 1) + 2] = RxByte; 197 | PC = tt & 0x0f; 198 | if ((tt & 0x80) != 0) { 199 | memcpy(utx_buf, rx_buf, 4); 200 | return TRUE; 201 | } 202 | } 203 | return FALSE; 204 | } 205 | 206 | /** Main program entry point. This routine contains the overall program flow, including initial 207 | * setup of all components and the main program loop. 208 | */ 209 | int main(void) 210 | { 211 | SetupHardware(); 212 | 213 | if (mocoMode == 1) { 214 | processMIDI(); 215 | } else { 216 | processSerial(); 217 | } 218 | } 219 | 220 | void processMIDI() 221 | { 222 | sei(); 223 | 224 | for (;;){ 225 | /* receive from Serial MIDI line */ 226 | if( UCSR1A & (1< BUFFER_NEARLY_FULL)) 294 | { 295 | TIFR0 |= (1 << TOV0); 296 | 297 | if (USARTtoUSB_Buffer.Count) { 298 | LEDs_TurnOnLEDs(LEDMASK_TX); 299 | PulseMSRemaining.TxLEDPulse = TX_RX_LED_PULSE_MS; 300 | } 301 | 302 | /* Read bytes from the USART receive buffer into the USB IN endpoint */ 303 | while (BufferCount--) 304 | CDC_Device_SendByte(&VirtualSerial_CDC_Interface, RingBuffer_Remove(&USARTtoUSB_Buffer)); 305 | 306 | /* Turn off TX LED(s) once the TX pulse period has elapsed */ 307 | if (PulseMSRemaining.TxLEDPulse && !(--PulseMSRemaining.TxLEDPulse)) 308 | LEDs_TurnOffLEDs(LEDMASK_TX); 309 | 310 | /* Turn off RX LED(s) once the RX pulse period has elapsed */ 311 | if (PulseMSRemaining.RxLEDPulse && !(--PulseMSRemaining.RxLEDPulse)) 312 | LEDs_TurnOffLEDs(LEDMASK_RX); 313 | } 314 | 315 | /* Load the next byte from the USART transmit buffer into the USART */ 316 | if (!(RingBuffer_IsEmpty(&USBtoUSART_Buffer))) { 317 | Serial_TxByte(RingBuffer_Remove(&USBtoUSART_Buffer)); 318 | 319 | LEDs_TurnOnLEDs(LEDMASK_RX); 320 | PulseMSRemaining.RxLEDPulse = TX_RX_LED_PULSE_MS; 321 | } 322 | 323 | CDC_Device_USBTask(&VirtualSerial_CDC_Interface); 324 | USB_USBTask(); 325 | } 326 | } 327 | 328 | 329 | /** Configures the board hardware and chip peripherals for the demo's functionality. */ 330 | void SetupHardware(void) 331 | { 332 | /* Disable watchdog if enabled by bootloader/fuses */ 333 | MCUSR &= ~(1 << WDRF); 334 | wdt_disable(); 335 | 336 | DDRB = 0x00; 337 | PORTB = 0x04; /* PULL-UP PB2 */ 338 | 339 | if ((PINB & 0x04) == 0) { /* JUMPER BTW PB2-GND */ 340 | mocoMode = 0; 341 | } 342 | 343 | /* Hardware Initialization */ 344 | if (mocoMode == 1) { 345 | /* set baud rate */ 346 | UBRR1L = 31; /* 312500Hz at 16MHz clock */ 347 | /* */ 348 | UCSR1B = (1<State.LineEncoding.ParityType) 414 | { 415 | case CDC_PARITY_Odd: 416 | ConfigMask = ((1 << UPM11) | (1 << UPM10)); 417 | break; 418 | case CDC_PARITY_Even: 419 | ConfigMask = (1 << UPM11); 420 | break; 421 | } 422 | 423 | if (CDCInterfaceInfo->State.LineEncoding.CharFormat == CDC_LINEENCODING_TwoStopBits) 424 | ConfigMask |= (1 << USBS1); 425 | 426 | switch (CDCInterfaceInfo->State.LineEncoding.DataBits) 427 | { 428 | case 6: 429 | ConfigMask |= (1 << UCSZ10); 430 | break; 431 | case 7: 432 | ConfigMask |= (1 << UCSZ11); 433 | break; 434 | case 8: 435 | ConfigMask |= ((1 << UCSZ11) | (1 << UCSZ10)); 436 | break; 437 | } 438 | 439 | /* Must turn off USART before reconfiguring it, otherwise incorrect operation may occur */ 440 | UCSR1B = 0; 441 | UCSR1A = 0; 442 | UCSR1C = 0; 443 | 444 | /* Special case 57600 baud for compatibility with the ATmega328 bootloader. */ 445 | UBRR1 = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600) 446 | ? SERIAL_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS) 447 | : SERIAL_2X_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS); 448 | 449 | UCSR1C = ConfigMask; 450 | UCSR1A = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600) ? 0 : (1 << U2X1); 451 | UCSR1B = ((1 << RXCIE1) | (1 << TXEN1) | (1 << RXEN1)); 452 | } 453 | 454 | /** ISR to manage the reception of data from the serial port, placing received bytes into a circular buffer 455 | * for later transmission to the host. 456 | */ 457 | ISR(USART1_RX_vect, ISR_BLOCK) 458 | { 459 | if (mocoMode == 0) { 460 | uint8_t ReceivedByte = UDR1; 461 | 462 | if (USB_DeviceState == DEVICE_STATE_Configured) 463 | RingBuffer_Insert(&USARTtoUSB_Buffer, ReceivedByte); 464 | } 465 | } 466 | 467 | /** Event handler for the CDC Class driver Host-to-Device Line Encoding Changed event. 468 | * 469 | * \param[in] CDCInterfaceInfo Pointer to the CDC class interface configuration structure being referenced 470 | */ 471 | void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo) 472 | { 473 | bool CurrentDTRState = (CDCInterfaceInfo->State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR); 474 | 475 | if (CurrentDTRState) 476 | AVR_RESET_LINE_PORT &= ~AVR_RESET_LINE_MASK; 477 | else 478 | AVR_RESET_LINE_PORT |= AVR_RESET_LINE_MASK; 479 | } 480 | -------------------------------------------------------------------------------- /dualMocoLUFA/HEX/dualMoco.hex: -------------------------------------------------------------------------------- 1 | :10000000FFC0000019C1000017C1000015C10000A9 2 | :1000100013C1000011C100000FC100000DC100009C 3 | :100020000BC1000009C1000007C1000085C6000027 4 | :1000300049C6000001C10000FFC00000FDC0000073 5 | :10004000FBC00000F9C00000F7C00000F5C00000D0 6 | :10005000F3C00000F1C00000EFC00000A9C1000023 7 | :10006000EBC00000E9C00000E7C00000E5C00000F0 8 | :10007000E3C00000E1C00000DFC00000DDC0000000 9 | :10008000DBC00000D9C00000D7C00000D5C0000010 10 | :10009000D3C00000D1C0000012011001020000080E 11 | :1000A000EB034B2001000102DC01120110010000F2 12 | :1000B0000008EB03482000000102000109023E0095 13 | :1000C000020100C0320904000001020201000524FF 14 | :1000D000000110042402060524060001070582031E 15 | :1000E0000800FF09040100020A00000007050402DD 16 | :1000F000400001070583024000010902650002017A 17 | :1001000000C03209040000000101000009240100C0 18 | :100110000109000101090401000201030000072494 19 | :10012000010001410006240201010006240202022E 20 | :100130000009240301030102010009240302040150 21 | :100140000101000905010240000000000525010130 22 | :100150000109058202400000000005250101030499 23 | :100160000309043203410072006400750069006EE7 24 | :10017000006F00200028007700770077002E0061D4 25 | :100180000072006400750069006E006F002E00634D 26 | :100190000063002900000024036B007500770061F4 27 | :1001A0000074006100790040006E00690066007410 28 | :1001B0000079002E0063006F006D000000180341FD 29 | :1001C0000072006400750069006E006F0020005529 30 | :1001D000006E006F00000026034D004900440049F6 31 | :1001E000002F004D004F0043004F00200066006FBD 32 | :1001F00000720020004C0055004600410000000045 33 | :1002000011241FBECFEFD2E0DEBFCDBF11E0A0E0D2 34 | :10021000B1E0EAE3F5E102C005900D92A033B10729 35 | :10022000D9F712E0A0E3B1E001C01D92AE37B107EB 36 | :10023000E1F7B9D20C949B0AE3CE9C01DC01AE57E6 37 | :10024000BF4FED91FC91119741911196FC93EE9364 38 | :1002500080589F4FE817F90711F42D933C939FB7EF 39 | :10026000F894F901EC57FF4F8081815080839FBF44 40 | :10027000842F0895CF93DF93EC0130913101988161 41 | :100280009F70923008F1DE0121E0E32FF0E0EB5B9C 42 | :10029000FE4F11968C91119780833F5F3F71213003 43 | :1002A00029F4953089F09F3059F40EC0223041F482 44 | :1002B000923051F0963041F09C3031F09D3021F079 45 | :1002C0002F5F1196243009F7653018F0249664509A 46 | :1002D000D6CF30933101DF91CF91089508950895DD 47 | :1002E000DF92EF92FF920F931F93FC01848981307C 48 | :1002F00019F0823021F405C040E3D42E04C0DD247F 49 | :1003000002C030E2D32E8389823011F488E0D82AEB 50 | :100310008589873031F0883031F0863031F482E0E1 51 | :1003200003C084E001C086E0D82A1092C900109270 52 | :10033000C8001092CA00E784F0880189128980E021 53 | :10034000E81681EEF80680E0080780E0180719F447 54 | :1003500020E130E010C0C801B701969587957795E8 55 | :10036000679560587B47814E9F4FA80197010E9477 56 | :10037000790A215030403093CD002093CC00D092A8 57 | :10038000CA0080E0E81681EEF80680E0080780E009 58 | :10039000180711F082E001C080E08093C80088E96E 59 | :1003A0008093C9001F910F91FF90EF90DF90089507 60 | :1003B0001F920F920FB60F9211242F938F939F933A 61 | :1003C000EF93FF9380910001882319F59091CE005F 62 | :1003D0008EB38430F1F4E091E801F091E90190836B 63 | :1003E000E091E801F091E901CF0101969093E901D4 64 | :1003F0008093E801885E914021F488E691E0928341 65 | :1004000081839FB7F8948091EC018F5F8093EC011A 66 | :100410009FBFFF91EF919F918F912F910F900FBEF2 67 | :100420000F901F901895FC01858580FF02C05F9892 68 | :1004300008955F9A089580910001882319F481E05E 69 | :1004400091E063C7089580910001813021F487E134 70 | :1004500091E00C94450A81E091E00DC784B7877F55 71 | :1004600084BF88E10FB6F89480936000109260001A 72 | :100470000FBE14B884E085B91A9902C01092000129 73 | :1004800080910001813039F48FE18093CC0088E1C4 74 | :100490008093C90010C087E690E09093CD008093D0 75 | :1004A000CC0086E08093CA001092C80088E1809357 76 | :1004B000C900539A5A9AFBD38AB180638AB98BB127 77 | :1004C00080638BB984E085BD80910001882311F49D 78 | :1004D0005F9A579A08950F931F93CF93DF932FB787 79 | :1004E000F8948DEE91E090936E0280936D0290935C 80 | :1004F000700280936F022FBF2FB7F89488E691E0C7 81 | :100500009093E9018093E8019093EB018093EA01D5 82 | :100510002FBF7894CDEED1E003E08FB7F89490919F 83 | :1005200071028FBF903809F181E091E01AD697FDF2 84 | :100530001CC0E0916D02F0916E028083E0916D022B 85 | :10054000F0916E02CF01019690936E0280936D023E 86 | :100550008D56924011F4D283C1839FB7F894809155 87 | :1005600071028F5F809371029FBF8FB7F8941091D3 88 | :10057000EC018FBFA89902C0113678F1A89A80913A 89 | :10058000EC01882361F05D980093650108C088E65E 90 | :1005900091E053DE682F81E091E049D6115011239C 91 | :1005A000B1F780916501882351F0809165018150F8 92 | :1005B0008093650180916501882309F45D9A80919B 93 | :1005C0006601882351F08091660181508093660115 94 | :1005D00080916601882309F45C9A8FB7F894909112 95 | :1005E00071028FBF992369F08DEE91E026DE982F7E 96 | :1005F0008091C80085FFFCCF9093CE005C9800935B 97 | :10060000660181E091E004D699D587CF382F80919B 98 | :100610003301882331F0373F09F053C01092330182 99 | :100620002CC0383F50F08FE080933D0130933E0165 100 | :1006300010923F011092400142C037FF08C0303F86 101 | :1006400021F481E08093330118C010923401E091CD 102 | :100650003401EE23A1F4232F22952F70822F90E0F6 103 | :10066000877090700196880F991F809334012093B2 104 | :100670003501309336011092380180E00895F0E0A2 105 | :10068000DF01A05EBE4F9C91E170F070EB5CFE4F0D 106 | :100690003283892F8F708093340197FF12C080912D 107 | :1006A000350190913601A0913701B09138018093C6 108 | :1006B0003D0190933E01A0933F01B093400181E042 109 | :1006C000089580E00895EF92FF920F931F93DF93B8 110 | :1006D000CF9300D000D0CDB7DEB7789403E07E0191 111 | :1006E0000894E11CF11C8091C80087FF0BC0109199 112 | :1006F00030018091CE008ADF182B109330015D9875 113 | :100700000093650180913001882389F087E191E0B1 114 | :100710006DE371E097D687E191E080D610923001C9 115 | :1007200006C0C70164E0A6DD5C980093660187E11E 116 | :1007300091E0B70155D68823A1F78091C80085FFC5 117 | :1007400011C09091320180913101891759F0E92F40 118 | :10075000F0E0EB5BFE4F80818093CE009F5F9F7146 119 | :1007600090933201A89B1DC0A89A809165018823AF 120 | :1007700051F0809165018150809365018091650100 121 | :10078000882309F45D9A80916601882351F0809155 122 | :10079000660181508093660180916601882309F487 123 | :1007A0005C9ACCD4A0CF5ADE80910001813011F444 124 | :1007B0008ADF01C090DE80E090E00895DA01923097 125 | :1007C00089F09330E9F0913009F03FC08091000149 126 | :1007D000813019F0E8E9F0E002C0EAEAF0E022E155 127 | :1007E00030E037C080910001813029F0ECEBF0E07F 128 | :1007F0002EE330E02EC0EAEFF0E025E630E029C03D 129 | :10080000813049F0813018F08230F9F410C0EFE502 130 | :10081000F1E0849113C080910001813021F4E7E977 131 | :10082000F1E084910BC0E3E6F1E0849107C0809190 132 | :100830000001813031F4E7EDF1E08491282F30E0C0 133 | :1008400008C0EDEBF1E08491F9CFE0E0F0E020E0CA 134 | :1008500030E0ED93FC93C901089528E030E040E0DA 135 | :1008600003C04F5F220F331F28173907D0F3842F9F 136 | :100870008295807F08958093E9008091EB008160EC 137 | :100880008093EB001092ED006093EC004093ED003C 138 | :100890008091EE00881F8827881F08951092F40029 139 | :1008A00090E09093E9001092F0001092E80010920E 140 | :1008B000ED008091EB008E7F8093EB009F5F953081 141 | :1008C00081F708958091760288238CF403C08EB35B 142 | :1008D0008823B1F08091E80082FFF9CF8091E80091 143 | :1008E0008B778093E80008958EB3882349F0809138 144 | :1008F000E80080FFF9CF8091E8008E778093E800D0 145 | :10090000089594E68091EC0080FF05C08091E80096 146 | :1009100080FF05C023C08091E80082FD1FC08EB318 147 | :10092000882311F482E008958EB3853011F483E0BA 148 | :1009300008958091EB0085FF02C081E008958091C9 149 | :10094000E10082FFDFCF8091E1008B7F8093E100A7 150 | :10095000992311F484E008959150D4CF80E0089554 151 | :10096000DF92EF92FF920F931F93CF93DF93182F95 152 | :10097000092FEB017A01C5DFD82E882319F5812FC5 153 | :10098000902F9C0189011CC08091E80085FD12C058 154 | :100990008091E8008E778093E800E114F10431F053 155 | :1009A000F7010995813011F485E00DC0AADF882395 156 | :1009B00039F009C0F80181918F018093F1002197EE 157 | :1009C000209711F78D2DDF91CF911F910F91FF90FF 158 | :1009D000EF90DF900895DF92EF92FF920F931F93B5 159 | :1009E000CF93DF93182F092FEB017A018ADFD82EDE 160 | :1009F000882319F5812F902F9C0189011CC08091BB 161 | :100A0000E80085FD12C08091E8008B778093E800B4 162 | :100A1000E114F10431F0F7010995813011F485E01A 163 | :100A20000DC06FDF882339F009C08091F100F80113 164 | :100A300081938F012197209711F78D2DDF91CF9111 165 | :100A40001F910F91FF90EF90DF9008959C014091CE 166 | :100A50007C0250917D024617570718F4F90120E0F7 167 | :100A600038C06115710511F0AB01F8CF8091E80035 168 | :100A70008E778093E80040E050E0F0CF8091E8006E 169 | :100A800083FF02C081E008958091E80082FD2DC0BF 170 | :100A90008EB3882381F18EB3853079F18091E8009F 171 | :100AA00080FF17C09091F20006C081918093F10001 172 | :100AB000415050409F5F4115510511F09830A8F307 173 | :100AC00020E0983009F421E08091E8008E7780934F 174 | :100AD000E8004115510591F6222381F606C08EB338 175 | :100AE000882349F08EB3853041F08091E80082FF81 176 | :100AF000F6CF80E0089582E0089583E008959C0198 177 | :100B000040917C0250917D024617570710F490E007 178 | :100B10003BC06115710511F0AB01F9CF8091E80080 179 | :100B20008E778093E80040E050E0F1CF8091E800BC 180 | :100B300083FF02C081E008958091E80082FD30C00B 181 | :100B40008EB3882399F18EB3853091F18091E800BE 182 | :100B500080FF1AC08091F20009C0F9012F5F3F4F5A 183 | :100B6000E491E093F100415050408F5F41155105F1 184 | :100B700011F0883090F390E0883009F491E0809192 185 | :100B8000E8008E778093E8004115510579F69923A6 186 | :100B900069F606C08EB3882349F08EB3853041F0E4 187 | :100BA0008091E80082FFF6CF80E0089582E008950A 188 | :100BB00083E008959C016115710529F48091E80096 189 | :100BC0008B778093E800F90120C08091E80083FFD3 190 | :100BD00002C081E008958EB3882339F18EB3853049 191 | :100BE00031F18091E80082FFF0CF06C08091F100E2 192 | :100BF00081936150704021F08091F2008823B1F719 193 | :100C00008091E8008B778093E80061157105E9F623 194 | :100C100006C08EB3882349F08EB3853041F08091B1 195 | :100C2000E80080FFF6CF80E0089582E0089583E039 196 | :100C3000089542D044D01EBA1092740210927302EA 197 | :100C40001092720284E089BD89B5826089BD09B4C1 198 | :100C500000FEFDCF8091D800982F9F779093D80009 199 | :100C600080688093D800809163008E7F80936300BA 200 | :100C70008091D8008F7D8093D8008091E0008E7F96 201 | :100C80008093E0008091E1008E7F8093E10080916D 202 | :100C9000E20081608093E2008091E100877F809391 203 | :100CA000E1008091E20088608093E2000895C1DF56 204 | :100CB00081E08093750208951092E20008951092E9 205 | :100CC000E10008951F920F920FB60F9211241F9307 206 | :100CD0002F933F934F935F936F937F938F939F9344 207 | :100CE000AF93BF93EF93FF93E9EEF0E0108117709D 208 | :100CF0001082E0EFF0E08081877F80837894C3D01A 209 | :100D0000F894A9EEB0E01C92E0EFF0E080818860FA 210 | :100D100080831C93FF91EF91BF91AF919F918F9131 211 | :100D20007F916F915F914F913F912F911F910F9004 212 | :100D30000FBE0F901F9018951F920F920FB60F9233 213 | :100D400011242F933F934F935F936F937F938F93D0 214 | :100D50009F93AF93BF93EF93FF938091E10080FF48 215 | :100D60001BC08091E20080FF17C08091E1008E7F60 216 | :100D70008093E1008091E2008E7F8093E200809179 217 | :100D8000E20080618093E2008091D80080628093CD 218 | :100D9000D80019BC1EBAA3DA8091E10084FF29C0F3 219 | :100DA0008091E20084FF25C084E089BD89B582601E 220 | :100DB00089BD09B400FEFDCF8091D8008F7D80935E 221 | :100DC000D8008091E1008F7E8093E1008091E20065 222 | :100DD0008F7E8093E2008091E20081608093E20048 223 | :100DE00080917402882311F481E001C084E08EBBFD 224 | :100DF00075DA8091E10083FF27C08091E20083FFD4 225 | :100E000023C08091E100877F8093E10082E08EBB68 226 | :100E1000109274028091E1008E7F8093E1008091B6 227 | :100E2000E2008E7F8093E2008091E20080618093F7 228 | :100E3000E20034DD80E060E042E01DDD8091F00002 229 | :100E400088608093F00079D18091E10082FF0AC030 230 | :100E50008091E20082FF06C08091E1008B7F809349 231 | :100E6000E1006BD1FF91EF91BF91AF919F918F9175 232 | :100E70007F916F915F914F913F912F910F900FBE96 233 | :100E80000F901F9018951F93DF93CF93CDB7DEB7C8 234 | :100E9000AC970FB6F894DEBF0FBECDBFE6E7F2E029 235 | :100EA0008091F100819322E0EE37F207C9F780913B 236 | :100EB000760230917702353009F487C0363040F43D 237 | :100EC0003130C9F1313070F0333009F01DC133C019 238 | :100ED000383009F4EFC0393009F4FEC0363009F07B 239 | :100EE00013C192C0803821F0823809F00DC108C0CA 240 | :100EF0009091720280917302882399F0926011C0E0 241 | :100F000080917A0287708093E9008091EB0090E0F5 242 | :100F100025E0969587952A95E1F7982F9170109284 243 | :100F2000E9008091E800877F8093E8009093F100CA 244 | :100F30001092F100CAC0882319F0823009F0E4C091 245 | :100F400090E08F719070009721F0029709F0DDC05A 246 | :100F50000CC080917802813009F0D7C010927302E2 247 | :100F6000333069F5809373022AC080917802882318 248 | :100F700031F520917A02277009F4C7C02093E90067 249 | :100F80008091EB0080FFC1C0333021F48091EB00F1 250 | :100F9000806213C08091EB0080618093EB0081E060 251 | :100FA00090E002C0880F991F2A95E2F78093EA002B 252 | :100FB0001092EA008091EB0088608093EB00109221 253 | :100FC000E9008091E800877F83C0882309F09CC0F6 254 | :100FD000109178028091E800877F8093E80072DCAE 255 | :100FE00004C08EB3882309F490C08091E80080FF8C 256 | :100FF000F8CF812F8F7711F492E001C093E09EBB70 257 | :1010000080688093E30081C08058823008F07CC003 258 | :10101000809178029091790223E08C3D920799F5B6 259 | :101020005FB7F894DE0115964EE020E030E061E213 260 | :10103000E42FF0E060935700849120FF03C0829575 261 | :101040008F704F5F982F9F70892F805D8A3308F0D3 262 | :10105000895F8C9311961C9211972F5F3F4F1296C8 263 | :101060002431310529F75FBF8AE28B8383E08C83CB 264 | :101070008091E800877F8093E800CE0103966AE2C2 265 | :1010800070E0E4DC11C060917A02AE014F5F5F4F07 266 | :1010900095DBBC010097C9F18091E800877F8093C0 267 | :1010A000E80089819A812BDD8091E8008B7780931D 268 | :1010B000E8002BC0803841F58091E800877F80935D 269 | :1010C000E800809174028093F1008091E8008E77AF 270 | :1010D0008093E800F7DB19C08823B1F4909178027F 271 | :1010E000923098F48091E800877F8093E800909395 272 | :1010F0007402E8DB80917402882311F483E001C05C 273 | :1011000084E08EBBA0D901C096D98091E80083FF0E 274 | :101110000AC08091EB0080628093EB008091E80030 275 | :10112000877F8093E800AC960FB6F894DEBF0FBEC1 276 | :10113000CDBFCF91DF911F91089508951F938EB376 277 | :10114000882361F01091E9001092E9008091E80095 278 | :1011500083FF01C098DE17701093E9001F91089576 279 | :101160000895FC018EB3843021F587859089A1898B 280 | :10117000B2890097A105B105E1F085818093E9006E 281 | :101180008091E80082FF15C08091F200882319F455 282 | :101190002FEF3FEF04C08091F100282F30E08091C5 283 | :1011A000F200882341F48091E8008B778093E80077 284 | :1011B00002C02FEF3FEFC9010895FC018EB38430C8 285 | :1011C00011F587859089A189B2890097A105B1059C 286 | :1011D000D1F081818093E9008091F2008823A9F009 287 | :1011E0009091E8008091E8008E778093E80095FD6B 288 | :1011F0000CC087DB982F882349F48091E8008E7714 289 | :101200008093E80003C092E001C090E0892F089528 290 | :10121000FC018EB3843051F487859089A189B2890D 291 | :101220000097A105B10511F0CF01C7CF08951F9315 292 | :10123000FC01162F8EB38430D9F487859089A1895B 293 | :10124000B2890097A105B10599F081818093E900E9 294 | :101250008091E80085FD08C08091E8008E7780933A 295 | :10126000E8004FDB882329F41093F10080E001C0EF 296 | :1012700082E01F9108950F931F93CF93DF93EC01AA 297 | :101280000D96FC0189E0DF011D928A95E9F72A811C 298 | :101290003B8109818C81882311F410E001C014E0A6 299 | :1012A000C901DBDA182B1260802F61E8412FE3DAE5 300 | :1012B000882329F12E813F810D818885882311F4AF 301 | :1012C00010E001C014E0C901C8DA182B1260802FA9 302 | :1012D00060E8412FD0DA882391F02A853B85098583 303 | :1012E0008C85882311F410E001C014E0C901B5DA3F 304 | :1012F000182B1260802F61EC412FBDDA01C080E015 305 | :10130000DF91CF911F910F910895CF93DF93EC015F 306 | :101310008091E80083FF61C0888190E020917A028B 307 | :1013200030917B022817390709F057C08091770266 308 | :10133000813261F0823220F4803209F04EC019C04F 309 | :10134000823271F1833209F048C039C0809176024F 310 | :10135000813A09F042C08091E800877F8093E800DD 311 | :10136000CE010F9667E070E071DB8091E8008B772B 312 | :1013700014C080917602813281F58091E800877FE8 313 | :101380008093E800CE010F9667E070E013DCCE0199 314 | :101390000E9470018091E8008E778093E8001DC064 315 | :1013A000809176028132C9F48091E800877F809332 316 | :1013B000E800809178028D87CE0135D80DC08091EC 317 | :1013C0007602813251F48091E800877F8093E800B3 318 | :1013D000CE0160917802C4DE75DADF91CF91089575 319 | :1013E000FC018EB38430C1F485818093E900809143 320 | :1013F000E80085FF11C0CB0164E070E040E050E000 321 | :10140000EADA8091E80085FD05C08091E8008B77DD 322 | :101410008093E80081E0089580E00895FC018EB398 323 | :10142000843011F082E0089581818093E9008091F9 324 | :10143000F200882331F08091E8008E778093E800F5 325 | :1014400060CA0895FC018EB3843011F092E01BC095 326 | :1014500081818093E9008091E80085FD02C090E0E1 327 | :1014600012C0CB0164E070E040E050E079DA982FE0 328 | :10147000882349F48091E80085FD05C08091E8004B 329 | :101480008E778093E800892F08950F931F93CF9351 330 | :10149000DF93EC010981002391F02A813B818C814B 331 | :1014A000882311F410E001C014E0C901D6D9182B2B 332 | :1014B0001260802F61E8412FDED98823A9F00D81C9 333 | :1014C000002389F02E813F818885882311F410E064 334 | :1014D00001C014E0C901C1D9182B1260802F60E847 335 | :1014E000412FC9D9811181E0DF91CF911F910F91D7 336 | :1014F0000895A1E21A2EAA1BBB1BFD010DC0AA1F55 337 | :10150000BB1FEE1FFF1FA217B307E407F50720F06C 338 | :10151000A21BB30BE40BF50B661F771F881F991FE7 339 | :101520001A9469F760957095809590959B01AC0130 340 | :0A153000BD01CF010895F894FFCF2C 341 | :10153A00010003400000044000000208000000000F 342 | :10154A00000000000000000102400000014000000D 343 | :10155A00000003820584078609888A008C000F8EA2 344 | :00000001FF 345 | -------------------------------------------------------------------------------- /dualMocoLUFA/Descriptors.c: -------------------------------------------------------------------------------- 1 | /* 2 | dualMocoLUFA Project 3 | Copyright (C) 2013 by morecat_lab 4 | 5 | 2013/09/22 6 | 7 | http://morecatlab.akiba.coocan.jp/ 8 | 9 | based on LUFA-100807 10 | */ 11 | /* 12 | LUFA Library 13 | Copyright (C) Dean Camera, 2010. 14 | 15 | dean [at] fourwalledcubicle [dot] com 16 | www.fourwalledcubicle.com 17 | */ 18 | 19 | /* 20 | Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) 21 | 22 | Permission to use, copy, modify, distribute, and sell this 23 | software and its documentation for any purpose is hereby granted 24 | without fee, provided that the above copyright notice appear in 25 | all copies and that both that the copyright notice and this 26 | permission notice and warranty disclaimer appear in supporting 27 | documentation, and that the name of the author not be used in 28 | advertising or publicity pertaining to distribution of the 29 | software without specific, written prior permission. 30 | 31 | The author disclaim all warranties with regard to this 32 | software, including all implied warranties of merchantability 33 | and fitness. In no event shall the author be liable for any 34 | special, indirect or consequential damages or any damages 35 | whatsoever resulting from loss of use, data or profits, whether 36 | in an action of contract, negligence or other tortious action, 37 | arising out of or in connection with the use or performance of 38 | this software. 39 | */ 40 | 41 | /** \file 42 | * 43 | * USB Device Descriptors, for library use when in USB device mode. Descriptors are special 44 | * computer-readable structures which the host requests upon device enumeration, to determine 45 | * the device's capabilities and functions. 46 | */ 47 | 48 | #include "Descriptors.h" 49 | #include "dualMoco.h" 50 | 51 | /* On some devices, there is a factory set internal serial number which can be automatically sent to the host as 52 | * the device's serial number when the Device Descriptor's .SerialNumStrIndex entry is set to USE_INTERNAL_SERIAL. 53 | * This allows the host to track a device across insertions on different ports, allowing them to retain allocated 54 | * resources like COM port numbers and drivers. On demos using this feature, give a warning on unsupported devices 55 | * so that the user can supply their own serial number descriptor instead or remove the USE_INTERNAL_SERIAL value 56 | * from the Device Descriptor (forcing the host to generate a serial number for each device from the VID, PID and 57 | * port location). 58 | */ 59 | #if (USE_INTERNAL_SERIAL == NO_DESCRIPTOR) 60 | #warning USE_INTERNAL_SERIAL is not available on this AVR - please manually construct a device serial descriptor. 61 | #endif 62 | 63 | /** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall 64 | * device characteristics, including the supported USB version, control endpoint size and the 65 | * number of device configurations. The descriptor is read out by the USB host when the enumeration 66 | * process begins. 67 | */ 68 | /* for SERIAL */ 69 | USB_Descriptor_Device_t PROGMEM DeviceDescriptorSerial = 70 | { 71 | .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, 72 | 73 | .USBSpecification = VERSION_BCD(01.10), 74 | .Class = 0x02, 75 | .SubClass = 0x00, 76 | .Protocol = 0x00, 77 | 78 | .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, 79 | 80 | .VendorID = 0x03EB, // Atmel 81 | 82 | .ProductID = 0x204B, // LUFA USB to Serial Demo Application 83 | .ReleaseNumber = 0x0001, 84 | 85 | .ManufacturerStrIndex = 0x01, 86 | .ProductStrIndex = 0x02, 87 | .SerialNumStrIndex = USE_INTERNAL_SERIAL, 88 | 89 | .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS 90 | }; 91 | 92 | /* for MIDI */ 93 | USB_Descriptor_Device_t PROGMEM DeviceDescriptorMIDI = 94 | { 95 | .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device}, 96 | 97 | .USBSpecification = VERSION_BCD(01.10), 98 | .Class = 0x00, 99 | .SubClass = 0x00, 100 | .Protocol = 0x00, 101 | 102 | .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, 103 | 104 | .VendorID = 0x03EB, // Atmel 105 | .ProductID = 0x2048, // LUFA USB-MIDI Demo application 106 | .ReleaseNumber = 0x0000, 107 | 108 | .ManufacturerStrIndex = 0x01, 109 | .ProductStrIndex = 0x02, 110 | .SerialNumStrIndex = NO_DESCRIPTOR, 111 | 112 | .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS 113 | }; 114 | 115 | /** Configuration descriptor structure. This descriptor, located in FLASH memory, describes the usage 116 | * of the device in one of its supported configurations, including information about any device interfaces 117 | * and endpoints. The descriptor is read out by the USB host during the enumeration process when selecting 118 | * a configuration so that the host may correctly communicate with the USB device. 119 | */ 120 | /* for Serial */ 121 | USB_Descriptor_ConfigurationCDC_t PROGMEM ConfigurationDescriptorSerial = 122 | { 123 | .Config = 124 | { 125 | .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, 126 | 127 | .TotalConfigurationSize = sizeof(USB_Descriptor_ConfigurationCDC_t), 128 | .TotalInterfaces = 2, 129 | 130 | .ConfigurationNumber = 1, 131 | .ConfigurationStrIndex = NO_DESCRIPTOR, 132 | 133 | .ConfigAttributes = (USB_CONFIG_ATTR_BUSPOWERED | USB_CONFIG_ATTR_SELFPOWERED), 134 | 135 | .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) 136 | }, 137 | 138 | .CDC_CCI_Interface = 139 | { 140 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 141 | 142 | .InterfaceNumber = 0, 143 | .AlternateSetting = 0, 144 | 145 | .TotalEndpoints = 1, 146 | 147 | .Class = 0x02, 148 | .SubClass = 0x02, 149 | .Protocol = 0x01, 150 | 151 | .InterfaceStrIndex = NO_DESCRIPTOR 152 | }, 153 | 154 | .CDC_Functional_IntHeader = 155 | { 156 | .Header = {.Size = sizeof(CDC_FUNCTIONAL_DESCRIPTOR(2)), .Type = 0x24}, 157 | .SubType = 0x00, 158 | 159 | .Data = {0x01, 0x10} 160 | }, 161 | 162 | .CDC_Functional_AbstractControlManagement = 163 | { 164 | .Header = {.Size = sizeof(CDC_FUNCTIONAL_DESCRIPTOR(1)), .Type = 0x24}, 165 | .SubType = 0x02, 166 | 167 | .Data = {0x06} 168 | }, 169 | 170 | .CDC_Functional_Union = 171 | { 172 | .Header = {.Size = sizeof(CDC_FUNCTIONAL_DESCRIPTOR(2)), .Type = 0x24}, 173 | .SubType = 0x06, 174 | 175 | .Data = {0x00, 0x01} 176 | }, 177 | 178 | .CDC_NotificationEndpoint = 179 | { 180 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 181 | 182 | .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_IN | CDC_NOTIFICATION_EPNUM), 183 | .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 184 | .EndpointSize = CDC_NOTIFICATION_EPSIZE, 185 | .PollingIntervalMS = 0xFF 186 | }, 187 | 188 | .CDC_DCI_Interface = 189 | { 190 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 191 | 192 | .InterfaceNumber = 1, 193 | .AlternateSetting = 0, 194 | 195 | .TotalEndpoints = 2, 196 | 197 | .Class = 0x0A, 198 | .SubClass = 0x00, 199 | .Protocol = 0x00, 200 | 201 | .InterfaceStrIndex = NO_DESCRIPTOR 202 | }, 203 | 204 | .CDC_DataOutEndpoint = 205 | { 206 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 207 | 208 | .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_OUT | CDC_RX_EPNUM), 209 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 210 | .EndpointSize = CDC_TXRX_EPSIZE, 211 | .PollingIntervalMS = 0x01 212 | }, 213 | 214 | .CDC_DataInEndpoint = 215 | { 216 | .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, 217 | 218 | .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_IN | CDC_TX_EPNUM), 219 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 220 | .EndpointSize = CDC_TXRX_EPSIZE, 221 | .PollingIntervalMS = 0x01 222 | } 223 | }; 224 | 225 | /* for MIDI */ 226 | USB_Descriptor_ConfigurationMIDI_t PROGMEM ConfigurationDescriptorMIDI = 227 | { 228 | .Config = 229 | { 230 | .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, 231 | 232 | .TotalConfigurationSize = sizeof(USB_Descriptor_ConfigurationMIDI_t), 233 | .TotalInterfaces = 2, 234 | 235 | .ConfigurationNumber = 1, 236 | .ConfigurationStrIndex = NO_DESCRIPTOR, 237 | 238 | .ConfigAttributes = (USB_CONFIG_ATTR_BUSPOWERED | USB_CONFIG_ATTR_SELFPOWERED), 239 | 240 | .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) 241 | }, 242 | 243 | .Audio_ControlInterface = 244 | { 245 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 246 | 247 | .InterfaceNumber = 0, 248 | .AlternateSetting = 0, 249 | 250 | .TotalEndpoints = 0, 251 | 252 | .Class = 0x01, 253 | .SubClass = 0x01, 254 | .Protocol = 0x00, 255 | 256 | .InterfaceStrIndex = NO_DESCRIPTOR 257 | }, 258 | 259 | .Audio_ControlInterface_SPC = 260 | { 261 | .Header = {.Size = sizeof(USB_Audio_Interface_AC_t), .Type = DTYPE_AudioInterface}, 262 | .Subtype = DSUBTYPE_Header, 263 | 264 | .ACSpecification = VERSION_BCD(01.00), 265 | .TotalLength = sizeof(USB_Audio_Interface_AC_t), 266 | 267 | .InCollection = 1, 268 | .InterfaceNumbers = {1}, 269 | }, 270 | 271 | .Audio_StreamInterface = 272 | { 273 | .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, 274 | 275 | .InterfaceNumber = 1, 276 | .AlternateSetting = 0, 277 | 278 | .TotalEndpoints = 2, 279 | 280 | .Class = 0x01, 281 | .SubClass = 0x03, 282 | .Protocol = 0x00, 283 | 284 | .InterfaceStrIndex = NO_DESCRIPTOR 285 | }, 286 | 287 | .Audio_StreamInterface_SPC = 288 | { 289 | .Header = {.Size = sizeof(USB_MIDI_AudioInterface_AS_t), .Type = DTYPE_AudioInterface}, 290 | .Subtype = DSUBTYPE_General, 291 | 292 | .AudioSpecification = VERSION_BCD(01.00), 293 | 294 | .TotalLength = (sizeof(USB_Descriptor_ConfigurationMIDI_t) - 295 | offsetof(USB_Descriptor_ConfigurationMIDI_t, Audio_StreamInterface_SPC)) 296 | }, 297 | 298 | .MIDI_In_Jack_Emb = 299 | { 300 | .Header = {.Size = sizeof(USB_MIDI_In_Jack_t), .Type = DTYPE_AudioInterface}, 301 | .Subtype = DSUBTYPE_InputJack, 302 | 303 | .JackType = MIDI_JACKTYPE_EMBEDDED, 304 | .JackID = 0x01, 305 | 306 | .JackStrIndex = NO_DESCRIPTOR 307 | }, 308 | 309 | .MIDI_In_Jack_Ext = 310 | { 311 | .Header = {.Size = sizeof(USB_MIDI_In_Jack_t), .Type = DTYPE_AudioInterface}, 312 | .Subtype = DSUBTYPE_InputJack, 313 | 314 | .JackType = MIDI_JACKTYPE_EXTERNAL, 315 | .JackID = 0x02, 316 | 317 | .JackStrIndex = NO_DESCRIPTOR 318 | }, 319 | 320 | .MIDI_Out_Jack_Emb = 321 | { 322 | .Header = {.Size = sizeof(USB_MIDI_Out_Jack_t), .Type = DTYPE_AudioInterface}, 323 | .Subtype = DSUBTYPE_OutputJack, 324 | 325 | .JackType = MIDI_JACKTYPE_EMBEDDED, 326 | .JackID = 0x03, 327 | 328 | .NumberOfPins = 1, 329 | .SourceJackID = {0x02}, 330 | .SourcePinID = {0x01}, 331 | 332 | .JackStrIndex = NO_DESCRIPTOR 333 | }, 334 | 335 | .MIDI_Out_Jack_Ext = 336 | { 337 | .Header = {.Size = sizeof(USB_MIDI_Out_Jack_t), .Type = DTYPE_AudioInterface}, 338 | .Subtype = DSUBTYPE_OutputJack, 339 | 340 | .JackType = MIDI_JACKTYPE_EXTERNAL, 341 | .JackID = 0x04, 342 | 343 | .NumberOfPins = 1, 344 | .SourceJackID = {0x01}, 345 | .SourcePinID = {0x01}, 346 | 347 | .JackStrIndex = NO_DESCRIPTOR 348 | }, 349 | 350 | .MIDI_In_Jack_Endpoint = 351 | { 352 | .Endpoint = 353 | { 354 | .Header = {.Size = sizeof(USB_Audio_StreamEndpoint_Std_t), .Type = DTYPE_Endpoint}, 355 | 356 | .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_OUT | MIDI_STREAM_OUT_EPNUM), 357 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 358 | .EndpointSize = MIDI_STREAM_EPSIZE, 359 | .PollingIntervalMS = 0 360 | }, 361 | 362 | .Refresh = 0, 363 | .SyncEndpointNumber = 0 364 | }, 365 | 366 | .MIDI_In_Jack_Endpoint_SPC = 367 | { 368 | .Header = {.Size = sizeof(USB_MIDI_Jack_Endpoint_t), .Type = DTYPE_AudioEndpoint}, 369 | .Subtype = DSUBTYPE_General, 370 | 371 | .TotalEmbeddedJacks = 0x01, 372 | .AssociatedJackID = {0x01} 373 | }, 374 | 375 | .MIDI_Out_Jack_Endpoint = 376 | { 377 | .Endpoint = 378 | { 379 | .Header = {.Size = sizeof(USB_Audio_StreamEndpoint_Std_t), .Type = DTYPE_Endpoint}, 380 | 381 | .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_IN | MIDI_STREAM_IN_EPNUM), 382 | .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), 383 | .EndpointSize = MIDI_STREAM_EPSIZE, 384 | .PollingIntervalMS = 0 385 | }, 386 | 387 | .Refresh = 0, 388 | .SyncEndpointNumber = 0 389 | }, 390 | 391 | .MIDI_Out_Jack_Endpoint_SPC = 392 | { 393 | .Header = {.Size = sizeof(USB_MIDI_Jack_Endpoint_t), .Type = DTYPE_AudioEndpoint}, 394 | .Subtype = DSUBTYPE_General, 395 | 396 | .TotalEmbeddedJacks = 0x01, 397 | .AssociatedJackID = {0x03} 398 | } 399 | }; 400 | 401 | /** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests 402 | * the string descriptor with index 0 (the first index). It is actually an array of 16-bit integers, which indicate 403 | * via the language ID table available at USB.org what languages the device supports for its string descriptors. 404 | */ 405 | USB_Descriptor_String_t PROGMEM LanguageString = 406 | { 407 | .Header = {.Size = USB_STRING_LEN(1), .Type = DTYPE_String}, 408 | 409 | .UnicodeString = {LANGUAGE_ID_ENG} 410 | }; 411 | 412 | /** Manufacturer descriptor string. This is a Unicode string containing the manufacturer's details in human readable 413 | * form, and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 414 | * Descriptor. 415 | */ 416 | /* for Serial */ 417 | USB_Descriptor_String_t PROGMEM ManufacturerStringSerial = 418 | { 419 | .Header = {.Size = USB_STRING_LEN(24), .Type = DTYPE_String}, 420 | 421 | .UnicodeString = L"Arduino (www.arduino.cc)" 422 | }; 423 | 424 | /* for MIDI */ 425 | USB_Descriptor_String_t PROGMEM ManufacturerStringMIDI = 426 | { 427 | .Header = {.Size = USB_STRING_LEN(17), .Type = DTYPE_String}, 428 | 429 | .UnicodeString = L"kuwatay@nifty.com" 430 | }; 431 | /** Product descriptor string. This is a Unicode string containing the product's details in human readable form, 432 | * and is read out upon request by the host when the appropriate string ID is requested, listed in the Device 433 | * Descriptor. 434 | */ 435 | /* for Serial */ 436 | USB_Descriptor_String_t PROGMEM ProductStringSerial = 437 | { 438 | #if (ARDUINO_MODEL_PID == ARDUINO_UNO_PID) 439 | .Header = {.Size = USB_STRING_LEN(11), .Type = DTYPE_String}, 440 | 441 | .UnicodeString = L"Arduino Uno" 442 | #elif (ARDUINO_MODEL_PID == ARDUINO_MEGA2560_PID) 443 | .Header = {.Size = USB_STRING_LEN(17), .Type = DTYPE_String}, 444 | 445 | .UnicodeString = L"Arduino Mega 2560" 446 | #endif 447 | 448 | }; 449 | /* for MIDI */ 450 | USB_Descriptor_String_t PROGMEM ProductStringMIDI = 451 | { 452 | .Header = {.Size = USB_STRING_LEN(18), .Type = DTYPE_String}, 453 | 454 | .UnicodeString = L"MIDI/MOCO for LUFA" 455 | }; 456 | 457 | /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors" 458 | * documentation) by the application code so that the address and size of a requested descriptor can be given 459 | * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function 460 | * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the 461 | * USB host. 462 | */ 463 | uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, 464 | const uint8_t wIndex, 465 | void** const DescriptorAddress) 466 | { 467 | const uint8_t DescriptorType = (wValue >> 8); 468 | const uint8_t DescriptorNumber = (wValue & 0xFF); 469 | 470 | void* Address = NULL; 471 | uint16_t Size = NO_DESCRIPTOR; 472 | 473 | switch (DescriptorType) 474 | { 475 | case DTYPE_Device: 476 | if (mocoMode == 1) { 477 | Address = (void*)&DeviceDescriptorMIDI; 478 | Size = sizeof(USB_Descriptor_Device_t); 479 | } else { 480 | Address = (void*)&DeviceDescriptorSerial; 481 | Size = sizeof(USB_Descriptor_Device_t); 482 | } 483 | break; 484 | case DTYPE_Configuration: 485 | if (mocoMode == 1) { 486 | Address = (void*)&ConfigurationDescriptorMIDI; 487 | Size = sizeof(USB_Descriptor_ConfigurationMIDI_t); 488 | } else { 489 | Address = (void*)&ConfigurationDescriptorSerial; 490 | Size = sizeof(USB_Descriptor_ConfigurationCDC_t); 491 | } 492 | break; 493 | case DTYPE_String: 494 | switch (DescriptorNumber) 495 | { 496 | case 0x00: 497 | Address = (void*)&LanguageString; 498 | Size = pgm_read_byte(&LanguageString.Header.Size); 499 | break; 500 | case 0x01: 501 | if (mocoMode == 1) { 502 | Address = (void*)&ManufacturerStringMIDI; 503 | Size = pgm_read_byte(&ManufacturerStringMIDI.Header.Size); 504 | } else { 505 | Address = (void*)&ManufacturerStringSerial; 506 | Size = pgm_read_byte(&ManufacturerStringSerial.Header.Size); 507 | } 508 | break; 509 | case 0x02: 510 | if (mocoMode == 1) { 511 | Address = (void*)&ProductStringMIDI; 512 | Size = pgm_read_byte(&ProductStringMIDI.Header.Size); 513 | } else { 514 | Address = (void*)&ProductStringSerial; 515 | Size = pgm_read_byte(&ProductStringSerial.Header.Size); 516 | } 517 | break; 518 | } 519 | 520 | break; 521 | } 522 | 523 | *DescriptorAddress = Address; 524 | return Size; 525 | } 526 | -------------------------------------------------------------------------------- /firmware/MIDI.cpp: -------------------------------------------------------------------------------- 1 | /*! 2 | * @file MIDI.cpp 3 | * Project MIDI Library 4 | * @brief MIDI Library for the Arduino 5 | * @version 3.2 6 | * @author Francois Best 7 | * @date 24/02/11 8 | * license GPL Forty Seven Effects - 2011 9 | */ 10 | 11 | #include "MIDI.h" 12 | #include 13 | #include "Arduino.h" // If using an old (pre-1.0) version of Arduino, use WConstants.h instead of Arduino.h 14 | #include "HardwareSerial.h" 15 | 16 | 17 | /*! \brief Main instance (the class comes pre-instantiated). */ 18 | MIDI_Class MIDI; 19 | 20 | 21 | /*! \brief Default constructor for MIDI_Class. */ 22 | MIDI_Class::MIDI_Class() 23 | { 24 | 25 | #if USE_CALLBACKS 26 | 27 | // Initialise callbacks to NULL pointer 28 | mNoteOffCallback = NULL; 29 | mNoteOnCallback = NULL; 30 | mAfterTouchPolyCallback = NULL; 31 | mControlChangeCallback = NULL; 32 | mProgramChangeCallback = NULL; 33 | mAfterTouchChannelCallback = NULL; 34 | mPitchBendCallback = NULL; 35 | mSystemExclusiveCallback = NULL; 36 | mTimeCodeQuarterFrameCallback = NULL; 37 | mSongPositionCallback = NULL; 38 | mSongSelectCallback = NULL; 39 | mTuneRequestCallback = NULL; 40 | mClockCallback = NULL; 41 | mStartCallback = NULL; 42 | mContinueCallback = NULL; 43 | mStopCallback = NULL; 44 | mActiveSensingCallback = NULL; 45 | mSystemResetCallback = NULL; 46 | 47 | #endif 48 | 49 | } 50 | 51 | 52 | /*! \brief Default destructor for MIDI_Class. 53 | 54 | This is not really useful for the Arduino, as it is never called... 55 | */ 56 | MIDI_Class::~MIDI_Class() 57 | { 58 | 59 | } 60 | 61 | 62 | /*! \brief Call the begin method in the setup() function of the Arduino. 63 | 64 | All parameters are set to their default values: 65 | - Input channel set to 1 if no value is specified 66 | - Full thru mirroring 67 | */ 68 | void MIDI_Class::begin(const byte inChannel) 69 | { 70 | 71 | // Initialise the Serial port 72 | USE_SERIAL_PORT.begin(MIDI_BAUDRATE); 73 | 74 | 75 | #if COMPILE_MIDI_OUT 76 | 77 | #if USE_RUNNING_STATUS 78 | 79 | mRunningStatus_TX = InvalidType; 80 | 81 | #endif // USE_RUNNING_STATUS 82 | 83 | #endif // COMPILE_MIDI_OUT 84 | 85 | 86 | #if COMPILE_MIDI_IN 87 | 88 | mInputChannel = inChannel; 89 | mRunningStatus_RX = InvalidType; 90 | mPendingMessageIndex = 0; 91 | mPendingMessageExpectedLenght = 0; 92 | 93 | mMessage.valid = false; 94 | mMessage.type = InvalidType; 95 | mMessage.channel = 0; 96 | mMessage.data1 = 0; 97 | mMessage.data2 = 0; 98 | 99 | #endif // COMPILE_MIDI_IN 100 | 101 | 102 | #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru 103 | 104 | mThruFilterMode = Full; 105 | mThruActivated = true; 106 | 107 | #endif // Thru 108 | 109 | } 110 | 111 | 112 | #if COMPILE_MIDI_OUT 113 | 114 | // Private method for generating a status byte from channel and type 115 | const byte MIDI_Class::genstatus(const kMIDIType inType, 116 | const byte inChannel) const 117 | { 118 | 119 | return ((byte)inType | ((inChannel-1) & 0x0F)); 120 | 121 | } 122 | 123 | 124 | /*! \brief Generate and send a MIDI message from the values given. 125 | \param type The message type (see type defines for reference) 126 | \param data1 The first data byte. 127 | \param data2 The second data byte (if the message contains only 1 data byte, set this one to 0). 128 | \param channel The output channel on which the message will be sent (values from 1 to 16). Note: you cannot send to OMNI. 129 | 130 | This is an internal method, use it only if you need to send raw data from your code, at your own risks. 131 | */ 132 | void MIDI_Class::send(kMIDIType type, 133 | byte data1, 134 | byte data2, 135 | byte channel) 136 | { 137 | 138 | // Then test if channel is valid 139 | if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) { 140 | 141 | #if USE_RUNNING_STATUS 142 | mRunningStatus_TX = InvalidType; 143 | #endif 144 | 145 | return; // Don't send anything 146 | } 147 | 148 | if (type <= PitchBend) { 149 | // Channel messages 150 | 151 | // Protection: remove MSBs on data 152 | data1 &= 0x7F; 153 | data2 &= 0x7F; 154 | 155 | byte statusbyte = genstatus(type,channel); 156 | 157 | #if USE_RUNNING_STATUS 158 | // Check Running Status 159 | if (mRunningStatus_TX != statusbyte) { 160 | // New message, memorise and send header 161 | mRunningStatus_TX = statusbyte; 162 | USE_SERIAL_PORT.write(mRunningStatus_TX); 163 | } 164 | #else 165 | // Don't care about running status, send the Control byte. 166 | USE_SERIAL_PORT.write(statusbyte); 167 | #endif 168 | 169 | // Then send data 170 | USE_SERIAL_PORT.write(data1); 171 | if (type != ProgramChange && type != AfterTouchChannel) { 172 | USE_SERIAL_PORT.write(data2); 173 | } 174 | return; 175 | } 176 | if (type >= TuneRequest && type <= SystemReset) { 177 | // System Real-time and 1 byte. 178 | sendRealTime(type); 179 | } 180 | 181 | } 182 | 183 | 184 | /*! \brief Send a Note On message 185 | \param NoteNumber Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n 186 | \param Velocity Note attack velocity (0 to 127). A NoteOn with 0 velocity is considered as a NoteOff. 187 | \param Channel The channel on which the message will be sent (1 to 16). 188 | */ 189 | void MIDI_Class::sendNoteOn(byte NoteNumber, 190 | byte Velocity, 191 | byte Channel) 192 | { 193 | 194 | send(NoteOn,NoteNumber,Velocity,Channel); 195 | 196 | } 197 | 198 | 199 | /*! \brief Send a Note Off message (a real Note Off, not a Note On with null velocity) 200 | \param NoteNumber Pitch value in the MIDI format (0 to 127). Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html\n 201 | \param Velocity Release velocity (0 to 127). 202 | \param Channel The channel on which the message will be sent (1 to 16). 203 | */ 204 | void MIDI_Class::sendNoteOff(byte NoteNumber, 205 | byte Velocity, 206 | byte Channel) 207 | { 208 | 209 | send(NoteOff,NoteNumber,Velocity,Channel); 210 | 211 | } 212 | 213 | 214 | /*! \brief Send a Program Change message 215 | \param ProgramNumber The Program to select (0 to 127). 216 | \param Channel The channel on which the message will be sent (1 to 16). 217 | */ 218 | void MIDI_Class::sendProgramChange(byte ProgramNumber, 219 | byte Channel) 220 | { 221 | 222 | send(ProgramChange,ProgramNumber,0,Channel); 223 | 224 | } 225 | 226 | 227 | /*! \brief Send a Control Change message 228 | \param ControlNumber The controller number (0 to 127). See the detailed description here: http://www.somascape.org/midi/tech/spec.html#ctrlnums 229 | \param ControlValue The value for the specified controller (0 to 127). 230 | \param Channel The channel on which the message will be sent (1 to 16). 231 | */ 232 | void MIDI_Class::sendControlChange(byte ControlNumber, 233 | byte ControlValue, 234 | byte Channel) 235 | { 236 | 237 | send(ControlChange,ControlNumber,ControlValue,Channel); 238 | 239 | } 240 | 241 | 242 | /*! \brief Send a Polyphonic AfterTouch message (applies to only one specified note) 243 | \param NoteNumber The note to apply AfterTouch to (0 to 127). 244 | \param Pressure The amount of AfterTouch to apply (0 to 127). 245 | \param Channel The channel on which the message will be sent (1 to 16). 246 | */ 247 | void MIDI_Class::sendPolyPressure(byte NoteNumber, 248 | byte Pressure, 249 | byte Channel) 250 | { 251 | 252 | send(AfterTouchPoly,NoteNumber,Pressure,Channel); 253 | 254 | } 255 | 256 | 257 | /*! \brief Send a MonoPhonic AfterTouch message (applies to all notes) 258 | \param Pressure The amount of AfterTouch to apply to all notes. 259 | \param Channel The channel on which the message will be sent (1 to 16). 260 | */ 261 | void MIDI_Class::sendAfterTouch(byte Pressure, 262 | byte Channel) 263 | { 264 | 265 | send(AfterTouchChannel,Pressure,0,Channel); 266 | 267 | } 268 | 269 | 270 | /*! \brief Send a Pitch Bend message using a signed integer value. 271 | \param PitchValue The amount of bend to send (in a signed integer format), between -8192 (maximum downwards bend) and 8191 (max upwards bend), center value is 0. 272 | \param Channel The channel on which the message will be sent (1 to 16). 273 | */ 274 | void MIDI_Class::sendPitchBend(int PitchValue, 275 | byte Channel) 276 | { 277 | 278 | unsigned int bend = PitchValue + 8192; 279 | sendPitchBend(bend,Channel); 280 | 281 | } 282 | 283 | 284 | /*! \brief Send a Pitch Bend message using an unsigned integer value. 285 | \param PitchValue The amount of bend to send (in a signed integer format), between 0 (maximum downwards bend) and 16383 (max upwards bend), center value is 8192. 286 | \param Channel The channel on which the message will be sent (1 to 16). 287 | */ 288 | void MIDI_Class::sendPitchBend(unsigned int PitchValue, 289 | byte Channel) 290 | { 291 | 292 | send(PitchBend,(PitchValue & 0x7F),(PitchValue >> 7) & 0x7F,Channel); 293 | 294 | } 295 | 296 | 297 | /*! \brief Send a Pitch Bend message using a floating point value. 298 | \param PitchValue The amount of bend to send (in a floating point format), between -1.0f (maximum downwards bend) and +1.0f (max upwards bend), center value is 0.0f. 299 | \param Channel The channel on which the message will be sent (1 to 16). 300 | */ 301 | void MIDI_Class::sendPitchBend(double PitchValue, 302 | byte Channel) 303 | { 304 | 305 | unsigned int pitchval = (PitchValue+1.f)*8192; 306 | if (pitchval > 16383) pitchval = 16383; // overflow protection 307 | sendPitchBend(pitchval,Channel); 308 | 309 | } 310 | 311 | 312 | /*! \brief Generate and send a System Exclusive frame. 313 | \param length The size of the array to send 314 | \param array The byte array containing the data to send 315 | \param ArrayContainsBoundaries When set to 'true', 0xF0 & 0xF7 bytes (start & stop SysEx) will NOT be sent (and therefore must be included in the array). 316 | default value is set to 'false' for compatibility with previous versions of the library. 317 | */ 318 | void MIDI_Class::sendSysEx(int length, 319 | const byte *const array, 320 | bool ArrayContainsBoundaries) 321 | { 322 | 323 | if (ArrayContainsBoundaries == false) { 324 | 325 | USE_SERIAL_PORT.write(0xF0); 326 | 327 | for (int i=0;i> 7) & 0x7F); 407 | 408 | #if USE_RUNNING_STATUS 409 | mRunningStatus_TX = InvalidType; 410 | #endif 411 | 412 | } 413 | 414 | 415 | /*! \brief Send a Song Select message */ 416 | void MIDI_Class::sendSongSelect(byte SongNumber) 417 | { 418 | 419 | USE_SERIAL_PORT.write((byte)SongSelect); 420 | USE_SERIAL_PORT.write(SongNumber & 0x7F); 421 | 422 | #if USE_RUNNING_STATUS 423 | mRunningStatus_TX = InvalidType; 424 | #endif 425 | 426 | } 427 | 428 | 429 | /*! \brief Send a Real Time (one byte) message. 430 | 431 | \param Type The available Real Time types are: Start, Stop, Continue, Clock, ActiveSensing and SystemReset. 432 | You can also send a Tune Request with this method. 433 | @see kMIDIType 434 | */ 435 | void MIDI_Class::sendRealTime(kMIDIType Type) 436 | { 437 | switch (Type) { 438 | case TuneRequest: // Not really real-time, but one byte anyway. 439 | case Clock: 440 | case Start: 441 | case Stop: 442 | case Continue: 443 | case ActiveSensing: 444 | case SystemReset: 445 | USE_SERIAL_PORT.write((byte)Type); 446 | break; 447 | default: 448 | // Invalid Real Time marker 449 | break; 450 | } 451 | 452 | // Do not cancel Running Status for real-time messages as they can be interleaved within any message. 453 | // Though, TuneRequest can be sent here, and as it is a System Common message, it must reset Running Status. 454 | #if USE_RUNNING_STATUS 455 | if (Type == TuneRequest) mRunningStatus_TX = InvalidType; 456 | #endif 457 | 458 | } 459 | 460 | #endif // COMPILE_MIDI_OUT 461 | 462 | 463 | 464 | #if COMPILE_MIDI_IN 465 | 466 | /*! \brief Read a MIDI message from the serial port using the main input channel (see setInputChannel() for reference). 467 | 468 | Returned value: true if any valid message has been stored in the structure, false if not. 469 | A valid message is a message that matches the input channel. \n\n 470 | If the Thru is enabled and the messages matches the filter, it is sent back on the MIDI output. 471 | */ 472 | bool MIDI_Class::read() 473 | { 474 | 475 | return read(mInputChannel); 476 | 477 | } 478 | 479 | 480 | /*! \brief Reading/thru-ing method, the same as read() with a given input channel to read on. */ 481 | bool MIDI_Class::read(const byte inChannel) 482 | { 483 | 484 | if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled. 485 | 486 | if (parse(inChannel)) { 487 | 488 | if (input_filter(inChannel)) { 489 | 490 | #if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) 491 | thru_filter(inChannel); 492 | #endif 493 | 494 | #if USE_CALLBACKS 495 | launchCallback(); 496 | #endif 497 | 498 | return true; 499 | } 500 | 501 | } 502 | 503 | return false; 504 | 505 | } 506 | 507 | 508 | // Private method: MIDI parser 509 | bool MIDI_Class::parse(byte inChannel) 510 | { 511 | 512 | const int bytes_available = USE_SERIAL_PORT.available(); 513 | 514 | if (bytes_available <= 0) { 515 | // No data available. 516 | return false; 517 | } 518 | 519 | // If the buffer is full -> Don't Panic! Call the Vogons to destroy it. 520 | if (bytes_available == 128) { 521 | USE_SERIAL_PORT.flush(); 522 | } 523 | else { 524 | 525 | /* Parsing algorithm: 526 | Get a byte from the serial buffer. 527 | * If there is no pending message to be recomposed, start a new one. 528 | - Find type and channel (if pertinent) 529 | - Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty. 530 | * Else, add the extracted byte to the pending message, and check validity. When the message is done, store it. 531 | */ 532 | 533 | 534 | const byte extracted = USE_SERIAL_PORT.read(); 535 | 536 | if (mPendingMessageIndex == 0) { // Start a new pending message 537 | mPendingMessage[0] = extracted; 538 | 539 | // Check for running status first 540 | switch (getTypeFromStatusByte(mRunningStatus_RX)) { 541 | // Only these types allow Running Status: 542 | case NoteOff: 543 | case NoteOn: 544 | case AfterTouchPoly: 545 | case ControlChange: 546 | case ProgramChange: 547 | case AfterTouchChannel: 548 | case PitchBend: 549 | 550 | // If the status byte is not received, prepend it to the pending message 551 | if (extracted < 0x80) { 552 | mPendingMessage[0] = mRunningStatus_RX; 553 | mPendingMessage[1] = extracted; 554 | mPendingMessageIndex = 1; 555 | } 556 | // Else: well, we received another status byte, so the running status does not apply here. 557 | // It will be updated upon completion of this message. 558 | 559 | break; 560 | 561 | default: 562 | // No running status 563 | break; 564 | } 565 | 566 | 567 | switch (getTypeFromStatusByte(mPendingMessage[0])) { 568 | 569 | // 1 byte messages 570 | case Start: 571 | case Continue: 572 | case Stop: 573 | case Clock: 574 | case ActiveSensing: 575 | case SystemReset: 576 | case TuneRequest: 577 | // Handle the message type directly here. 578 | mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); 579 | mMessage.channel = 0; 580 | mMessage.data1 = 0; 581 | mMessage.data2 = 0; 582 | mMessage.valid = true; 583 | 584 | // \fix Running Status broken when receiving Clock messages. 585 | // Do not reset all input attributes, Running Status must remain unchanged. 586 | //reset_input_attributes(); 587 | 588 | // We still need to reset these 589 | mPendingMessageIndex = 0; 590 | mPendingMessageExpectedLenght = 0; 591 | 592 | return true; 593 | break; 594 | 595 | // 2 bytes messages 596 | case ProgramChange: 597 | case AfterTouchChannel: 598 | case TimeCodeQuarterFrame: 599 | case SongSelect: 600 | mPendingMessageExpectedLenght = 2; 601 | break; 602 | 603 | // 3 bytes messages 604 | case NoteOn: 605 | case NoteOff: 606 | case ControlChange: 607 | case PitchBend: 608 | case AfterTouchPoly: 609 | case SongPosition: 610 | mPendingMessageExpectedLenght = 3; 611 | break; 612 | 613 | case SystemExclusive: 614 | mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes 615 | mRunningStatus_RX = InvalidType; 616 | break; 617 | 618 | case InvalidType: 619 | default: 620 | // This is obviously wrong. Let's get the hell out'a here. 621 | reset_input_attributes(); 622 | return false; 623 | break; 624 | } 625 | 626 | // Then update the index of the pending message. 627 | mPendingMessageIndex++; 628 | 629 | #if USE_1BYTE_PARSING 630 | // Message is not complete. 631 | return false; 632 | #else 633 | // Call the parser recursively 634 | // to parse the rest of the message. 635 | return parse(inChannel); 636 | #endif 637 | 638 | } 639 | else { 640 | 641 | // First, test if this is a status byte 642 | if (extracted >= 0x80) { 643 | 644 | // Reception of status bytes in the middle of an uncompleted message 645 | // are allowed only for interleaved Real Time message or EOX 646 | switch (extracted) { 647 | case Clock: 648 | case Start: 649 | case Continue: 650 | case Stop: 651 | case ActiveSensing: 652 | case SystemReset: 653 | 654 | /* 655 | This is tricky. Here we will have to extract the one-byte message, 656 | pass it to the structure for being read outside the MIDI class, 657 | and recompose the message it was interleaved into. 658 | 659 | Oh, and without killing the running status.. 660 | 661 | This is done by leaving the pending message as is, it will be completed on next calls. 662 | */ 663 | 664 | mMessage.type = (kMIDIType)extracted; 665 | mMessage.data1 = 0; 666 | mMessage.data2 = 0; 667 | mMessage.channel = 0; 668 | mMessage.valid = true; 669 | return true; 670 | 671 | break; 672 | 673 | // End of Exclusive 674 | case 0xF7: 675 | if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { 676 | 677 | // Store System Exclusive array in midimsg structure 678 | for (byte i=0;i> 8; 687 | 688 | mMessage.channel = 0; 689 | mMessage.valid = true; 690 | 691 | reset_input_attributes(); 692 | 693 | return true; 694 | } 695 | else { 696 | // Well well well.. error. 697 | reset_input_attributes(); 698 | return false; 699 | } 700 | 701 | break; 702 | default: 703 | break; 704 | } 705 | 706 | 707 | 708 | } 709 | 710 | 711 | // Add extracted data byte to pending message 712 | mPendingMessage[mPendingMessageIndex] = extracted; 713 | 714 | 715 | // Now we are going to check if we have reached the end of the message 716 | if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) { 717 | 718 | // "FML" case: fall down here with an overflown SysEx.. 719 | // This means we received the last possible data byte that can fit the buffer. 720 | // If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE. 721 | if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { 722 | reset_input_attributes(); 723 | return false; 724 | } 725 | 726 | 727 | mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); 728 | mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message 729 | 730 | mMessage.data1 = mPendingMessage[1]; 731 | 732 | // Save data2 only if applicable 733 | if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2]; 734 | else mMessage.data2 = 0; 735 | 736 | // Reset local variables 737 | mPendingMessageIndex = 0; 738 | mPendingMessageExpectedLenght = 0; 739 | 740 | mMessage.valid = true; 741 | 742 | // Activate running status (if enabled for the received type) 743 | switch (mMessage.type) { 744 | case NoteOff: 745 | case NoteOn: 746 | case AfterTouchPoly: 747 | case ControlChange: 748 | case ProgramChange: 749 | case AfterTouchChannel: 750 | case PitchBend: 751 | // Running status enabled: store it from received message 752 | mRunningStatus_RX = mPendingMessage[0]; 753 | break; 754 | 755 | default: 756 | // No running status 757 | mRunningStatus_RX = InvalidType; 758 | break; 759 | } 760 | return true; 761 | } 762 | else { 763 | // Then update the index of the pending message. 764 | mPendingMessageIndex++; 765 | 766 | #if USE_1BYTE_PARSING 767 | // Message is not complete. 768 | return false; 769 | #else 770 | // Call the parser recursively 771 | // to parse the rest of the message. 772 | return parse(inChannel); 773 | #endif 774 | 775 | } 776 | 777 | } 778 | 779 | } 780 | 781 | // What are our chances to fall here? 782 | return false; 783 | } 784 | 785 | 786 | // Private method: check if the received message is on the listened channel 787 | bool MIDI_Class::input_filter(byte inChannel) 788 | { 789 | 790 | 791 | // This method handles recognition of channel (to know if the message is destinated to the Arduino) 792 | 793 | 794 | if (mMessage.type == InvalidType) return false; 795 | 796 | 797 | // First, check if the received message is Channel 798 | if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { 799 | 800 | // Then we need to know if we listen to it 801 | if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) { 802 | return true; 803 | 804 | } 805 | else { 806 | // We don't listen to this channel 807 | return false; 808 | } 809 | 810 | } 811 | else { 812 | 813 | // System messages are always received 814 | return true; 815 | } 816 | 817 | } 818 | 819 | 820 | // Private method: reset input attributes 821 | void MIDI_Class::reset_input_attributes() 822 | { 823 | 824 | mPendingMessageIndex = 0; 825 | mPendingMessageExpectedLenght = 0; 826 | mRunningStatus_RX = InvalidType; 827 | 828 | } 829 | 830 | 831 | // Getters 832 | /*! \brief Get the last received message's type 833 | 834 | Returns an enumerated type. @see kMIDIType 835 | */ 836 | kMIDIType MIDI_Class::getType() const 837 | { 838 | 839 | return mMessage.type; 840 | 841 | } 842 | 843 | 844 | /*! \brief Get the channel of the message stored in the structure. 845 | 846 | Channel range is 1 to 16. For non-channel messages, this will return 0. 847 | */ 848 | byte MIDI_Class::getChannel() const 849 | { 850 | 851 | return mMessage.channel; 852 | 853 | } 854 | 855 | 856 | /*! \brief Get the first data byte of the last received message. */ 857 | byte MIDI_Class::getData1() const 858 | { 859 | 860 | return mMessage.data1; 861 | 862 | } 863 | 864 | 865 | /*! \brief Get the second data byte of the last received message. */ 866 | byte MIDI_Class::getData2() const 867 | { 868 | 869 | return mMessage.data2; 870 | 871 | } 872 | 873 | 874 | /*! \brief Get the System Exclusive byte array. 875 | 876 | @see getSysExArrayLength to get the array's length in bytes. 877 | */ 878 | const byte * MIDI_Class::getSysExArray() const 879 | { 880 | 881 | return mMessage.sysex_array; 882 | 883 | } 884 | 885 | /*! \brief Get the lenght of the System Exclusive array. 886 | 887 | It is coded using data1 as LSB and data2 as MSB. 888 | \return The array's length, in bytes. 889 | */ 890 | unsigned int MIDI_Class::getSysExArrayLength() const 891 | { 892 | 893 | unsigned int coded_size = ((unsigned int)(mMessage.data2) << 8) | mMessage.data1; 894 | 895 | return (coded_size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : coded_size; 896 | 897 | } 898 | 899 | 900 | /*! \brief Check if a valid message is stored in the structure. */ 901 | bool MIDI_Class::check() const 902 | { 903 | 904 | return mMessage.valid; 905 | 906 | } 907 | 908 | 909 | // Setters 910 | /*! \brief Set the value for the input MIDI channel 911 | \param Channel the channel value. Valid values are 1 to 16, 912 | MIDI_CHANNEL_OMNI if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable MIDI input. 913 | */ 914 | void MIDI_Class::setInputChannel(const byte Channel) 915 | { 916 | 917 | mInputChannel = Channel; 918 | 919 | } 920 | 921 | 922 | #if USE_CALLBACKS 923 | 924 | void MIDI_Class::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; } 925 | void MIDI_Class::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; } 926 | void MIDI_Class::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; } 927 | void MIDI_Class::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; } 928 | void MIDI_Class::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; } 929 | void MIDI_Class::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; } 930 | void MIDI_Class::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; } 931 | void MIDI_Class::setHandleSystemExclusive(void (*fptr)(byte * array, byte size)) { mSystemExclusiveCallback = fptr; } 932 | void MIDI_Class::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } 933 | void MIDI_Class::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; } 934 | void MIDI_Class::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } 935 | void MIDI_Class::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; } 936 | void MIDI_Class::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; } 937 | void MIDI_Class::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; } 938 | void MIDI_Class::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; } 939 | void MIDI_Class::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; } 940 | void MIDI_Class::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; } 941 | void MIDI_Class::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; } 942 | 943 | 944 | /*! \brief Detach an external function from the given type. 945 | 946 | Use this method to cancel the effects of setHandle********. 947 | \param Type The type of message to unbind. When a message of this type is received, no function will be called. 948 | */ 949 | void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) 950 | { 951 | 952 | switch (Type) { 953 | case NoteOff: mNoteOffCallback = NULL; break; 954 | case NoteOn: mNoteOnCallback = NULL; break; 955 | case AfterTouchPoly: mAfterTouchPolyCallback = NULL; break; 956 | case ControlChange: mControlChangeCallback = NULL; break; 957 | case ProgramChange: mProgramChangeCallback = NULL; break; 958 | case AfterTouchChannel: mAfterTouchChannelCallback = NULL; break; 959 | case PitchBend: mPitchBendCallback = NULL; break; 960 | case SystemExclusive: mSystemExclusiveCallback = NULL; break; 961 | case TimeCodeQuarterFrame: mTimeCodeQuarterFrameCallback = NULL; break; 962 | case SongPosition: mSongPositionCallback = NULL; break; 963 | case SongSelect: mSongSelectCallback = NULL; break; 964 | case TuneRequest: mTuneRequestCallback = NULL; break; 965 | case Clock: mClockCallback = NULL; break; 966 | case Start: mStartCallback = NULL; break; 967 | case Continue: mContinueCallback = NULL; break; 968 | case Stop: mStopCallback = NULL; break; 969 | case ActiveSensing: mActiveSensingCallback = NULL; break; 970 | case SystemReset: mSystemResetCallback = NULL; break; 971 | default: 972 | break; 973 | } 974 | 975 | } 976 | 977 | 978 | // Private - launch callback function based on received type. 979 | void MIDI_Class::launchCallback() 980 | { 981 | 982 | // The order is mixed to allow frequent messages to trigger their callback faster. 983 | 984 | switch (mMessage.type) { 985 | // Notes 986 | case NoteOff: if (mNoteOffCallback != NULL) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; 987 | case NoteOn: if (mNoteOnCallback != NULL) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; 988 | 989 | // Real-time messages 990 | case Clock: if (mClockCallback != NULL) mClockCallback(); break; 991 | case Start: if (mStartCallback != NULL) mStartCallback(); break; 992 | case Continue: if (mContinueCallback != NULL) mContinueCallback(); break; 993 | case Stop: if (mStopCallback != NULL) mStopCallback(); break; 994 | case ActiveSensing: if (mActiveSensingCallback != NULL) mActiveSensingCallback(); break; 995 | 996 | // Continuous controllers 997 | case ControlChange: if (mControlChangeCallback != NULL) mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; 998 | case PitchBend: if (mPitchBendCallback != NULL) mPitchBendCallback(mMessage.channel,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) - 8192); break; // TODO: check this 999 | case AfterTouchPoly: if (mAfterTouchPolyCallback != NULL) mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; 1000 | case AfterTouchChannel: if (mAfterTouchChannelCallback != NULL) mAfterTouchChannelCallback(mMessage.channel,mMessage.data1); break; 1001 | 1002 | case ProgramChange: if (mProgramChangeCallback != NULL) mProgramChangeCallback(mMessage.channel,mMessage.data1); break; 1003 | case SystemExclusive: if (mSystemExclusiveCallback != NULL) mSystemExclusiveCallback(mMessage.sysex_array,mMessage.data1); break; 1004 | 1005 | // Occasional messages 1006 | case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != NULL) mTimeCodeQuarterFrameCallback(mMessage.data1); break; 1007 | case SongPosition: if (mSongPositionCallback != NULL) mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)); break; 1008 | case SongSelect: if (mSongSelectCallback != NULL) mSongSelectCallback(mMessage.data1); break; 1009 | case TuneRequest: if (mTuneRequestCallback != NULL) mTuneRequestCallback(); break; 1010 | 1011 | case SystemReset: if (mSystemResetCallback != NULL) mSystemResetCallback(); break; 1012 | case InvalidType: 1013 | default: 1014 | break; 1015 | } 1016 | 1017 | } 1018 | 1019 | 1020 | #endif // USE_CALLBACKS 1021 | 1022 | 1023 | #endif // COMPILE_MIDI_IN 1024 | 1025 | 1026 | 1027 | 1028 | #if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru 1029 | 1030 | /*! \brief Set the filter for thru mirroring 1031 | \param inThruFilterMode a filter mode 1032 | 1033 | @see kThruFilterMode 1034 | */ 1035 | void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode) 1036 | { 1037 | 1038 | mThruFilterMode = inThruFilterMode; 1039 | if (mThruFilterMode != Off) mThruActivated = true; 1040 | else mThruActivated = false; 1041 | 1042 | } 1043 | 1044 | 1045 | /*! \brief Setter method: turn message mirroring on. */ 1046 | void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode) 1047 | { 1048 | 1049 | mThruActivated = true; 1050 | mThruFilterMode = inThruFilterMode; 1051 | 1052 | } 1053 | 1054 | 1055 | /*! \brief Setter method: turn message mirroring off. */ 1056 | void MIDI_Class::turnThruOff() 1057 | { 1058 | 1059 | mThruActivated = false; 1060 | mThruFilterMode = Off; 1061 | 1062 | } 1063 | 1064 | 1065 | // This method is called upon reception of a message and takes care of Thru filtering and sending. 1066 | void MIDI_Class::thru_filter(byte inChannel) 1067 | { 1068 | 1069 | /* 1070 | This method handles Soft-Thru filtering. 1071 | 1072 | Soft-Thru filtering: 1073 | - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off 1074 | - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting 1075 | 1076 | */ 1077 | 1078 | // If the feature is disabled, don't do anything. 1079 | if (!mThruActivated || (mThruFilterMode == Off)) return; 1080 | 1081 | 1082 | // First, check if the received message is Channel 1083 | if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { 1084 | 1085 | const bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)); 1086 | 1087 | // Now let's pass it to the output 1088 | switch (mThruFilterMode) { 1089 | case Full: 1090 | send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); 1091 | return; 1092 | break; 1093 | case SameChannel: 1094 | if (filter_condition) { 1095 | send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); 1096 | return; 1097 | } 1098 | break; 1099 | case DifferentChannel: 1100 | if (!filter_condition) { 1101 | send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); 1102 | return; 1103 | } 1104 | break; 1105 | case Off: 1106 | // Do nothing. 1107 | // Technically it's impossible to get there because the case was already tested earlier. 1108 | break; 1109 | default: 1110 | break; 1111 | } 1112 | 1113 | } 1114 | else { 1115 | 1116 | // Send the message to the output 1117 | switch (mMessage.type) { 1118 | // Real Time and 1 byte 1119 | case Clock: 1120 | case Start: 1121 | case Stop: 1122 | case Continue: 1123 | case ActiveSensing: 1124 | case SystemReset: 1125 | case TuneRequest: 1126 | sendRealTime(mMessage.type); 1127 | return; 1128 | break; 1129 | 1130 | case SystemExclusive: 1131 | // Send SysEx (0xF0 and 0xF7 are included in the buffer) 1132 | sendSysEx(mMessage.data1,mMessage.sysex_array,true); 1133 | return; 1134 | break; 1135 | 1136 | case SongSelect: 1137 | sendSongSelect(mMessage.data1); 1138 | return; 1139 | break; 1140 | 1141 | case SongPosition: 1142 | sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7)); 1143 | return; 1144 | break; 1145 | 1146 | case TimeCodeQuarterFrame: 1147 | sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2); 1148 | return; 1149 | break; 1150 | default: 1151 | break; 1152 | 1153 | } 1154 | 1155 | } 1156 | 1157 | } 1158 | 1159 | 1160 | #endif // Thru 1161 | 1162 | 1163 | --------------------------------------------------------------------------------