├── .gitmodules ├── README.md ├── examples ├── AnalogToMidi.jpg ├── MIDIdrum │ └── MIDIdrum.ino ├── MIDIenc │ └── MIDIenc.ino ├── MIDIpot │ └── MIDIpot.ino ├── MIDIswitch │ └── MIDIswitch.ino ├── MIDIswitch_toggle │ └── MIDIswitch_toggle.ino ├── MIDItouch │ └── MIDItouch.ino ├── PiezoSchematic.jpg ├── findAnalogRange │ └── findAnalogRange.ino ├── findTouchRange │ └── findTouchRange.ino ├── useAfterTouch │ └── useAfterTouch.ino └── useMuxedInput │ └── useMuxedInput.ino ├── keywords.txt ├── library.properties └── src ├── MIDIcontroller.h ├── MIDIdrum.cpp ├── MIDIdrum.h ├── MIDIenc.cpp ├── MIDIenc.h ├── MIDIpot.cpp ├── MIDIpot.h ├── MIDIswitch.cpp ├── MIDIswitch.h ├── MIDItouch.cpp ├── MIDItouch.h └── elapsedMillis.h /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Flicker"] 2 | path = Flicker 3 | url = https://github.com/joshnishikawa/Flicker 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MIDIcontroller 3.1.4 2 | ### A library for creating Teensy MIDI controllers. 3 | ###### by Josh Nishikawa 4 | 5 | FEATURES INCLUDE: 6 | - velocity sensitive FSR or Piezo inputs 7 | - momentary, latch or 'trigger' type MIDI buttons (also work with Cap Touch) 8 | - stable analog to MIDI conversion for potentiometers and other sensors 9 | - stable Capacitive Touch to MIDI conversion for expression control 10 | - support for encoders 11 | - Min/Max output can be set (or inverted) for all MIDI. Min/Max input 12 | can also be set for analog input (maintaining stability in conversion) 13 | ___ 14 | ***THIS LIBRARY REQUIRES - MIDI Library, Bounce, Encoder, Flicker*** 15 | 16 | ___ 17 | 18 | ### VERSION LOG: 19 | 3.1.3 MAJOR RELEASE WITH BREAKING CHANGES: 20 | - Min and Max output is no longer set when constructing objects. 21 | use outputRange(min, max) instead. 22 | - MIDIbutton is now completely deprecated. Use MIDIswitch instead. 23 | - MIDIcapSense is now completely deprecated. Use MIDItouch instead. 24 | - MIDIdrum using TOUCH is deprecated. It's still doable but just too unstable 25 | and too niche for this library. Use the Flicker library instead. 26 | 27 | OTHER CHANGES: 28 | - Added sensitivity(int) function to MIDIdrum. Takes a number between 1 and 100. 29 | Lower numbers require higher velocity to trigger MIDI. 100 is default 30 | and triggers even if pressed very slowly. 99 requires at least a light tap. 31 | - MIDIenc can now take PROGRAM_CHANGE as an argument. This allows the encoder 32 | to send program change messages instead of CC messages. 33 | - MIDIswitch can now take START, STOP, CONTINUE, CLOCK or SYSTEM_RESET as an 34 | argument. This allows the switch to send those messages instead of CC. 35 | - Better examples 36 | - Made inclusion of Flicker library optional. Some boards don't to Cap Touch. 37 | 38 | (thanks @digitalelements for suggestions and testing all of the above) 39 | 40 | - MIDIenc.read() now updates the value of the encoder. This allows the value 41 | to be changed without actually sending MIDI. 42 | - Split analogRange and touchRange into separate utilities. 43 | - Bugfix: MIDIenc.value initialized to outLo when user-specified. 44 | 45 | 2.5.5 46 | - Dependency for deprecated Bounce library changed to Bounce2. This allows the 47 | library to be installed via the Arduino IDE2 library manager. 48 | - Bugfix: "unsigned int = -1" == oops! 49 | - Other bugfixes 50 | 51 | 2.5.3 52 | - 'MIDIswitch' now preferred over 'MIDIbutton' ( 'MIDIbutton' still works ). 53 | - Added a send(FORCE) function to allow event-based sending of current CC value. 54 | This works for MIDIswitch, MIDIpot, MIDIenc and MIDItouch. This allows 55 | 'bulk send' to be implemented. WARNING! In the main loop, just use send() 56 | 57 | - MIDIswitch default of MOMENTARY works now. use MIDIswitch(pin, CC) 58 | 59 | 2.4.3 60 | - Fixed the PER_DETENT bug (again). 61 | - Made PER_DETENT the default since it works cleaner and is probably more useful 62 | - Added a write(int) function to the MIDIenc class that will set it to a 63 | specific value and immediately send a MIDI message for that CC at that value 64 | 65 | 2.4.2 66 | - Fixed a bug preventing PER_DETENT from working on encoders. 67 | 68 | 2.4.1 69 | - Bounce (not Bounce2) is listed as a dependency. 70 | 71 | 2.4.0 72 | - The .4. is for the added inputRange() and setWaitTime() functions 73 | - This update also (actually) fixes that MIDIdrum bug preventing high velocity 74 | 75 | 2.3.4 76 | - Added inputRange() to MIDIdrum. 77 | - Added an example for muxed input. 78 | - Fixed a bug preventing MIDIdrum from sending velocity 127. 79 | 80 | 2.3.3 81 | - Made waitTime for MIDIdrum user-selectable. 82 | 83 | 2.3.2 84 | - Fixed the broken smooth() function for analog inputs. 85 | 86 | 2.3.1 87 | - All examples updated to prevent MIDI and usbMIDI stack crashes. 88 | 89 | 2.3.0 90 | - A major update to the Flicker library: 91 | - thresholds for Capacitive Touch buttons are automatically detected 92 | - more stable expression control for MIDItouch(previously MIDIcapSens) 93 | - MIDIdrum includes option to use velocity-sensitive Capacitive Touch 94 | - Even better stabilization of analog inputs 95 | - Added option for encoders to change 1 CC value per detent 96 | - Made "killSwitch" user-selectable (just put any CC# instead of KILL) 97 | - Added setKillSwitch(byte) to set kill CC (use OFF or 0 to disable it). 98 | - Added a few visual aides 99 | 100 | 2.2.5 101 | - Bugfixed jitter that occurred when using inputRange() with input maxed 102 | - Arguments for specific velocities can now be passed to velocity inputs 103 | - Added literals to highlight MOMENTARY, LATCH, TRIGGER and KILL modes 104 | - Long overdue completion of the "Flicker" library (for Cap Touch input) 105 | - Made separate examples for 'pot' and 'sensor' (to avoid confusion) 106 | 107 | 2.2.0 108 | - Added support for Piezos (Must be wired properly. See example) 109 | - "MIDInote" class changed to "MIDIdrum" and optimized for FSR and Piezo 110 | - Removed redundant 'velocity' variable. Just call outputRange(127, 127) 111 | - Added support for using a Capacitive Touch input as a MIDIbutton 112 | - Included 'Flicker' library (required for Capacitive Touch buttons) 113 | 114 | 2.1.5 115 | - got rid of useless '*MC' pointer. renamed 'kill' to 'mode' 116 | 117 | 2.1.3 118 | - included an example of how to implement aftertouch 119 | 120 | 2.1.2 121 | - many variables changed to 'byte' or 'uint16_t' for easy storage 122 | - public and private variables are better sorted 123 | 124 | 2.1.0 125 | - Split read() and send() functions. MIDI channel is now user selectable 126 | 127 | 2.0.6 128 | - Fixed a bug preventing poly CC to return to zero after note off. 129 | 130 | 2.0.5 131 | - Added a condition to prevent many double note triggers. 132 | 133 | 2.0.4 134 | - Added support for capacitive sensors (and started this version log) 135 | ___ 136 | 137 | Permission is hereby granted, free of charge, to any person obtaining a copy 138 | of this software and associated documentation files (the "Software"), to deal 139 | in the Software without restriction, including without limitation the rights 140 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 141 | copies of the Software, and to permit persons to whom the Software is 142 | furnished to do so, subject to the following conditions: 143 | 144 | The above copyright notice and this permission notice shall be included in 145 | all copies or substantial portions of the Software. 146 | 147 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 148 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 149 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 150 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 151 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 152 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 153 | THE SOFTWARE. 154 | -------------------------------------------------------------------------------- /examples/AnalogToMidi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshnishikawa/MIDIcontroller/75173fc269d2cf2f34f236061c3560f1e251d845/examples/AnalogToMidi.jpg -------------------------------------------------------------------------------- /examples/MIDIdrum/MIDIdrum.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example sets up a velocity sensitive input using a force-sensitive 3 | resistor (FSR) or a Piezo transducer. Only the wiring differs. 4 | 5 | AN FSR SHOULD BE WIRED LIKE SO: 6 | 7 | 3.3v-----(FSR)-----\ 8 | )---Analog Pin 9 | GND------/\/\/-----/ 10 | 10k 11 | 12 | THE PIEZO IS DIFFERENT AND MUST BE WIRED CORRECTLY TO WORK AND PREVENT DAMAGE! 13 | 14 | - The black wire of the Piezo, the anode of a diode and 15 | one side of a 10k resistor should all be connected to ground. 16 | - The cathode of A SECOND diode should be connected to the voltage. 17 | - The red wire of the Piezo should be connected to a 470 ohm resistor. 18 | - The remaining ends of BOTH resistors and BOTH diodes should be connected 19 | to the same analog pin on the microcontroller. 20 | 21 | *See PiezoSchematic.jpg for a diagram. 22 | */ 23 | 24 | #include "MIDIcontroller.h" 25 | 26 | byte MIDIchannel = 5; 27 | const int pressPin = A0; // Change this to the correct ANALOG pin 28 | 29 | /* MIDIdrum parameters are: 30 | 1) pin (required) 31 | 2) note number (required) 32 | 3) sensitivity (optional 1 ~ 100. 90 is default. Higher is more sensitive) 33 | */ 34 | 35 | MIDIdrum myPad(pressPin, 37); 36 | // MIDIdrum myPad(pressPin, 37, 100); // Trigger even without hit (any contact) 37 | 38 | void setup(){ 39 | // UNCOMMENT ANY OF THE FOLLOWING LINES TO CHANGE THE DEFAULTS 40 | // myPad.inputRange(12, 720); // Specify the usable analog range for the FSR 41 | // myPad.outputRange(20, 120); // Map input to send only velocities 20 ~ 120 42 | // myPad.setSensitivity(100); // 100% triggers even without hit (any contact) 43 | // myPad.setWaitTime(30); //'debounce' 30ms before allowing next trigger 44 | } 45 | 46 | void loop(){ 47 | myPad.send(); 48 | //myPad.send(64); could be used for a fixed velocity e.g. 64 49 | 50 | 51 | // This prevents crashes that happen when incoming usbMIDI is ignored. 52 | while(usbMIDI.read()){} 53 | 54 | // Also uncomment this if compiling for standard MIDI 55 | // while(MIDI.read()){} 56 | } 57 | 58 | /* GM DRUM SOUNDS 59 | MIDI# - NOTE - SOUND 60 | 35 B 0 Acoustic Bass Drum 61 | 36 C 1 Bass Drum 1 62 | 37 C# 1 Side Stick 63 | 38 D 1 Acoustic Snare 64 | 39 D# 1 Hand Clap 65 | 40 E 1 Electric Snare 66 | 41 F 1 Low Floor Tom 67 | 42 F# 1 Closed Hi Hat 68 | 43 G 1 High Floor Tom 69 | 44 G# 1 Pedal Hi-Hat 70 | 45 A 1 Low Tom 71 | 46 A# 1 Open Hi-Hat 72 | 47 B 1 Low-Mid Tom 73 | 48 C 2 Hi-Mid Tom 74 | 49 C# 2 Crash Cymbal 1 75 | 50 D 2 High Tom 76 | 51 D# 2 Ride Cymbal 1 77 | 52 E 2 Chinese Cymbal 78 | 53 F 2 Ride Bell 79 | 54 F# 2 Tambourine 80 | 55 G 2 Splash Cymbal 81 | 56 G# 2 Cowbell 82 | 57 A 2 Crash Cymbal 2 83 | 58 A# 2 Vibraslap 84 | 59 B 2 Ride Cymbal 2 85 | 60 C 3 Hi Bongo 86 | 61 C# 3 Low Bongo 87 | 62 D 3 Mute Hi Conga 88 | 63 D# 3 Open Hi Conga 89 | 64 E 3 Low Conga 90 | 65 F 3 High Timbale 91 | 66 F# 3 Low Timbale 92 | 67 G 3 High Agogo 93 | 68 G# 3 Low Agogo 94 | 69 A 3 Cabasa 95 | 70 A# 3 Maracas 96 | 71 B 3 Short Whistle 97 | 72 C 4 Long Whistle 98 | 73 C# 4 Short Guiro 99 | 74 D 4 Long Guiro 100 | 75 D# 4 Claves 101 | 76 E 4 Hi Wood Block 102 | 77 F 4 Low Wood Block 103 | 78 F# 4 Mute Cuica 104 | 79 G 4 Open Cuica 105 | 80 G# 4 Mute Triangle 106 | 81 A 4 Open Triangle 107 | 108 | 109 | MIDI NOTE NUMBERS 110 | C C# D D# E F F# G G# A A# B 111 | |------------------------------------------------ 112 | -2 | 0 1 2 3 4 5 6 7 8 9 10 11 113 | -1 | 12 13 14 15 16 17 18 19 20 21 22 23 114 | 0 | 24 25 26 27 28 29 30 31 32 33 34 35 115 | 1 | 36 37 38 39 40 41 42 43 44 45 46 47 116 | 2 | 48 49 50 51 52 53 54 55 56 57 58 59 117 | 3 | 60 61 62 63 64 65 66 67 68 69 70 71 118 | 4 | 72 73 74 75 76 77 78 79 80 81 82 83 119 | 5 | 84 85 86 87 88 89 90 91 92 93 94 95 120 | 6 | 96 97 98 99 100 101 102 103 104 105 106 107 121 | 7 | 108 109 110 111 112 113 114 115 116 117 118 119 122 | 8 | 120 121 122 123 124 125 126 127 123 | */ 124 | -------------------------------------------------------------------------------- /examples/MIDIenc/MIDIenc.ino: -------------------------------------------------------------------------------- 1 | #include "MIDIcontroller.h" 2 | 3 | byte MIDIchannel = 5; 4 | 5 | const int encPinA = 24; // Change these numbers to 6 | const int encPinB = 25; // the two pins your encoder is on. 7 | 8 | /* Encoder parameters are: 9 | 1) pin A (required) 10 | 2) pin B (required) 11 | 3) a CC number ( 0 ~ 119 ) 12 | OR a channel mode number ( 120 ~ 127 ) 13 | OR PROGRAM_CHANGE to send program changes 14 | (required) 15 | 4) PER_VALUE or PER_DETENT (optional, PER_DETENT is default) 16 | */ 17 | 18 | MIDIenc myEnc(encPinA, encPinB, 24); 19 | // MIDIenc myEnc(encPinA, encPinB, 24, PER_VALUE); // Faster but not for use with detented encoders. 20 | // MIDIenc myEnc(encPinA, encPinB, PROGRAM_CHANGE); // Send program change instead of CC# 21 | 22 | void setup(){ 23 | // UNCOMMENT ANY OF THE FOLLOWING LINES TO CHANGE THE DEFAULTS 24 | // myEnc.write(64); // Initialize the encoder to 64 25 | // myEnc.outputRange(127, 0); // Reverse the direction of the encoder 26 | // myEnc.outputRange(56, 79); // Restrict CC value or program change # to 56 ~ 79 27 | } 28 | 29 | void loop(){ 30 | myEnc.send(); 31 | 32 | 33 | // This prevents crashes that happen when incoming usbMIDI is ignored. 34 | while(usbMIDI.read()){} 35 | 36 | // Also uncomment this if compiling for standard MIDI 37 | // while(MIDI.read()){} 38 | } 39 | -------------------------------------------------------------------------------- /examples/MIDIpot/MIDIpot.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example is for a potentiometer (POT) or any variable resistor (SENSOR) 3 | such as a photo cell, FSR, flex resistor, thermistor etc. 4 | _________ 5 | 3.3v___| | 6 | Analog Pin___| 10k | 7 | GND___| POT | 8 | |_________| 9 | 10 | 3.3v---(SENSOR)---\ 11 | )---Analog Pin 12 | GND------/\/\/----/ 13 | 10k 14 | */ 15 | 16 | #include "MIDIcontroller.h" 17 | 18 | byte MIDIchannel = 5; 19 | const int potPin = A0; // Change this to the ANALOG pin you want to use 20 | 21 | /* MIDIpot parameters are: 22 | 1) pin (required) 23 | 2) CC number (required) 24 | 3) a secondary on/off CC# (optional) 25 | */ 26 | 27 | MIDIpot myPot(potPin, 22); 28 | // MIDIpot myPot(potPin, 22, 9); // CC# 9 OFF is sent when you turn the pot all the way down. 29 | // CC# 9 ON is sent when you start turning it up again. 30 | 31 | void setup(){ 32 | // UNCOMMENT ANY OF THE FOLLOWING LINES TO CHANGE THE DEFAULTS 33 | // myPot.inputRange(20, 900); // Specify the usable analog range for the pot 34 | // myPot.outputRange(127, 0); // Reverse the direction of the pot 35 | // myPot.outputRange(20, 120); // Restrict value to 20 ~ 120 36 | } 37 | 38 | void loop(){ 39 | myPot.send(); 40 | 41 | 42 | // This prevents crashes that happen when incoming usbMIDI is ignored. 43 | while(usbMIDI.read()){} 44 | 45 | // Also uncomment this if compiling for standard MIDI 46 | // while(MIDI.read()){} 47 | } 48 | -------------------------------------------------------------------------------- /examples/MIDIswitch/MIDIswitch.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example allows any type of switch to be used to send MIDI messages. 3 | A capacititive touch sensor may also be used. 4 | */ 5 | 6 | #include "MIDIcontroller.h" 7 | 8 | byte MIDIchannel = 5; 9 | const int switchPin = 10; //any digital pin 10 | const int ledPin = 13; //Set an LED to show the state of a latch button. 11 | 12 | /* MIDIswitch parameters are: 13 | 1) pin (required) 14 | 2) a Control Change / Channel Mode number: 0 ~ 127 15 | OR a Real Time message: START, STOP, CONTINUE, CLOCK, SYSTEM_RESET 16 | (required) 17 | 3) MOMENTARY, LATCH or TRIGGER 18 | MOMENTARY: Sends ON messages when connection is made, OFF when broken. 19 | LATCH: Sends alternating ON OFF messages each time a connection is made. 20 | TRIGGER: Sends ON messages each time a connection is made but no OFF messages. 21 | (optional, CC default is MOMENTARY, Real Time default is TRIGGER) 22 | 4) BINARY or TOUCH 23 | (optional, BINARY is default, TOUCH is for capacitive touch sensors) 24 | */ 25 | 26 | MIDIswitch myInput(switchPin, 21); // Momentary button for CC# 21 27 | // MIDIswitch myInput(switchPin, 21, LATCH); // will latch on and off 28 | // MIDIswitch myInput(switchPin, 21, LATCH, TOUCH); // use a capacitive touch sensor instead of a switch 29 | // MIDIswitch myInput(switchPin, START); // will send start messages 30 | 31 | void setup(){ 32 | pinMode(ledPin, OUTPUT); 33 | 34 | // YOU MUST UNCOMMENT ONE OF THE FOLLOWING 2 LINES TO USE A TOUCH SENSOR 35 | // myInput.inputRange(); // WARNING! If you touch the input during setup(), it won't work! 36 | // myInput.inputRange(70, 2100); // OR use the 'findTouchRange' example to find values to specify here 37 | 38 | // UNCOMMENT ANY OF THE FOLLOWING LINES TO CHANGE THE DEFAULTS 39 | // myInput.setControlNumber(22); // change CC# 40 | // myInput.setMode(TRIGGER); // will send an ON and OFF message for each press 41 | // myInput.outputRange(30, 70); // toggle between 30 and 70 42 | } 43 | 44 | void loop(){ 45 | myInput.send(); 46 | digitalWrite(ledPin, myInput.state); 47 | 48 | 49 | // This prevents crashes that happen when incoming usbMIDI is ignored. 50 | while(usbMIDI.read()){} 51 | 52 | // Also uncomment this if compiling for standard MIDI 53 | // while(MIDI.read()){} 54 | } 55 | -------------------------------------------------------------------------------- /examples/MIDIswitch_toggle/MIDIswitch_toggle.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example will show how to use a switch to toggle between two different 3 | MIDI messages. It could allow a switch to toggle START and STOP for example. 4 | */ 5 | 6 | #include "MIDIcontroller.h" 7 | 8 | byte MIDIchannel = 5; 9 | const int switchPin = 2; 10 | const int ledPin = 13; //Set an LED to show the state of the input. 11 | bool state = false; 12 | 13 | MIDIswitch myInput(switchPin, START); // Don't use LATCH 14 | 15 | void setup(){ 16 | pinMode(ledPin, OUTPUT); 17 | } 18 | 19 | void loop(){ 20 | if ( myInput.send() > -1) { // no input == -1 21 | state = !state; 22 | if (state) myInput.setControlNumber(STOP); 23 | else myInput.setControlNumber(START); 24 | } 25 | 26 | digitalWrite(ledPin, state); // LED indicates state 27 | 28 | 29 | // This prevents crashes that happen when incoming usbMIDI is ignored. 30 | while(usbMIDI.read()){} 31 | 32 | // Also uncomment this if compiling for standard MIDI 33 | // while(MIDI.read()){} 34 | } 35 | -------------------------------------------------------------------------------- /examples/MIDItouch/MIDItouch.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This is an example of how to set up a capacitive touch 3 | sensor as a variable MIDI input. Any capacitive object connected 4 | to a 'touch' pin will work. If using metal, use a layer of 5 | something non-conductive between the metal and your skin. 6 | */ 7 | 8 | #include "MIDIcontroller.h" 9 | 10 | byte MIDIchannel = 5; 11 | const int touchPin = 22; // Change this to the correct TOUCH pin 12 | 13 | /* MIDIpot parameters are: 14 | 1) pin (required) 15 | 2) CC number (required) 16 | 3) a secondary on/off CC# (optional) 17 | */ 18 | 19 | MIDItouch myInput(touchPin, 60); 20 | // MIDItouch myInput(touchPin, 60, 9); // CC# 9 OFF is sent when reading drops below threshold. 21 | // CC# 9 ON is sent when threshold is breached. 22 | 23 | void setup(){ 24 | myInput.inputRange(); // WARNING! If you touch the input during setup(), it won't work. 25 | // myInput.inputRange(70, 2100); // OR use the 'findTouchRange' example to find values to specify here 26 | 27 | // myInput.outputRange(20, 120); // Restrict output to 20 ~ 120 28 | } 29 | 30 | void loop(){ 31 | myInput.send(); 32 | 33 | 34 | // This prevents crashes that happen when incoming usbMIDI is ignored. 35 | while(usbMIDI.read()){} 36 | 37 | // Also uncomment this if compiling for standard MIDI 38 | // while(MIDI.read()){} 39 | } 40 | -------------------------------------------------------------------------------- /examples/PiezoSchematic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshnishikawa/MIDIcontroller/75173fc269d2cf2f34f236061c3560f1e251d845/examples/PiezoSchematic.jpg -------------------------------------------------------------------------------- /examples/findAnalogRange/findAnalogRange.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch is here for convenience. Use it to determine the usable analog 3 | range of a sensor or pot. It will print the highest and lowest values it sees. 4 | Use that info to set the input range or threshold for objects in your sketch. 5 | */ 6 | 7 | #include "Bounce2.h" 8 | 9 | int analogPin = A0; // Change to the ANALOG pin you want to use. 10 | int resetPin = 19; // You can assign a button to reset the values. 11 | Bounce reset = Bounce(resetPin, 50); 12 | int hi; 13 | int lo; 14 | 15 | void setReset(){ 16 | hi = analogRead(analogPin); 17 | lo = hi; 18 | Serial.print("High: "); Serial.print(hi); Serial.print("\t"); 19 | Serial.print("Low: "); Serial.println(lo); 20 | } 21 | 22 | void setup(){ 23 | pinMode(resetPin, INPUT_PULLUP); 24 | setReset(); 25 | } 26 | 27 | void loop(){ 28 | reset.update(); 29 | if(reset.rose()){ 30 | setReset(); 31 | } 32 | 33 | int newVal = analogRead(analogPin); 34 | if (newVal > hi){ 35 | hi = newVal; 36 | Serial.print("High: "); Serial.print(hi); Serial.print("\t"); 37 | Serial.print("Low: "); Serial.println(lo); 38 | } 39 | else if (newVal < lo){ 40 | lo = newVal; 41 | Serial.print("High: "); Serial.print(hi); Serial.print("\t"); 42 | Serial.print("Low: "); Serial.println(lo); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/findTouchRange/findTouchRange.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This sketch is here for convenience. Use it to determine the usable range of a 3 | capacitive touch sensor. It will print the highest and lowest values it sees. 4 | Use that info to set the input range or threshold for objects in your sketch. 5 | */ 6 | 7 | #include "Bounce2.h" 8 | 9 | int touchPin = 23; // Change to the TOUCH pin you want to use. 10 | int resetPin = 2; // you can set a pin to reset hi/lo 11 | Bounce reset = Bounce(resetPin, 50); 12 | int hi; 13 | int lo; 14 | 15 | void setReset(){ 16 | hi = touchRead(touchPin); 17 | lo = hi; 18 | Serial.print("High: "); Serial.print(hi); Serial.print("\t"); 19 | Serial.print("Low: "); Serial.println(lo); 20 | } 21 | 22 | void setup(){ 23 | pinMode(resetPin, INPUT_PULLUP); 24 | setReset(); 25 | } 26 | 27 | void loop(){ 28 | reset.update(); 29 | if(reset.rose()){ 30 | setReset(); 31 | } 32 | 33 | int newVal = touchRead(17); 34 | newVal = touchRead(18); 35 | newVal = touchRead(19); 36 | newVal = touchRead(22); 37 | 38 | newVal = touchRead(touchPin); 39 | if (newVal > hi){ 40 | hi = newVal; 41 | Serial.print("High: "); Serial.print(hi); Serial.print("\t"); 42 | Serial.print("Low: "); Serial.println(lo); 43 | } 44 | else if (newVal < lo){ 45 | lo = newVal; 46 | Serial.print("High: "); Serial.print(hi); Serial.print("\t"); 47 | Serial.print("Low: "); Serial.println(lo); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/useAfterTouch/useAfterTouch.ino: -------------------------------------------------------------------------------- 1 | #include "MIDIcontroller.h" 2 | 3 | byte MIDIchannel = 5; 4 | const int FSRpin = A0; // Change this to the ANALOG pin you want to use. 5 | 6 | MIDIdrum myPad(FSRpin, 40); 7 | MIDIpot aftertouch(FSRpin, 77); 8 | 9 | void setup(){ 10 | } 11 | 12 | void loop(){ 13 | myPad.send(); 14 | aftertouch.send(); 15 | 16 | 17 | // This prevents crashes that happen when incoming usbMIDI is ignored. 18 | while(usbMIDI.read()){} 19 | 20 | // Also uncomment this if compiling for standard MIDI 21 | // while(MIDI.read()){} 22 | } 23 | -------------------------------------------------------------------------------- /examples/useMuxedInput/useMuxedInput.ino: -------------------------------------------------------------------------------- 1 | #include "MIDIcontroller.h" 2 | 3 | byte MIDIchannel = 5; 4 | 5 | const int selector_pin_a = 0; 6 | const int selector_pin_b = 1; 7 | const int selector_pin_c = 2; 8 | //const int selector_pin_d = 3; // uncomment only if using MUX-16 9 | 10 | const int mux_input_pin = A0; // Change this to the ANALOG pin you want to use 11 | MIDIpot* Pots[8]; // change to Pots[16] if using MUX-16 12 | 13 | void setup(){ 14 | pinMode(selector_pin_a, OUTPUT); 15 | pinMode(selector_pin_b, OUTPUT); 16 | pinMode(selector_pin_c, OUTPUT); 17 | // pinMode(selector_pin_d, OUTPUT); // only if using MUX-16 18 | 19 | for(int i=0; i<8; i++){ // change to i<16 if using MUX-16 20 | Pots[i] = new MIDIpot(mux_input_pin, 12+i); 21 | // CC 12~ are probably safe to use but you can use any CC number you like. 22 | 23 | Pots[i]->inputRange(15, 1000); // helps reach the full output range 24 | } 25 | } 26 | 27 | void loop(){ 28 | for(int i=0; i<8; i++){ // change to i<16 if using MUX-16 29 | //digitalWrite(selector_pin_d, (i&15)>>3); // uncomment only if using MUX-16 30 | digitalWrite(selector_pin_c, (i&7)>>2); 31 | digitalWrite(selector_pin_b, (i&3)>>1); 32 | digitalWrite(selector_pin_a, (i&1)); 33 | 34 | Pots[i]->send(); 35 | } 36 | 37 | 38 | // This prevents crashes that happen when incoming usbMIDI is ignored. 39 | while(usbMIDI.read()){} 40 | 41 | // Also uncomment this if compiling for standard MIDI 42 | // while(MIDI.read()){} 43 | } 44 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | MIDIswitch KEYWORD1 2 | MIDIenc KEYWORD1 3 | MIDIpot KEYWORD1 4 | MIDItouch KEYWORD1 5 | MIDIdrum KEYWORD1 6 | 7 | read KEYWORD2 8 | send KEYWORD2 9 | setNoteNumber KEYWORD2 10 | setControlNumber KEYWORD2 11 | setMode KEYWORD2 12 | setChannel KEYWORD2 13 | inputRange KEYWORD2 14 | outputRange KEYWORD2 15 | setThreshold KEYWORD2 16 | setThresholds KEYWORD2 17 | setWaitTime KEYWORD2 18 | sensitivity KEYWORD2 19 | 20 | MOMENTARY LITERAL1 21 | LATCH LITERAL1 22 | TRIGGER LITERAL1 23 | BINARY LITERAL1 24 | TOUCH LITERAL1 25 | FSR LITERAL1 26 | PIEZO LITERAL1 27 | KILL LITERAL1 28 | FORCE LITERAL1 29 | OFF LITERAL1 30 | PER_VALUE LITERAL1 31 | PER_DETENT LITERAL1 32 | 33 | START LITERAL1 34 | STOP LITERAL1 35 | CONTINUE LITERAL1 36 | CLOCK LITERAL1 37 | SYSTEM_RESET LITERAL1 38 | PROGRAM_CHANGE LITERAL1 -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=MIDIcontroller 2 | version=3.1.4 3 | author=Josh Nishikawa 4 | maintainer=Josh Nishikawa 5 | sentence=A library for creating Teensy MIDI controllers. 6 | paragraph=Includes easy to follow examples for implementing velocity sensitive FSR, Piezo or Capacitive Touch inputs - momentary, latch or trigger type MIDI buttons (also works with Cap Touch) - stable analog to MIDI conversion for potentiometers and other sensors - stable Capacitive Touch to MIDI conversion for expression control - support for encoders - Min/Max output can be set (or inverted) for all MIDI - Min/Max input can also be set for analog input (maintaining stability in conversion) 7 | category=Sensors 8 | url=https://github.com/joshnishikawa/MIDIcontroller 9 | architectures=avr 10 | depends=MIDI Library, Flicker, Bounce2, Encoder 11 | -------------------------------------------------------------------------------- /src/MIDIcontroller.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDIcontroller_h 2 | #define MIDIcontroller_h 3 | 4 | #include 5 | #include "MIDIswitch.h" 6 | #include "MIDIpot.h" 7 | #include "MIDIenc.h" 8 | #include "MIDIdrum.h" 9 | 10 | // Include the MIDItouch class only if the board has capacitive touch pins 11 | // Teensy 3.6 Teensy 3.2 Teensy 3.0 Teensy LC 12 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 13 | #include "MIDItouch.h" 14 | #endif 15 | 16 | #define FORCE true 17 | 18 | // Bounce2, Encoder and Flicker libraries are also required 19 | // github.com/joshnishikawa/Flicker 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/MIDIdrum.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDIdrum.h" 2 | 3 | // constructors 4 | MIDIdrum::MIDIdrum(){}; 5 | 6 | MIDIdrum::MIDIdrum(int p, uint8_t num){ 7 | pinMode(p, INPUT); 8 | pin = p; 9 | number = num; 10 | outLo = 1; 11 | outHi = 127; 12 | 13 | threshold = 12; 14 | sensitivity = 10; // 90% sensitive by default 15 | upperThreshold = threshold + sensitivity; 16 | 17 | inHi = 1023; 18 | isOn = false; 19 | peak = 0; 20 | state = 0; //0= idle, 1= test velocity, 2= look for peak, 3= ignore aftershock 21 | waitTime = 0; // millis 22 | timer = 0; 23 | }; 24 | 25 | MIDIdrum::MIDIdrum(int p, uint8_t num, uint8_t sens){ 26 | pinMode(p, INPUT); 27 | pin = p; 28 | number = num; 29 | outLo = 0; 30 | outHi = 127; 31 | 32 | threshold = 10; 33 | sens = constrain(sens, 0, 100); 34 | sensitivity = 100 - sens; 35 | upperThreshold = threshold + sensitivity; 36 | 37 | sensitivity = 10; // 90% sensitive by default 38 | upperThreshold = threshold + sensitivity; 39 | 40 | inHi = 1023; 41 | isOn = false; 42 | peak = 0; 43 | state = 0; //0= idle, 1= test velocity, 2= look for peak, 3= ignore aftershock 44 | waitTime = 0; // millis 45 | timer = 0; 46 | }; 47 | 48 | // destructor 49 | MIDIdrum::~MIDIdrum(){ 50 | }; 51 | 52 | 53 | int MIDIdrum::read(){ 54 | int newValue = analogRead(pin); 55 | 56 | switch (state){ 57 | case 1: 58 | // test for velocity 59 | if (timer < 2 && newValue >= upperThreshold){ 60 | peak = newValue; 61 | state = 2; 62 | } 63 | else if (timer >= 2){ 64 | state = 3; 65 | } 66 | return -1; 67 | break; 68 | 69 | case 2: 70 | // look for peak 71 | if (newValue > peak) { 72 | peak = newValue; 73 | return -1; 74 | } 75 | else if (timer >= 10){ 76 | newValue = constrain(peak, upperThreshold, inHi); 77 | newValue = newValue >= inHi ? outHi : map(peak,upperThreshold,inHi,outLo,outHi); 78 | isOn = true; 79 | peak = 0; 80 | state = 3; 81 | timer = 0; 82 | return newValue; 83 | } 84 | else { 85 | return -1; 86 | } 87 | break; 88 | 89 | case 3: 90 | if (newValue > threshold) { 91 | timer = 0; // keep resetting timer if above threshold 92 | return -1; 93 | } 94 | else if (timer > waitTime) { 95 | state = 0; // go back to idle after a certain interval below threshold 96 | if (isOn){ 97 | isOn = false; 98 | return 0; 99 | } else return -1; 100 | } 101 | else{ 102 | return -1; 103 | } 104 | break; 105 | 106 | default: 107 | // idle: search for threshold crossing 108 | if (newValue >= threshold) { 109 | state = 1; 110 | timer = 0; 111 | } 112 | return -1; //still just listening 113 | break; 114 | } 115 | }; 116 | 117 | int MIDIdrum::send(){ 118 | int newValue = read(); 119 | if (newValue >= 0){ 120 | usbMIDI.sendNoteOn(number, newValue, MIDIchannel); 121 | } 122 | return newValue; 123 | }; 124 | 125 | int MIDIdrum::send(int vel){ 126 | int newValue = read(); 127 | if (newValue >= 0){ 128 | constrain(vel, 1, 127); 129 | usbMIDI.sendNoteOn(number, vel, MIDIchannel); 130 | } 131 | return newValue; 132 | }; 133 | 134 | void MIDIdrum::setNoteNumber(uint8_t num){ // Set the NOTE number. 135 | number = num; 136 | }; 137 | 138 | void MIDIdrum::outputRange(uint8_t min, uint8_t max){ // Set min & max output values 139 | outLo = constrain(min, 0, 127); 140 | outHi = constrain(max, 0, 127); 141 | }; 142 | 143 | // Limit the analog input to the usable range of a sensor. 144 | void MIDIdrum::inputRange(uint16_t thresh, uint16_t max){ 145 | threshold = constrain(thresh, 0, 1023); 146 | inHi = constrain(max, 0, 1023); 147 | }; 148 | 149 | void MIDIdrum::setThreshold(unsigned int thresh){ 150 | threshold = constrain(thresh, 0, 1023); 151 | }; 152 | 153 | void MIDIdrum::setWaitTime(unsigned int time){ 154 | waitTime = time; 155 | }; 156 | 157 | void MIDIdrum::setSensitivity(uint8_t sens){ 158 | // sensitivity(100) should be thought of as 100% sensitive meaning that notes 159 | // will always sound regardless of velocity. 0 would mean that there needs to 160 | // be enough velocity to reach an analog reading 100 above the threshold 161 | // within 2ms of the threshold crossing. 162 | sens = constrain(sens, 0, 100); 163 | sensitivity = 100 - sens; 164 | upperThreshold = threshold + sensitivity; 165 | }; -------------------------------------------------------------------------------- /src/MIDIdrum.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDIdrum_h 2 | #define MIDIdrum_h 3 | 4 | #include "Arduino.h" 5 | #include "elapsedMillis.h" 6 | 7 | extern byte MIDIchannel; 8 | 9 | class MIDIdrum{ 10 | int pin; 11 | int peak; 12 | int sensitivity; 13 | int state; // 0 = idle, 1 = looking for peak, 2 = ignoring aftershock 14 | bool isOn; 15 | unsigned int waitTime; 16 | elapsedMillis timer; 17 | 18 | public: 19 | // default constructor 20 | MIDIdrum(); 21 | 22 | // constructor with pin and note number only. 23 | MIDIdrum(int p, uint8_t num); 24 | 25 | // constructor with pin, note number and sensitivity. 26 | MIDIdrum(int p, uint8_t num, uint8_t sens); 27 | 28 | // destructor 29 | ~MIDIdrum(); 30 | 31 | int read(); 32 | int send(); 33 | int send(int vel); 34 | uint8_t number; 35 | uint8_t outLo = 1; 36 | uint8_t outHi = 127; 37 | unsigned int inHi = 1023; 38 | unsigned int threshold, upperThreshold; 39 | void setNoteNumber(uint8_t num); 40 | void setThreshold(unsigned int thresh); 41 | void inputRange(uint16_t thresh, uint16_t max); 42 | void setSensitivity(uint8_t sens); 43 | void setWaitTime(unsigned int time); 44 | void outputRange(uint8_t min, uint8_t max); 45 | }; 46 | 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /src/MIDIenc.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDIenc.h" 2 | 3 | // constructors 4 | MIDIenc::MIDIenc(){}; 5 | 6 | MIDIenc::MIDIenc(int a, int b, byte num){ 7 | myKnob = new Encoder(a, b); 8 | number = num; 9 | detentOrValue = 4; // CC changes once per detent 10 | value = 0; 11 | outLo = 0; 12 | outHi = 127; 13 | }; 14 | 15 | MIDIenc::MIDIenc(int a, int b, byte num, byte detentOrValue){ 16 | myKnob = new Encoder(a, b); 17 | number = num; 18 | this->detentOrValue = detentOrValue; // CC changes per encoder value or detent 19 | value = 0; 20 | outLo = 0; 21 | outHi = 127; 22 | }; 23 | 24 | // destructor 25 | MIDIenc::~MIDIenc(){ 26 | delete myKnob; 27 | }; 28 | 29 | 30 | int MIDIenc::read(){ 31 | int incdec = myKnob->read(); 32 | 33 | if (incdec >= detentOrValue){ 34 | myKnob->write(0); 35 | if ( value < outHi ){ 36 | value++; 37 | return value; 38 | } 39 | else{ return -1; } 40 | } 41 | else if (incdec <= -detentOrValue){ 42 | myKnob->write(0); 43 | if ( value > outLo ){ 44 | value--; 45 | return value; 46 | } 47 | else{ return -1; } 48 | } 49 | else{ return -1; } 50 | }; 51 | 52 | 53 | int MIDIenc::send(){ 54 | int newValue = read(); 55 | if (newValue >= 0){ 56 | value = newValue; 57 | if (number == PROGRAM_CHANGE){ 58 | usbMIDI.sendProgramChange(value, MIDIchannel); 59 | } 60 | else{ 61 | usbMIDI.sendControlChange(number, newValue, MIDIchannel); 62 | } 63 | } 64 | return newValue; 65 | } 66 | 67 | 68 | int MIDIenc::send(bool force){ 69 | if (force){ 70 | usbMIDI.sendControlChange(number, value, MIDIchannel); 71 | return value; 72 | } 73 | else{ return -1; } 74 | } 75 | 76 | 77 | // Manually set the value. 78 | void MIDIenc::write(byte val){ 79 | value = constrain(val, outLo, outHi); 80 | }; 81 | 82 | // Set the CC number. 83 | void MIDIenc::setControlNumber(byte num){ 84 | number = num; 85 | }; 86 | 87 | // Set upper and lower limits for outgoing MIDI messages. 88 | void MIDIenc::outputRange(byte min, byte max){ 89 | outLo = constrain(min, 0, 127); 90 | outHi = constrain(max, 0, 127); 91 | }; -------------------------------------------------------------------------------- /src/MIDIenc.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDIenc_h 2 | #define MIDIenc_h 3 | 4 | #include "Arduino.h" 5 | #include "Bounce2.h" 6 | #include "Encoder.h" 7 | 8 | #define PER_VALUE 1 9 | #define PER_DETENT 4 10 | #define PROGRAM_CHANGE 0xC0 11 | 12 | extern byte MIDIchannel; 13 | 14 | class MIDIenc{ 15 | public: 16 | // default constructor 17 | MIDIenc(); 18 | 19 | // constructor when only pins and control number are given 20 | MIDIenc(int a, int b, byte num); 21 | 22 | // " pins, control number and whether value changes PER_DETENT or PER_VALUE 23 | MIDIenc(int a, int b, byte num, byte detentOrValue); 24 | 25 | // destructor 26 | ~MIDIenc(); 27 | 28 | int read(); // read input and return a MIDI value (or -1 if none) 29 | int send(); // calls read(), sends and returns a MIDI value (or -1 if none) 30 | int send(bool force); // forces MIDI output regardless of input 31 | byte number; 32 | byte value; 33 | byte outLo, outHi; 34 | byte detentOrValue; 35 | Encoder *myKnob; 36 | void write(byte val); 37 | void setControlNumber(byte num); 38 | void outputRange(byte min, byte max); 39 | }; 40 | 41 | #endif -------------------------------------------------------------------------------- /src/MIDIpot.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDIpot.h" 2 | 3 | // constructors 4 | MIDIpot::MIDIpot(){}; 5 | 6 | MIDIpot::MIDIpot(int p, uint8_t num){ 7 | pinMode(p, INPUT); 8 | pin = p; 9 | number = num; 10 | value = 0; 11 | mode = false; 12 | killSwitch = 0; 13 | inLo = 0; 14 | inHi = 1023; 15 | outLo = 0; 16 | outHi = 127; 17 | invert = outLo > outHi; 18 | 19 | // Sets the interval at which alalog signals will actually register. 20 | divider = !invert ? (inHi-inLo)/(outHi-outLo):(inHi-inLo)/(outLo-outHi); 21 | divider = divider < 1 ? 1 : divider; // Allows analog range < 127 (NOT GOOD!) 22 | }; 23 | 24 | MIDIpot::MIDIpot(int p, uint8_t num, uint8_t k){ 25 | pinMode(p, INPUT); 26 | pin = p; 27 | number = num; 28 | value = 0; 29 | mode = true; 30 | killSwitch = k; 31 | inLo = 0; 32 | inHi = 1023; 33 | outLo = 0; 34 | outHi = 127; 35 | invert = outLo > outHi; 36 | 37 | // Sets the interval at which alalog signals will actually register. 38 | divider = !invert ? (inHi-inLo)/(outHi-outLo):(inHi-inLo)/(outLo-outHi); 39 | divider = divider < 1 ? 1 : divider; // Allows analog range < 127 (NOT GOOD!) 40 | }; 41 | 42 | // destructor 43 | MIDIpot::~MIDIpot(){ 44 | }; 45 | 46 | 47 | // returns new CC if there's enough change in the analog input; -1 otherwise 48 | int MIDIpot::read(){ 49 | int newValue = this->smooth(analogRead(pin), SMOOTHING); 50 | if (newValue >= inHi && value != outHi){ // Assign hi analog to hi MIDI 51 | value = outHi; 52 | return value; 53 | } 54 | else if (newValue <= inLo && value != outLo){ // Assign low analog to low MIDI 55 | value = outLo; 56 | return value; 57 | } 58 | else if (newValue % divider == 0){ // Filter intermittent values 59 | newValue = map(newValue, inLo, inHi, outLo, outHi); 60 | newValue = invert ? constrain(newValue, outHi, outLo) 61 | : constrain(newValue, outLo, outHi); 62 | return newValue == value ? -1 : newValue; 63 | } 64 | else{ return -1; } 65 | }; 66 | 67 | 68 | int MIDIpot::send(){ 69 | int newValue = read(); 70 | if (killSwitch != 0 && value == outLo && newValue > outLo){//ON before main CC 71 | usbMIDI.sendControlChange(killSwitch, 127, MIDIchannel); 72 | } 73 | 74 | if (newValue >= 0){ 75 | usbMIDI.sendControlChange(number, newValue, MIDIchannel); //MAIN CC MESSAGE 76 | if (killSwitch != 0 && value >= outLo && newValue == outLo){//OFF after main 77 | usbMIDI.sendControlChange(killSwitch, 0, MIDIchannel); 78 | } 79 | value = newValue; 80 | } 81 | return newValue; 82 | }; 83 | 84 | 85 | int MIDIpot::send(bool force){ 86 | if (force){ 87 | balancedValue = analogRead(pin); 88 | 89 | uint8_t newValue = map(balancedValue, inLo, inHi, outLo, outHi); 90 | newValue = invert ? constrain(newValue, outHi, outLo) 91 | : constrain(newValue, outLo, outHi); 92 | 93 | usbMIDI.sendControlChange(number, newValue, MIDIchannel); 94 | return newValue; 95 | } 96 | else{ return -1; } 97 | } 98 | 99 | 100 | void MIDIpot::setControlNumber(uint8_t num){ // Set the CC number. 101 | number = num; 102 | }; 103 | 104 | 105 | // Set upper and lower limits for outgoing MIDI messages. 106 | void MIDIpot::outputRange(uint8_t min, uint8_t max){ 107 | outLo = constrain(min, 0, 127); 108 | outHi = constrain(max, 0, 127); 109 | // Reset the interval at which alalog signals will actually register. 110 | divider = !invert ? (inHi-inLo)/(outHi-outLo):(inHi-inLo)/(outLo-outHi); 111 | divider = divider < 1 ? 1 : divider; // Allows analog range < 127 (NOT GOOD!) 112 | invert = outHi < outLo; // Check again for reverse polarity. 113 | }; 114 | 115 | 116 | // Limit the analog input to the usable range of a sensor. 117 | void MIDIpot::inputRange(uint16_t min, uint16_t max){ 118 | inLo = constrain(min, 0, 1023); 119 | inHi = constrain(max, 0, 1023); 120 | // Reset the interval at which alalog signals will actually register. 121 | divider = outHi > outLo ? (inHi-inLo)/(outHi-outLo):(inHi-inLo)/(outLo-outHi); 122 | divider = divider < 1 ? 1 : divider; // Allows analog range < 127 (NOT GOOD!) 123 | }; 124 | 125 | 126 | void MIDIpot::setKillSwitch(uint8_t k){ 127 | if(k == 0){ 128 | mode = false; 129 | } 130 | else{ 131 | mode = true; 132 | killSwitch = constrain(k, 1, 127); 133 | } 134 | }; 135 | 136 | 137 | int MIDIpot::smooth(int val, int NR){ 138 | difference = val - balancedValue; 139 | buffer = val == 0 ? -NR : val == balancedValue ? buffer/2 : buffer+difference; 140 | 141 | if (buffer*buffer >= NR*NR){ // This works better than abs(buffer) for me. 142 | balancedValue = val; 143 | buffer = 0; 144 | } 145 | return balancedValue; 146 | }; 147 | 148 | -------------------------------------------------------------------------------- /src/MIDIpot.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDIpot_h 2 | #define MIDIpot_h 3 | 4 | #include "Arduino.h" 5 | 6 | #define SMOOTHING 50 // Can be increased at the cost of some responsiveness 7 | #define KILL 9 // previously undefined CC# safe for general purpose assignment 8 | #define OFF 0 9 | 10 | extern byte MIDIchannel; 11 | 12 | class MIDIpot{ 13 | int pin = 0; 14 | bool invert = false; 15 | int divider = 8; // for converting from analog to MIDI resolution 16 | int buffer = 0; // a container used by the smooth() function 17 | int difference = 0; // smooth() adds this to buffer 'til there's enough bias 18 | uint16_t balancedValue = 0; // remains stable until signal bias changes it 19 | 20 | public: 21 | // default constructor 22 | MIDIpot(); 23 | 24 | // constructor when only pin & control number are given 25 | MIDIpot(int p, uint8_t num); 26 | 27 | // " when pin, control number are given and kill switch is enabled 28 | MIDIpot(int p, uint8_t num, uint8_t k); 29 | 30 | // destructor 31 | ~MIDIpot(); 32 | 33 | int read(); // read input and return a MIDI value (or -1 if none) 34 | int send(); //calls read(), sends/returns MIDI val (or -1 if none) 35 | int send(bool force); // forces MIDI output regardless of input 36 | uint16_t inLo = 0; 37 | uint16_t inHi = 1023; 38 | uint8_t outLo = 0; 39 | uint8_t outHi = 127; 40 | uint8_t number = 0; 41 | uint8_t value = 0; 42 | uint8_t mode = 0; // In case you need to kill an effect entirely 43 | uint8_t killSwitch = 0; // Which CC is getting its hands dirty? 44 | void setControlNumber(uint8_t num); 45 | void outputRange(uint8_t min, uint8_t max); 46 | void inputRange(uint16_t min, uint16_t max); 47 | void setKillSwitch(uint8_t k); 48 | int smooth(int value, int NR); // smoothes the values of analogRead() 49 | }; 50 | 51 | #endif -------------------------------------------------------------------------------- /src/MIDIswitch.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDIswitch.h" 2 | 3 | // constructors 4 | MIDIswitch::MIDIswitch() : Bounce(0, 0){}; 5 | 6 | MIDIswitch::MIDIswitch(int p, uint8_t num) : Bounce(p, 10){ 7 | pinMode(p, INPUT_PULLUP); 8 | number = num; 9 | 10 | switch (num){ 11 | case START: case STOP: case CONTINUE: case CLOCK: case SYSTEM_RESET: 12 | realTime = true; 13 | outHi = num; 14 | mode = TRIGGER; 15 | break; 16 | default: 17 | realTime = false; 18 | outHi = 127; 19 | mode = MOMENTARY; 20 | } 21 | } 22 | 23 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 24 | MIDIswitch::MIDIswitch(int p, uint8_t num, uint8_t x) : Bounce(p, 10), TouchSwitch(p, 0){ 25 | #else 26 | MIDIswitch::MIDIswitch(int p, uint8_t num, uint8_t x) : Bounce(p, 10){ 27 | #endif 28 | 29 | number = num; 30 | 31 | switch (num){ 32 | case START: case STOP: case CONTINUE: case CLOCK: case SYSTEM_RESET: 33 | realTime = true; 34 | outHi = num; 35 | mode = TRIGGER; // 'x' takes precedence if it indicates a different mode 36 | break; 37 | default: 38 | realTime = false; 39 | } 40 | 41 | switch (x){ 42 | case BINARY: // Bounce object by default 43 | break; 44 | case TOUCH: 45 | inputType = TOUCH; 46 | break; 47 | case MOMENTARY: case LATCH: case TRIGGER: 48 | mode = x; 49 | break; 50 | } 51 | 52 | if (inputType == BINARY) pinMode(p, INPUT_PULLUP); 53 | } 54 | 55 | 56 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 57 | MIDIswitch::MIDIswitch(int p, uint8_t num, uint8_t m, uint8_t t) : Bounce(0, 0), TouchSwitch(p, 0){ 58 | #else 59 | MIDIswitch::MIDIswitch(int p, uint8_t num, uint8_t m, uint8_t t) : Bounce(p, 10){ 60 | #endif 61 | 62 | number = num; 63 | 64 | switch (num){ 65 | case START: case STOP: case CONTINUE: case CLOCK: case SYSTEM_RESET: 66 | realTime = true; 67 | outHi = num; 68 | break; 69 | 70 | default: 71 | realTime = false; 72 | } 73 | 74 | switch (m){ 75 | case MOMENTARY: case LATCH: case TRIGGER: 76 | mode = m; 77 | break; 78 | case BINARY: case TOUCH: 79 | inputType = m; 80 | break; 81 | } 82 | 83 | switch (t){ 84 | case MOMENTARY: case LATCH: case TRIGGER: 85 | mode = t; 86 | break; 87 | case BINARY: case TOUCH: 88 | inputType = t; 89 | break; 90 | } 91 | 92 | if (inputType == BINARY) pinMode(p, INPUT_PULLUP); 93 | } 94 | 95 | 96 | // destructor 97 | MIDIswitch::~MIDIswitch(){}; 98 | 99 | 100 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 101 | void MIDIswitch::setThreshold(){ 102 | TouchSwitch::setThreshold(); 103 | } 104 | 105 | void MIDIswitch::setThreshold(int threshold){ 106 | TouchSwitch::setThreshold(threshold); 107 | } 108 | #endif 109 | 110 | int MIDIswitch::read(){ 111 | if (inputType == BINARY){ 112 | Bounce::update(); // Force a status report of the Bounce object. 113 | inputState = Bounce::state; 114 | if (Bounce::fell()){ // If the button's been pressed, 115 | return outHi; // return the high CC value. 116 | } 117 | else if (Bounce::rose()){ // If the button has been released, 118 | return outLo; // return the low CC value. 119 | } 120 | else{ return -1; } 121 | } 122 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 123 | else if (inputType == TOUCH){ 124 | TouchSwitch::update(); 125 | inputState = TouchSwitch::read(); 126 | if (TouchSwitch::rose()){ 127 | return outHi; 128 | } 129 | else if (TouchSwitch::fell()){ 130 | return outLo; 131 | } 132 | else{ return -1; } 133 | } 134 | #endif 135 | else{ return -1; } 136 | }; 137 | 138 | 139 | 140 | /* This function will send the appropriate Control Change or Real Time messages 141 | for the press and/or release of any MIDI button whether it's set to 142 | 'MOMENTARY' 'LATCH' or 'TRIGGER' mode.*/ 143 | int MIDIswitch::send(){ 144 | int newValue = read(); 145 | 146 | if (newValue == outHi){ // If the button's been pressed, 147 | if (state == false){ // and if it was latched OFF, 148 | if (realTime) usbMIDI.sendRealTime(outHi); 149 | else usbMIDI.sendControlChange(number,outHi,MIDIchannel); // send CC outHi, 150 | state = true; // Remember the button is now on. 151 | return realTime ? 1 : number; 152 | } 153 | else{ // If the button was latched ON, 154 | if (mode == TRIGGER){ // and the button's in TRIGGER mode, 155 | if (realTime) usbMIDI.sendRealTime(outHi); 156 | else usbMIDI.sendControlChange(number,outHi,MIDIchannel); // send CC outHi again 157 | return realTime ? 1 : number; 158 | } 159 | else { 160 | if (!realTime) usbMIDI.sendControlChange(number,outLo,MIDIchannel); // send CC outLo, 161 | state = false; // Remember the button is now off. 162 | return outLo; 163 | } 164 | } 165 | } 166 | else if (newValue == outLo && mode == MOMENTARY){ // MOMENTARY released? 167 | if (!realTime) usbMIDI.sendControlChange(number,outLo,MIDIchannel); // send CC outLo, 168 | state = false; // Remember the button is now off 169 | return outLo; 170 | } 171 | 172 | else return -1; 173 | }; 174 | 175 | 176 | int MIDIswitch::send(bool force){ 177 | if (force){ 178 | if (state){ 179 | if (realTime) usbMIDI.sendRealTime(outHi); 180 | else usbMIDI.sendControlChange(number,outHi,MIDIchannel); 181 | return outHi; 182 | } else { 183 | if (realTime) usbMIDI.sendRealTime(outHi); // ignore state for Real Time 184 | else usbMIDI.sendControlChange(number,outLo,MIDIchannel); 185 | return outLo; 186 | } 187 | } 188 | else { return -1; } 189 | } 190 | 191 | 192 | // Set the CC number. 193 | void MIDIswitch::setControlNumber(byte num){ 194 | switch (num){ 195 | case START: case STOP: case CONTINUE: case CLOCK: case SYSTEM_RESET: 196 | number = outHi = num; 197 | outLo = 0; 198 | break; 199 | 200 | default: 201 | number = num; 202 | switch (outHi){ 203 | case START: case STOP: case CONTINUE: case CLOCK: case SYSTEM_RESET: 204 | outHi = 127; // reset to default if Real Time message previously used 205 | break; 206 | } 207 | } 208 | }; 209 | 210 | 211 | // Set specific min and max values. 212 | void MIDIswitch::outputRange(byte min, byte max){ 213 | outLo = constrain(min, 0, 127); 214 | outHi = constrain(max, 0, 127); 215 | }; 216 | 217 | 218 | // Set the button mode. 219 | void MIDIswitch::setMode(byte mod){ 220 | mode = constrain(mod, 0, 2); 221 | }; 222 | -------------------------------------------------------------------------------- /src/MIDIswitch.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDIswitch_h 2 | #define MIDIswitch_h 3 | 4 | #include "Arduino.h" 5 | #include "Bounce2.h" 6 | 7 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 8 | #include "Flicker.h" 9 | #endif 10 | 11 | #define MOMENTARY 0 12 | #define LATCH 1 13 | #define TRIGGER 2 14 | 15 | #define BINARY 3 16 | #define TOUCH 4 17 | 18 | #define START 0xFA 19 | #define STOP 0xFC 20 | #define CONTINUE 0xFB 21 | #define CLOCK 0xF8 22 | #define SYSTEM_RESET 0xFF 23 | 24 | extern byte MIDIchannel; 25 | 26 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 27 | class MIDIswitch: public Bounce, public TouchSwitch{ 28 | #else 29 | class MIDIswitch: public Bounce{ 30 | #endif 31 | uint8_t inputType = BINARY; // Bounce object by default 32 | bool realTime = false; 33 | public: 34 | // default constructor 35 | MIDIswitch(); 36 | 37 | // constructor for a switch with the default mode of MONENTARY 38 | MIDIswitch(int p, uint8_t num); 39 | 40 | // 'x' could be BINARY, TOUCH, MONENTARY, LATCH or TRIGGER 41 | MIDIswitch(int p, uint8_t num, uint8_t x); 42 | 43 | // constructor for specifying mode (MOMENTARY, LATCH, TRIGGER) 44 | // and type (BINARY, TOUCH) 45 | MIDIswitch(int p, uint8_t num, uint8_t m, uint8_t t); 46 | 47 | // destructor 48 | ~MIDIswitch(); 49 | 50 | #if defined(__MK66FX1M0__) || defined(__MK20DX256__) || defined(__MK20DX128__) || defined(__MKL26Z64__) 51 | // setThreshold() is only used for Capacitive Touch inputs. It assumes the 52 | // input is NOT being touched and automatically calculates a threshold 53 | // using a call to touchRead(). Use in setup(). 54 | void setThreshold(); 55 | void setThreshold(int threshold); 56 | #endif 57 | 58 | int read(); // return outHi for falling edge, outLo for rising edge, else -1 59 | int send(); // calls read(), sends a MIDI value & returns the control number 60 | int send(bool force); // forces MIDI output regardless of input 61 | void write(bool s); // sets the state of a LATCH input 62 | uint8_t number = 0; // redefined on instatiation 63 | uint8_t outLo = 0; 64 | uint8_t outHi = 127; 65 | uint8_t mode = MOMENTARY; // momentary by default 66 | uint8_t inputState = 0; // refers to the actual physical state of the input 67 | bool state = false; // refers to the most recently sent MIDI message 68 | //e.g. a switch may be latched on when not held down 69 | void setControlNumber(byte num); 70 | void setMode(byte mod); 71 | void outputRange(byte min, byte max); 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/MIDItouch.cpp: -------------------------------------------------------------------------------- 1 | #include "MIDItouch.h" 2 | 3 | // constructors 4 | MIDItouch::MIDItouch() : TouchVariable(){}; 5 | 6 | MIDItouch::MIDItouch(int p, byte num) : TouchVariable(p, 0, 127){ 7 | pin = p; 8 | inputRange(); // only works if creating objects during setup 9 | number = num; 10 | value = 0; 11 | mode = false; // no killSwitch 12 | invert = outLo > outHi; 13 | }; 14 | 15 | MIDItouch::MIDItouch(int p, byte num, byte k) : TouchVariable(p, 0, 127){ 16 | pin = p; 17 | inputRange(); // only works if creating objects during setup 18 | number = num; 19 | value = 0; 20 | mode = true; 21 | killSwitch = k; 22 | invert = outLo > outHi; 23 | }; 24 | 25 | 26 | // destructor 27 | MIDItouch::~MIDItouch(){ 28 | }; 29 | 30 | 31 | int MIDItouch::read(){ 32 | int newValue = TouchVariable::read(); 33 | if (newValue == outHi && value != outHi){ 34 | value = outHi; 35 | return value; 36 | } 37 | else if (newValue == outLo && value != outLo){ 38 | value = outLo; 39 | return value; 40 | } 41 | else if (newValue == value){ 42 | return -1; 43 | } 44 | else{ 45 | value = newValue; 46 | return value; 47 | } 48 | }; 49 | 50 | 51 | int MIDItouch::send(){ 52 | int newValue = read(); 53 | if (mode && newValue > outLo && value == outLo){ //ON before main msg 54 | usbMIDI.sendControlChange(killSwitch, 127, MIDIchannel); 55 | } 56 | if (newValue >= 0){ 57 | usbMIDI.sendControlChange(number, newValue, MIDIchannel);//MAIN MESSAGE 58 | if (mode && newValue == outLo && value >= outLo){//OFF after main 59 | usbMIDI.sendControlChange(killSwitch, 0, MIDIchannel); 60 | } 61 | value = newValue; 62 | } 63 | return newValue; 64 | }; 65 | 66 | 67 | int MIDItouch::send(bool force){ 68 | int newValue = TouchVariable::read(); 69 | if (force){ 70 | usbMIDI.sendControlChange(number, newValue, MIDIchannel); 71 | } 72 | return newValue; 73 | } 74 | 75 | 76 | void MIDItouch::setControlNumber(byte num){ // Set the NOTE number. 77 | number = num; 78 | }; 79 | 80 | // Set upper and lower limits for outgoing MIDI messages. 81 | void MIDItouch::outputRange(byte min, byte max){ 82 | outLo = min; // inherited from TouchVariable 83 | outHi = max; // inherited from TouchVariable 84 | }; 85 | 86 | 87 | void MIDItouch::inputRange(){ 88 | TouchVariable::setInputRange(); 89 | }; 90 | 91 | 92 | void MIDItouch::inputRange(uint16_t min, uint16_t max){ 93 | TouchVariable::setInputRange(min, max); 94 | }; 95 | 96 | void MIDItouch::setKillSwitch(byte k){ 97 | if(k == 0){ 98 | mode = false; 99 | } 100 | else{ 101 | mode = true; 102 | killSwitch = constrain(k, 1, 127); 103 | } 104 | }; -------------------------------------------------------------------------------- /src/MIDItouch.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDItouch_h 2 | #define MIDItouch_h 3 | 4 | #include "Arduino.h" 5 | #include "Flicker.h" 6 | 7 | #define KILL 9 // previously undefined CC# safe for general purpose assignment 8 | #define OFF 0 9 | 10 | extern byte MIDIchannel; 11 | 12 | class MIDItouch: public TouchVariable{ 13 | int pin; 14 | byte invert; 15 | int divider; // for converting from the touchRead range to MIDI (0~127) 16 | 17 | public: 18 | // default constructor 19 | MIDItouch(); 20 | 21 | // constructor when only pin & control number are given 22 | MIDItouch(int p, byte num); 23 | 24 | // " when pin, control number are given and kill switch is enabled 25 | MIDItouch(int p, byte num, byte k); 26 | 27 | // deconstructor 28 | ~MIDItouch(); 29 | 30 | int read(); // read input and return a MIDI value (or -1 if none) 31 | int send(); // calls read(), sends and returns a MIDI value (or -1 if none) 32 | int send(bool force); // forces MIDI output regardless of input 33 | byte number; 34 | byte mode; // In case you need to kill an effect entirely 35 | byte killSwitch = 0; // Which CC is getting its hands dirty? 36 | byte value; 37 | void setControlNumber(byte num); 38 | void outputRange(byte min, byte max); 39 | 40 | // Use inputRange() with no arguments during setup() 41 | // To calculate the range automatically using a call to touchRead() 42 | void inputRange(); 43 | 44 | // Or a specific range can be given. 45 | void inputRange(uint16_t min, uint16_t max); 46 | void setKillSwitch(byte k); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/elapsedMillis.h: -------------------------------------------------------------------------------- 1 | /* Elapsed time types - for easy-to-use measurements of elapsed time 2 | * http://www.pjrc.com/teensy/ 3 | * Copyright (c) 2011 PJRC.COM, LLC 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 13 | * all 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 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #ifndef elapsedMillis_h 25 | #define elapsedMillis_h 26 | #ifdef __cplusplus 27 | 28 | #if ARDUINO >= 100 29 | #include "Arduino.h" 30 | #else 31 | #include "WProgram.h" 32 | #endif 33 | 34 | class elapsedMicros 35 | { 36 | private: 37 | unsigned long us; 38 | public: 39 | elapsedMicros(void) { us = micros(); } 40 | elapsedMicros(unsigned long val) { us = micros() - val; } 41 | elapsedMicros(const elapsedMicros &orig) { us = orig.us; } 42 | operator unsigned long () const { return micros() - us; } 43 | elapsedMicros & operator = (const elapsedMicros &rhs) { us = rhs.us; return *this; } 44 | elapsedMicros & operator = (unsigned long val) { us = micros() - val; return *this; } 45 | elapsedMicros & operator -= (unsigned long val) { us += val ; return *this; } 46 | elapsedMicros & operator += (unsigned long val) { us -= val ; return *this; } 47 | elapsedMicros operator - (int val) const { elapsedMicros r(*this); r.us += val; return r; } 48 | elapsedMicros operator - (unsigned int val) const { elapsedMicros r(*this); r.us += val; return r; } 49 | elapsedMicros operator - (long val) const { elapsedMicros r(*this); r.us += val; return r; } 50 | elapsedMicros operator - (unsigned long val) const { elapsedMicros r(*this); r.us += val; return r; } 51 | elapsedMicros operator + (int val) const { elapsedMicros r(*this); r.us -= val; return r; } 52 | elapsedMicros operator + (unsigned int val) const { elapsedMicros r(*this); r.us -= val; return r; } 53 | elapsedMicros operator + (long val) const { elapsedMicros r(*this); r.us -= val; return r; } 54 | elapsedMicros operator + (unsigned long val) const { elapsedMicros r(*this); r.us -= val; return r; } 55 | }; 56 | 57 | class elapsedMillis 58 | { 59 | private: 60 | unsigned long ms; 61 | public: 62 | elapsedMillis(void) { ms = millis(); } 63 | elapsedMillis(unsigned long val) { ms = millis() - val; } 64 | elapsedMillis(const elapsedMillis &orig) { ms = orig.ms; } 65 | operator unsigned long () const { return millis() - ms; } 66 | elapsedMillis & operator = (const elapsedMillis &rhs) { ms = rhs.ms; return *this; } 67 | elapsedMillis & operator = (unsigned long val) { ms = millis() - val; return *this; } 68 | elapsedMillis & operator -= (unsigned long val) { ms += val ; return *this; } 69 | elapsedMillis & operator += (unsigned long val) { ms -= val ; return *this; } 70 | elapsedMillis operator - (int val) const { elapsedMillis r(*this); r.ms += val; return r; } 71 | elapsedMillis operator - (unsigned int val) const { elapsedMillis r(*this); r.ms += val; return r; } 72 | elapsedMillis operator - (long val) const { elapsedMillis r(*this); r.ms += val; return r; } 73 | elapsedMillis operator - (unsigned long val) const { elapsedMillis r(*this); r.ms += val; return r; } 74 | elapsedMillis operator + (int val) const { elapsedMillis r(*this); r.ms -= val; return r; } 75 | elapsedMillis operator + (unsigned int val) const { elapsedMillis r(*this); r.ms -= val; return r; } 76 | elapsedMillis operator + (long val) const { elapsedMillis r(*this); r.ms -= val; return r; } 77 | elapsedMillis operator + (unsigned long val) const { elapsedMillis r(*this); r.ms -= val; return r; } 78 | }; 79 | 80 | class elapsedSeconds 81 | { 82 | private: 83 | unsigned long s; 84 | public: 85 | elapsedSeconds(void) { s = millis()/1000; } 86 | elapsedSeconds(unsigned long val) { s = millis()/1000 - val; } 87 | elapsedSeconds(const elapsedSeconds &orig) { s = orig.s; } 88 | operator unsigned long () const { return millis()/1000 - s; } 89 | elapsedSeconds & operator = (const elapsedSeconds &rhs) { s = rhs.s; return *this; } 90 | elapsedSeconds & operator = (unsigned long val) { s = millis()/1000 - val; return *this; } 91 | elapsedSeconds & operator -= (unsigned long val) { s += val ; return *this; } 92 | elapsedSeconds & operator += (unsigned long val) { s -= val ; return *this; } 93 | elapsedSeconds operator - (int val) const { elapsedSeconds r(*this); r.s += val; return r; } 94 | elapsedSeconds operator - (unsigned int val) const { elapsedSeconds r(*this); r.s += val; return r; } 95 | elapsedSeconds operator - (long val) const { elapsedSeconds r(*this); r.s += val; return r; } 96 | elapsedSeconds operator - (unsigned long val) const { elapsedSeconds r(*this); r.s += val; return r; } 97 | elapsedSeconds operator + (int val) const { elapsedSeconds r(*this); r.s -= val; return r; } 98 | elapsedSeconds operator + (unsigned int val) const { elapsedSeconds r(*this); r.s -= val; return r; } 99 | elapsedSeconds operator + (long val) const { elapsedSeconds r(*this); r.s -= val; return r; } 100 | elapsedSeconds operator + (unsigned long val) const { elapsedSeconds r(*this); r.s -= val; return r; } 101 | }; 102 | 103 | #endif // __cplusplus 104 | #endif // elapsedMillis_h --------------------------------------------------------------------------------