Line data Source code
1 : #include "Control_Surface_Class.hpp" 2 : #include <AH/Debug/Debug.hpp> 3 : #include <AH/Hardware/ExtendedInputOutput/ExtendedIOElement.hpp> 4 : #include <AH/Hardware/FilteredAnalog.hpp> 5 : #include <MIDI_Constants/Control_Change.hpp> 6 : #include <MIDI_Inputs/MIDIInputElement.hpp> 7 : #include <MIDI_Interfaces/DebugMIDI_Interface.hpp> 8 : #include <MIDI_Outputs/Abstract/MIDIOutputElement.hpp> 9 : #include <Selectors/Selector.hpp> 10 : 11 : #include <AH/Arduino-Wrapper.h> 12 : 13 : BEGIN_CS_NAMESPACE 14 : 15 : using AH::ExtendedIOElement; 16 : 17 584 : Control_Surface_ &Control_Surface_::getInstance() { 18 584 : static Control_Surface_ instance; 19 584 : return instance; 20 : } 21 : 22 0 : void Control_Surface_::begin() { 23 : #if defined(ARDUINO) && defined(DEBUG_OUT) 24 : DEBUG_OUT.begin(AH::defaultBaudRate); 25 : delay(250); 26 : #endif 27 : 28 0 : connectDefaultMIDI_Interface(); 29 : 30 0 : FilteredAnalog<>::setupADC(); 31 0 : ExtendedIOElement::beginAll(); 32 0 : Updatable<MIDI_Interface>::beginAll(); 33 0 : MIDIOutputOnly::beginAll(); 34 0 : beginDisplays(); 35 0 : MIDIInputElementNote::beginAll(); 36 0 : MIDIInputElementKP::beginAll(); 37 0 : MIDIInputElementCC::beginAll(); 38 0 : MIDIInputElementPC::beginAll(); 39 0 : MIDIInputElementCP::beginAll(); 40 0 : MIDIInputElementPB::beginAll(); 41 0 : MIDIInputElementSysEx::beginAll(); 42 0 : Updatable<>::beginAll(); 43 0 : Updatable<Display>::beginAll(); 44 0 : displayTimer.begin(); 45 0 : } 46 : 47 38 : bool Control_Surface_::connectDefaultMIDI_Interface() { 48 38 : if (hasSinkPipe() || hasSourcePipe()) 49 0 : return false; 50 38 : auto def = MIDI_Interface::getDefault(); 51 38 : if (def == nullptr) { 52 0 : FATAL_ERROR(F("No default MIDI Interface"), 0xF123); 53 : return false; 54 : } 55 38 : *this << inpipe << *def; 56 38 : *this >> outpipe >> *def; 57 38 : return true; 58 : } 59 : 60 0 : void Control_Surface_::disconnectMIDI_Interfaces() { 61 0 : disconnectSinkPipes(); 62 0 : disconnectSourcePipes(); 63 0 : } 64 : 65 0 : void Control_Surface_::loop() { 66 0 : ExtendedIOElement::updateAllBufferedInputs(); 67 0 : Updatable<>::updateAll(); 68 0 : updateMidiInput(); 69 0 : updateInputs(); 70 0 : if (displayTimer) 71 0 : updateDisplays(); 72 0 : ExtendedIOElement::updateAllBufferedOutputs(); 73 0 : } 74 : 75 0 : void Control_Surface_::updateMidiInput() { 76 0 : Updatable<MIDI_Interface>::updateAll(); 77 0 : } 78 : 79 124 : void Control_Surface_::sendChannelMessageImpl(ChannelMessage msg) { 80 124 : this->sourceMIDItoPipe(msg); 81 124 : } 82 0 : void Control_Surface_::sendSysExImpl(SysExMessage msg) { 83 0 : this->sourceMIDItoPipe(msg); 84 0 : } 85 0 : void Control_Surface_::sendSysCommonImpl(SysCommonMessage msg) { 86 0 : this->sourceMIDItoPipe(msg); 87 0 : } 88 0 : void Control_Surface_::sendRealTimeImpl(RealTimeMessage msg) { 89 0 : this->sourceMIDItoPipe(msg); 90 0 : } 91 : 92 0 : void Control_Surface_::sinkMIDIfromPipe(ChannelMessage midimsg) { 93 : #ifdef DEBUG_MIDI_PACKETS 94 : if (midimsg.hasTwoDataBytes()) 95 : DEBUG(">>> " << hex << midimsg.header << ' ' << midimsg.data1 << ' ' 96 : << midimsg.data2 << " (" << midimsg.cable.getOneBased() 97 : << ')' << dec); 98 : else 99 : DEBUG(">>> " << hex << midimsg.header << ' ' << midimsg.data1 << " (" 100 : << midimsg.cable.getOneBased() << ')' << dec); 101 : #endif 102 : 103 : // If the Channel Message callback exists, call it to see if we have to 104 : // continue handling it. 105 0 : if (channelMessageCallback && channelMessageCallback(midimsg)) 106 0 : return; 107 : 108 0 : if (midimsg.getMessageType() == MIDIMessageType::CONTROL_CHANGE && 109 0 : midimsg.getData1() == MIDI_CC::Reset_All_Controllers) { 110 : // Reset All Controllers 111 : DEBUG(F("Reset All Controllers")); 112 0 : MIDIInputElementCC::resetAll(); 113 0 : MIDIInputElementCP::resetAll(); 114 0 : } else if (midimsg.getMessageType() == MIDIMessageType::CONTROL_CHANGE && 115 0 : midimsg.getData1() == MIDI_CC::All_Notes_Off) { 116 : // All Notes Off 117 0 : MIDIInputElementNote::resetAll(); 118 : } else { 119 0 : switch (midimsg.getMessageType()) { 120 0 : case MIDIMessageType::NONE: break; 121 0 : case MIDIMessageType::NOTE_OFF: // fallthrough 122 : case MIDIMessageType::NOTE_ON: 123 : DEBUGFN(F("Updating Note elements with new MIDI " 124 : "message.")); 125 0 : MIDIInputElementNote::updateAllWith(midimsg); 126 0 : break; 127 0 : case MIDIMessageType::KEY_PRESSURE: 128 : DEBUGFN(F("Updating Key Pressure elements with new MIDI " 129 : "message.")); 130 0 : MIDIInputElementKP::updateAllWith(midimsg); 131 0 : break; 132 0 : case MIDIMessageType::CONTROL_CHANGE: 133 : DEBUGFN(F("Updating CC elements with new MIDI " 134 : "message.")); 135 0 : MIDIInputElementCC::updateAllWith(midimsg); 136 0 : break; 137 0 : case MIDIMessageType::PROGRAM_CHANGE: 138 : DEBUGFN(F("Updating Program Change elements with new MIDI " 139 : "message.")); 140 0 : MIDIInputElementPC::updateAllWith(midimsg); 141 0 : break; 142 0 : case MIDIMessageType::CHANNEL_PRESSURE: 143 : DEBUGFN(F("Updating Channel Pressure elements with new MIDI " 144 : "message.")); 145 0 : MIDIInputElementCP::updateAllWith(midimsg); 146 0 : break; 147 0 : case MIDIMessageType::PITCH_BEND: 148 : // Channel Pressure 149 : DEBUGFN(F("Updating Pitch Bend elements with new MIDI " 150 : "message.")); 151 0 : MIDIInputElementPB::updateAllWith(midimsg); 152 0 : break; 153 : 154 : // These MIDI types are not channel messages, so aren't handled here 155 : // LCOV_EXCL_START 156 : case MIDIMessageType::SYSEX_START: break; 157 : case MIDIMessageType::MTC_QUARTER_FRAME: break; 158 : case MIDIMessageType::SONG_POSITION_POINTER: break; 159 : case MIDIMessageType::SONG_SELECT: break; 160 : case MIDIMessageType::UNDEFINED_SYSCOMMON_1: break; 161 : case MIDIMessageType::UNDEFINED_SYSCOMMON_2: break; 162 : case MIDIMessageType::TUNE_REQUEST: break; 163 : case MIDIMessageType::SYSEX_END: break; 164 : case MIDIMessageType::TIMING_CLOCK: break; 165 : case MIDIMessageType::UNDEFINED_REALTIME_1: break; 166 : case MIDIMessageType::START: break; 167 : case MIDIMessageType::CONTINUE: break; 168 : case MIDIMessageType::STOP: break; 169 : case MIDIMessageType::UNDEFINED_REALTIME_2: break; 170 : case MIDIMessageType::ACTIVE_SENSING: break; 171 : case MIDIMessageType::SYSTEM_RESET: break; 172 : default: 173 : break; 174 : // LCOV_EXCL_STOP 175 : } 176 : } 177 : } 178 : 179 0 : void Control_Surface_::sinkMIDIfromPipe(SysExMessage msg) { 180 : #ifdef DEBUG_MIDI_PACKETS 181 : const uint8_t *data = msg.data; 182 : size_t len = msg.length; 183 : DEBUG_OUT << ">>> " << hex; 184 : for (size_t i = 0; i < len; i++) 185 : DEBUG_OUT << data[i] << ' '; 186 : DEBUG_OUT << " (" << msg.cable << ')' << dec << endl; 187 : #endif 188 : // If the SysEx Message callback exists, call it to see if we have to 189 : // continue handling it. 190 0 : if (sysExMessageCallback && sysExMessageCallback(msg)) 191 0 : return; 192 0 : MIDIInputElementSysEx::updateAllWith(msg); 193 : } 194 : 195 0 : void Control_Surface_::sinkMIDIfromPipe(SysCommonMessage msg) { 196 : #ifdef DEBUG_MIDI_PACKETS 197 : DEBUG_OUT << ">>> " << hex << msg.getMessageType() << ' ' << msg.getData1() 198 : << ' ' << msg.getData2() << " (" << msg.cable << ')' << dec 199 : << endl; 200 : #endif 201 : // If the SysEx Message callback exists, call it to see if we have to 202 : // continue handling it. 203 0 : if (sysCommonMessageCallback && sysCommonMessageCallback(msg)) 204 0 : return; 205 : } 206 : 207 0 : void Control_Surface_::sinkMIDIfromPipe(RealTimeMessage rtMessage) { 208 : #ifdef DEBUG_MIDI_PACKETS 209 : DEBUG(">>> " << hex << rtMessage.message << " (" 210 : << rtMessage.cable.getOneBased() << ')' << dec); 211 : #endif 212 : 213 : // If the Real-Time Message callback exists, call it to see if we have to 214 : // continue handling it. 215 0 : if (realTimeMessageCallback && realTimeMessageCallback(rtMessage)) 216 0 : return; 217 : } 218 : 219 0 : void Control_Surface_::updateInputs() { 220 0 : MIDIInputElementNote::updateAll(); 221 0 : MIDIInputElementKP::updateAll(); 222 0 : MIDIInputElementCC::updateAll(); 223 0 : MIDIInputElementPC::updateAll(); 224 0 : MIDIInputElementCP::updateAll(); 225 0 : MIDIInputElementPB::updateAll(); 226 0 : MIDIInputElementSysEx::updateAll(); 227 0 : } 228 : 229 0 : void Control_Surface_::beginDisplays() { 230 0 : auto &allElements = DisplayElement::getAll(); 231 0 : auto it = allElements.begin(); 232 0 : auto end = allElements.end(); 233 0 : if (it == end) 234 0 : return; 235 0 : auto previousDisplay = &it->getDisplay(); 236 : // Loop over all display elements 237 : while (true) { 238 0 : ++it; 239 : // If this is the first element on another display 240 0 : if (it == end || &it->getDisplay() != previousDisplay) { 241 : // Initialize the display 242 0 : previousDisplay->begin(); 243 0 : if (it == end) 244 0 : break; 245 0 : previousDisplay = &it->getDisplay(); 246 : } 247 : } 248 : } 249 : 250 0 : void Control_Surface_::updateDisplays() { 251 0 : auto &allElements = DisplayElement::getAll(); 252 0 : auto it = allElements.begin(); 253 0 : auto end = allElements.end(); 254 0 : if (it == end) 255 0 : return; 256 0 : auto prevIt = it; 257 0 : auto previousDisplay = &prevIt->getDisplay(); 258 0 : bool dirty = false; 259 : // Loop over all display elements 260 : while (true) { 261 0 : dirty |= it->getDirty(); 262 0 : ++it; 263 : // If this is the first element on another display 264 0 : if (it == end || &it->getDisplay() != previousDisplay) { 265 : // If there was at least one element on the previous display that 266 : // has to be redrawn 267 0 : if (dirty) { 268 : // Clear the display 269 0 : previousDisplay->clearAndDrawBackground(); 270 : // Update all elements on that display 271 0 : for (auto drawIt = prevIt; drawIt != it; ++drawIt) 272 0 : drawIt->draw(); 273 : // Write the buffer to the display 274 0 : previousDisplay->display(); 275 : } 276 0 : if (it == end) 277 0 : break; 278 0 : prevIt = it; 279 0 : previousDisplay = &it->getDisplay(); 280 0 : dirty = false; 281 : } 282 0 : } 283 : } 284 : 285 : #if CS_TRUE_CONTROL_SURFACE_INSTANCE || defined(DOXYGEN) 286 : Control_Surface_ &Control_Surface = Control_Surface_::getInstance(); 287 : #endif 288 : 289 : END_CS_NAMESPACE