LCOV - code coverage report
Current view: top level - src/MIDI_Inputs/MCU - SevenSegmentDisplay.hpp (source / functions) Coverage Total Hit
Test: 73449d9b107c772cf65493691543348214e5d5eb Lines: 100.0 % 36 36
Test Date: 2026-06-06 17:44:35 Functions: 100.0 % 8 8
Legend: Lines:     hit not hit

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include <AH/STL/algorithm> // std::fill
       4              : #include <MIDI_Inputs/MIDIInputElement.hpp>
       5              : #include <MIDI_Inputs/MIDIInputElementMatchers.hpp>
       6              : #include <Print.h>
       7              : 
       8              : BEGIN_CS_NAMESPACE
       9              : 
      10              : namespace MCU {
      11              : 
      12              : /// Class that receives and saves the text of a Mackie Control Universal
      13              : /// 7-segment display like the assignment display and the time display.
      14              : ///
      15              : /// @note   Implements Control Change updates only, not System Exclusive.
      16              : ///
      17              : /// @ingroup MIDIInputElements
      18              : template <uint8_t LENGTH>
      19              : class SevenSegmentDisplay
      20              :     : public MatchingMIDIInputElement<MIDIMessageType::ControlChange,
      21              :                                       TwoByteRangeMIDIMatcher>,
      22              :       public Printable {
      23              :   public:
      24              :     using Matcher = TwoByteRangeMIDIMatcher;
      25              :     using Parent =
      26              :         MatchingMIDIInputElement<MIDIMessageType::ControlChange, Matcher>;
      27              : 
      28           15 :     SevenSegmentDisplay(MIDIAddress address) : Parent({address, LENGTH}) {
      29           15 :         fillWithSpaces();
      30           15 :     }
      31              : 
      32            2 :     void reset() override {
      33            2 :         if (!ignoreReset) {
      34            1 :             fillWithSpaces();
      35            1 :             dirty = true;
      36              :         }
      37            2 :     }
      38              : 
      39              :   protected:
      40              :     /// Update a single character.
      41           87 :     void handleUpdate(typename Matcher::Result match) override {
      42           87 :         uint8_t index = LENGTH - 1 - match.index;
      43              :         // MIDI msg: character data → bits 0-5
      44              :         // MIDI msg: decimal point → bit 6 set, no decimal point → bit 6 not set
      45              :         // text:   decimal point → bit 7 set, no decimal point → bit 7 not set
      46           87 :         uint8_t decimalPt = (match.value & 0x40) << 1;
      47           87 :         uint8_t chardata = match.value & 0x3F;
      48           87 :         uint8_t character = chardata >= 0x20 ? chardata : chardata + 0x40;
      49           87 :         character |= decimalPt;
      50           87 :         dirty |= text[index] != character;
      51           87 :         text[index] = character;
      52           87 :     }
      53              : 
      54           48 :     void fillWithSpaces() { std::fill(std::begin(text), std::end(text), ' '); }
      55              : 
      56              :   public:
      57              :     /// @name Data access
      58              :     /// @{
      59              : 
      60              :     /**
      61              :      * @brief   Copy the ASCII text into the given buffer.
      62              :      *
      63              :      * @param[out] buffer
      64              :      *          The destination to write the text to.
      65              :      *          Will be null-terminated.
      66              :      *          Should have a size of at least `length`+1 bytes.
      67              :      * @param[in] offset
      68              :      *          The offset to start copying from (in the source text, the offset
      69              :      *          in the destination buffer is always zero).
      70              :      * @param[in] length
      71              :      *          The number of characters to copy.
      72              :      */
      73           33 :     void getText(char *buffer, uint8_t offset = 0,
      74              :                  uint8_t length = LENGTH) const {
      75           33 :         if (offset >= LENGTH)
      76            1 :             offset = LENGTH - 1;
      77           33 :         if (length > LENGTH - offset)
      78            4 :             length = LENGTH - offset;
      79          205 :         for (uint8_t i = 0; i < length; i++)
      80          172 :             buffer[i] = getCharacterAt(i + offset);
      81           33 :         buffer[length] = '\0';
      82           33 :     }
      83              : 
      84              :     /**
      85              :      * @brief   Get the character at the given index.
      86              :      * @todo    Documentation.
      87              :      */
      88          216 :     char getCharacterAt(uint8_t index) const { return text[index] & 0x7F; }
      89              : 
      90              :     /**
      91              :      * @brief   Copy the decimal points into the given buffer.
      92              :      *
      93              :      * @param[out] buffer
      94              :      *          The destination to write the decimal points to.
      95              :      *          Should have a size of at least `LENGTH` bytes.
      96              :      */
      97              :     void getDecimalPoints(bool *buffer) const {
      98              :         for (uint8_t i = 0; i < LENGTH; i++)
      99              :             buffer[i] = getDecimalPointAt(i);
     100              :     }
     101              : 
     102              :     /**
     103              :      * @brief   Get the decimal point state at the given index.
     104              :      * @todo    Documentation.
     105              :      */
     106           10 :     bool getDecimalPointAt(uint8_t index) const { return text[index] & 0x80; }
     107              : 
     108              :     /// @}
     109              : 
     110              :     /// @name Printing
     111              :     /// @{
     112              : 
     113              :     /// Print out the text of the display to the given Print.
     114            1 :     size_t printTo(Print &printer) const override {
     115            1 :         size_t s = 0;
     116           11 :         for (uint8_t i = 0; i < LENGTH; i++) {
     117           10 :             s += printer.print(getCharacterAt(i));
     118           10 :             if (getDecimalPointAt(i))
     119            5 :                 s += printer.print('.');
     120              :         }
     121            1 :         return s;
     122              :     }
     123              : 
     124              :     /// @}
     125              : 
     126              :     /// @name   Detecting changes
     127              :     /// @{
     128              : 
     129              :     /// Check if the value was updated since the last time the dirty flag was
     130              :     /// cleared.
     131              :     bool getDirty() const { return dirty; }
     132              :     /// Clear the dirty flag.
     133              :     void clearDirty() { dirty = false; }
     134              : 
     135              :     /// @}
     136              : 
     137              :   private:
     138              :     AH::Array<char, LENGTH> text; ///< Non-ASCII and not null-terminated.
     139              :     bool dirty = true;
     140              : 
     141              :   public:
     142              :     /// Don't reset the state when calling the `reset` method. This is the
     143              :     /// default, because in the original MCU, displays don't get reset when a
     144              :     /// Reset All Controllers message is received.
     145              :     bool ignoreReset = true;
     146              : };
     147              : 
     148              : } // namespace MCU
     149              : 
     150              : END_CS_NAMESPACE
        

Generated by: LCOV version 2.4-beta