LCOV - code coverage report
Current view: top level - src/Control_Surface - Control_Surface_Class.cpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 9.1 % 143 13
Test Date: 2026-06-06 17:44:35 Functions: 17.6 % 17 3
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 2.4-beta