LCOV - code coverage report
Current view: top level - src/Control_Surface - Control_Surface_Class.cpp (source / functions) Hit Total Coverage
Test: b8a30b4b7040ae1abf162fd0a258beaa2de43626 Lines: 13 143 9.1 %
Date: 2024-12-21 21:28:55 Functions: 3 17 17.6 %
Legend: Lines: hit not hit

          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         598 : Control_Surface_ &Control_Surface_::getInstance() {
      18         598 :     static Control_Surface_ instance;
      19         598 :     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          39 : bool Control_Surface_::connectDefaultMIDI_Interface() {
      48             : #if !DISABLE_PIPES
      49          39 :     if (hasSinkPipe() || hasSourcePipe())
      50           0 :         return false;
      51          39 :     auto def = MIDI_Interface::getDefault();
      52          39 :     if (def == nullptr) {
      53           0 :         FATAL_ERROR(F("No default MIDI Interface"), 0xF123);
      54             :         return false;
      55             :     }
      56          39 :     *this << inpipe << *def;
      57          39 :     *this >> outpipe >> *def;
      58          39 :     return true;
      59             : #else
      60             :     return MIDI_Interface::getDefault();
      61             : #endif
      62             : }
      63             : 
      64           0 : void Control_Surface_::disconnectMIDI_Interfaces() {
      65             : #if !DISABLE_PIPES
      66           0 :     disconnectSinkPipes();
      67           0 :     disconnectSourcePipes();
      68             : #endif
      69           0 : }
      70             : 
      71           0 : void Control_Surface_::loop() {
      72           0 :     ExtendedIOElement::updateAllBufferedInputs();
      73           0 :     Updatable<>::updateAll();
      74           0 :     updateMidiInput();
      75           0 :     updateInputs();
      76           0 :     if (displayTimer)
      77           0 :         updateDisplays();
      78           0 :     ExtendedIOElement::updateAllBufferedOutputs();
      79           0 : }
      80             : 
      81           0 : void Control_Surface_::updateMidiInput() {
      82             : #if !DISABLE_PIPES
      83           0 :     Updatable<MIDI_Interface>::updateAll();
      84             : #else
      85             :     if (auto iface = MIDI_Interface::getDefault()) {
      86             :         MIDIReadEvent event = iface->read();
      87             :         while (event != MIDIReadEvent::NO_MESSAGE) {
      88             :             MIDI_Interface::dispatchIncoming(iface, event);
      89             :             switch (event) {
      90             :                 case MIDIReadEvent::CHANNEL_MESSAGE:
      91             :                     sinkMIDIfromPipe(iface->getChannelMessage());
      92             :                     break;
      93             :                 case MIDIReadEvent::SYSEX_CHUNK: // fallthrough
      94             :                 case MIDIReadEvent::SYSEX_MESSAGE:
      95             :                     sinkMIDIfromPipe(iface->getSysExMessage());
      96             :                     break;
      97             :                 case MIDIReadEvent::SYSCOMMON_MESSAGE:
      98             :                     sinkMIDIfromPipe(iface->getSysCommonMessage());
      99             :                     break;
     100             :                 case MIDIReadEvent::REALTIME_MESSAGE:
     101             :                     sinkMIDIfromPipe(iface->getRealTimeMessage());
     102             :                     break;
     103             :                 case MIDIReadEvent::NO_MESSAGE: break; // LCOV_EXCL_LINE
     104             :                 default: break;                        // LCOV_EXCL_LINE
     105             :             }
     106             :             event = iface->read();
     107             :         }
     108             :     }
     109             : #endif
     110           0 : }
     111             : 
     112             : #if !DISABLE_PIPES
     113         125 : void Control_Surface_::sendChannelMessageImpl(ChannelMessage msg) {
     114         125 :     this->sourceMIDItoPipe(msg);
     115         125 : }
     116           0 : void Control_Surface_::sendSysExImpl(SysExMessage msg) {
     117           0 :     this->sourceMIDItoPipe(msg);
     118           0 : }
     119           0 : void Control_Surface_::sendSysCommonImpl(SysCommonMessage msg) {
     120           0 :     this->sourceMIDItoPipe(msg);
     121           0 : }
     122           0 : void Control_Surface_::sendRealTimeImpl(RealTimeMessage msg) {
     123           0 :     this->sourceMIDItoPipe(msg);
     124           0 : }
     125             : #else
     126             : void Control_Surface_::sendChannelMessageImpl(ChannelMessage msg) {
     127             :     if (auto def = MIDI_Interface::getDefault())
     128             :         def->send(msg);
     129             : }
     130             : void Control_Surface_::sendSysExImpl(SysExMessage msg) {
     131             :     if (auto def = MIDI_Interface::getDefault())
     132             :         def->send(msg);
     133             : }
     134             : void Control_Surface_::sendSysCommonImpl(SysCommonMessage msg) {
     135             :     if (auto def = MIDI_Interface::getDefault())
     136             :         def->send(msg);
     137             : }
     138             : void Control_Surface_::sendRealTimeImpl(RealTimeMessage msg) {
     139             :     if (auto def = MIDI_Interface::getDefault())
     140             :         def->send(msg);
     141             : }
     142             : #endif
     143             : 
     144           0 : void Control_Surface_::sinkMIDIfromPipe(ChannelMessage midimsg) {
     145             : #ifdef DEBUG_MIDI_PACKETS
     146             :     if (midimsg.hasTwoDataBytes())
     147             :         DEBUG(">>> " << hex << midimsg.header << ' ' << midimsg.data1 << ' '
     148             :                      << midimsg.data2 << " (" << midimsg.cable.getOneBased()
     149             :                      << ')' << dec);
     150             :     else
     151             :         DEBUG(">>> " << hex << midimsg.header << ' ' << midimsg.data1 << " ("
     152             :                      << midimsg.cable.getOneBased() << ')' << dec);
     153             : #endif
     154             : 
     155             :     // If the Channel Message callback exists, call it to see if we have to
     156             :     // continue handling it.
     157           0 :     if (channelMessageCallback && channelMessageCallback(midimsg))
     158           0 :         return;
     159             : 
     160           0 :     if (midimsg.getMessageType() == MIDIMessageType::ControlChange &&
     161           0 :         midimsg.getData1() == MIDI_CC::Reset_All_Controllers) {
     162             :         // Reset All Controllers
     163             :         DEBUG(F("Reset All Controllers"));
     164           0 :         MIDIInputElementCC::resetAll();
     165           0 :         MIDIInputElementCP::resetAll();
     166           0 :     } else if (midimsg.getMessageType() == MIDIMessageType::ControlChange &&
     167           0 :                midimsg.getData1() == MIDI_CC::All_Notes_Off) {
     168             :         // All Notes Off
     169           0 :         MIDIInputElementNote::resetAll();
     170             :     } else {
     171           0 :         switch (midimsg.getMessageType()) {
     172           0 :             case MIDIMessageType::None: break;
     173           0 :             case MIDIMessageType::NoteOff: // fallthrough
     174             :             case MIDIMessageType::NoteOn:
     175             :                 DEBUGFN(F("Updating Note elements with new MIDI "
     176             :                           "message."));
     177           0 :                 MIDIInputElementNote::updateAllWith(midimsg);
     178           0 :                 break;
     179           0 :             case MIDIMessageType::KeyPressure:
     180             :                 DEBUGFN(F("Updating Key Pressure elements with new MIDI "
     181             :                           "message."));
     182           0 :                 MIDIInputElementKP::updateAllWith(midimsg);
     183           0 :                 break;
     184           0 :             case MIDIMessageType::ControlChange:
     185             :                 DEBUGFN(F("Updating CC elements with new MIDI "
     186             :                           "message."));
     187           0 :                 MIDIInputElementCC::updateAllWith(midimsg);
     188           0 :                 break;
     189           0 :             case MIDIMessageType::ProgramChange:
     190             :                 DEBUGFN(F("Updating Program Change elements with new MIDI "
     191             :                           "message."));
     192           0 :                 MIDIInputElementPC::updateAllWith(midimsg);
     193           0 :                 break;
     194           0 :             case MIDIMessageType::ChannelPressure:
     195             :                 DEBUGFN(F("Updating Channel Pressure elements with new MIDI "
     196             :                           "message."));
     197           0 :                 MIDIInputElementCP::updateAllWith(midimsg);
     198           0 :                 break;
     199           0 :             case MIDIMessageType::PitchBend:
     200             :                 // Channel Pressure
     201             :                 DEBUGFN(F("Updating Pitch Bend elements with new MIDI "
     202             :                           "message."));
     203           0 :                 MIDIInputElementPB::updateAllWith(midimsg);
     204           0 :                 break;
     205             : 
     206             :             // These MIDI types are not channel messages, so aren't handled here
     207             :             // LCOV_EXCL_START
     208             :             case MIDIMessageType::SysExStart: break;
     209             :             case MIDIMessageType::MTCQuarterFrame: break;
     210             :             case MIDIMessageType::SongPositionPointer: break;
     211             :             case MIDIMessageType::SongSelect: break;
     212             :             case MIDIMessageType::UndefinedSysCommon1: break;
     213             :             case MIDIMessageType::UndefinedSysCommon2: break;
     214             :             case MIDIMessageType::TuneRequest: break;
     215             :             case MIDIMessageType::SysExEnd: break;
     216             :             case MIDIMessageType::TimingClock: break;
     217             :             case MIDIMessageType::UndefinedRealTime1: break;
     218             :             case MIDIMessageType::Start: break;
     219             :             case MIDIMessageType::Continue: break;
     220             :             case MIDIMessageType::Stop: break;
     221             :             case MIDIMessageType::UndefinedRealTime2: break;
     222             :             case MIDIMessageType::ActiveSensing: break;
     223             :             case MIDIMessageType::SystemReset: break;
     224             :             default:
     225             :                 break;
     226             :                 // LCOV_EXCL_STOP
     227             :         }
     228             :     }
     229             : }
     230             : 
     231           0 : void Control_Surface_::sinkMIDIfromPipe(SysExMessage msg) {
     232             : #ifdef DEBUG_MIDI_PACKETS
     233             :     const uint8_t *data = msg.data;
     234             :     size_t len = msg.length;
     235             :     DEBUG_OUT << ">>> " << hex;
     236             :     for (size_t i = 0; i < len; i++)
     237             :         DEBUG_OUT << data[i] << ' ';
     238             :     DEBUG_OUT << " (" << msg.cable << ')' << dec << endl;
     239             : #endif
     240             :     // If the SysEx Message callback exists, call it to see if we have to
     241             :     // continue handling it.
     242           0 :     if (sysExMessageCallback && sysExMessageCallback(msg))
     243           0 :         return;
     244           0 :     MIDIInputElementSysEx::updateAllWith(msg);
     245             : }
     246             : 
     247           0 : void Control_Surface_::sinkMIDIfromPipe(SysCommonMessage msg) {
     248             : #ifdef DEBUG_MIDI_PACKETS
     249             :     DEBUG_OUT << ">>> " << hex << msg.getMessageType() << ' ' << msg.getData1()
     250             :               << ' ' << msg.getData2() << " (" << msg.cable << ')' << dec
     251             :               << endl;
     252             : #endif
     253             :     // If the SysEx Message callback exists, call it to see if we have to
     254             :     // continue handling it.
     255           0 :     if (sysCommonMessageCallback && sysCommonMessageCallback(msg))
     256           0 :         return;
     257             : }
     258             : 
     259           0 : void Control_Surface_::sinkMIDIfromPipe(RealTimeMessage rtMessage) {
     260             : #ifdef DEBUG_MIDI_PACKETS
     261             :     DEBUG(">>> " << hex << rtMessage.message << " ("
     262             :                  << rtMessage.cable.getOneBased() << ')' << dec);
     263             : #endif
     264             : 
     265             :     // If the Real-Time Message callback exists, call it to see if we have to
     266             :     // continue handling it.
     267           0 :     if (realTimeMessageCallback && realTimeMessageCallback(rtMessage))
     268           0 :         return;
     269             : }
     270             : 
     271           0 : void Control_Surface_::updateInputs() {
     272           0 :     MIDIInputElementNote::updateAll();
     273           0 :     MIDIInputElementKP::updateAll();
     274           0 :     MIDIInputElementCC::updateAll();
     275           0 :     MIDIInputElementPC::updateAll();
     276           0 :     MIDIInputElementCP::updateAll();
     277           0 :     MIDIInputElementPB::updateAll();
     278           0 :     MIDIInputElementSysEx::updateAll();
     279           0 : }
     280             : 
     281           0 : void Control_Surface_::beginDisplays() {
     282           0 :     auto &allElements = DisplayElement::getAll();
     283           0 :     auto it = allElements.begin();
     284           0 :     auto end = allElements.end();
     285           0 :     if (it == end)
     286           0 :         return;
     287           0 :     auto previousDisplay = &it->getDisplay();
     288             :     // Loop over all display elements
     289             :     while (true) {
     290           0 :         ++it;
     291             :         // If this is the first element on another display
     292           0 :         if (it == end || &it->getDisplay() != previousDisplay) {
     293             :             // Initialize the display
     294           0 :             previousDisplay->begin();
     295           0 :             if (it == end)
     296           0 :                 break;
     297           0 :             previousDisplay = &it->getDisplay();
     298             :         }
     299             :     }
     300             : }
     301             : 
     302           0 : void Control_Surface_::updateDisplays() {
     303           0 :     auto &allElements = DisplayElement::getAll();
     304           0 :     auto it = allElements.begin();
     305           0 :     auto end = allElements.end();
     306           0 :     if (it == end)
     307           0 :         return;
     308           0 :     auto prevIt = it;
     309           0 :     auto previousDisplay = &prevIt->getDisplay();
     310           0 :     bool dirty = false;
     311             :     // Loop over all display elements
     312             :     while (true) {
     313           0 :         dirty |= it->getDirty();
     314           0 :         ++it;
     315             :         // If this is the first element on another display
     316           0 :         if (it == end || &it->getDisplay() != previousDisplay) {
     317             :             // If there was at least one element on the previous display that
     318             :             // has to be redrawn
     319           0 :             if (dirty) {
     320             :                 // Clear the display
     321           0 :                 previousDisplay->clearAndDrawBackground();
     322             :                 // Update all elements on that display
     323           0 :                 for (auto drawIt = prevIt; drawIt != it; ++drawIt)
     324           0 :                     drawIt->draw();
     325             :                 // Write the buffer to the display
     326           0 :                 previousDisplay->display();
     327             :             }
     328           0 :             if (it == end)
     329           0 :                 break;
     330           0 :             prevIt = it;
     331           0 :             previousDisplay = &it->getDisplay();
     332           0 :             dirty = false;
     333             :         }
     334           0 :     }
     335             : }
     336             : 
     337             : #if CS_TRUE_CONTROL_SURFACE_INSTANCE || defined(DOXYGEN)
     338             : Control_Surface_ &Control_Surface = Control_Surface_::getInstance();
     339             : #endif
     340             : 
     341             : END_CS_NAMESPACE

Generated by: LCOV version 1.15