LCOV - code coverage report
Current view: top level - src/MIDI_Inputs/MCU - SevenSegmentDisplay.hpp (source / functions) Hit Total Coverage
Test: b8a30b4b7040ae1abf162fd0a258beaa2de43626 Lines: 36 36 100.0 %
Date: 2024-12-21 21:28:55 Functions: 8 8 100.0 %
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          16 :     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 1.15