LCOV - code coverage report
Current view: top level - src/midi - midi_manager.cpp (source / functions) Coverage Total Hit
Test: merged.info Lines: 54.7 % 86 47
Test Date: 2026-06-01 11:15:25 Functions: 87.5 % 8 7

            Line data    Source code
       1              : #include "midi/midi_manager.h"
       2              : #ifndef __EMSCRIPTEN__
       3              : #include <rtmidi/RtMidi.h>
       4              : #endif
       5              : 
       6              : #include <iostream>
       7              : 
       8              : namespace Amplitron {
       9              : 
      10              : // ---------------------------------------------------------------------------
      11              : // Construction / destruction
      12              : // ---------------------------------------------------------------------------
      13              : 
      14          372 : MidiManager::MidiManager() = default;
      15              : 
      16          372 : MidiManager::~MidiManager() {
      17          279 :     shutdown();
      18          372 : }
      19              : 
      20              : // ---------------------------------------------------------------------------
      21              : // RtMidi callback — runs on RtMidi's internal thread, must be lock-free
      22              : // ---------------------------------------------------------------------------
      23              : 
      24              : #ifdef __EMSCRIPTEN__
      25              : void MidiManager::midi_callback(double /*timestamp*/,
      26              :                                 std::vector<unsigned char>* /*message*/,
      27              :                                 void* /*user_data*/) {
      28              : }
      29              : 
      30              : bool MidiManager::initialize() { return true; }
      31              : void MidiManager::shutdown() { close_port(); }
      32              : std::vector<std::string> MidiManager::get_available_ports() const { return {}; }
      33              : bool MidiManager::open_port(int /*port_index*/) { return false; }
      34              : void MidiManager::close_port() { current_port_ = -1; current_port_name_.clear(); }
      35              : #else
      36            0 : void MidiManager::midi_callback(double /*timestamp*/,
      37              :                                 std::vector<unsigned char>* message,
      38              :                                 void* user_data) {
      39            0 :     if (!message || message->size() < 3) return;
      40              : 
      41            0 :     auto* self = static_cast<MidiManager*>(user_data);
      42            0 :     uint8_t status = (*message)[0];
      43              : 
      44              :     // Only handle Control Change messages (0xB0 .. 0xBF)
      45            0 :     if ((status & 0xF0) != 0xB0) return;
      46              : 
      47            0 :     MidiEvent event{};
      48            0 :     event.status = status;
      49            0 :     event.data1  = (*message)[1];  // CC number
      50            0 :     event.data2  = (*message)[2];  // CC value
      51            0 :     self->midi_queue_.try_push(event);  // Drop if full — acceptable for CC
      52              : }
      53              : 
      54              : // ---------------------------------------------------------------------------
      55              : // Port management
      56              : // ---------------------------------------------------------------------------
      57              : 
      58           15 : bool MidiManager::initialize() {
      59           15 :     if (midi_in_) return true;  // Already initialized
      60              : 
      61            5 :     try {
      62           25 :         auto* rt = new RtMidiIn(RtMidi::UNSPECIFIED, "Amplitron MIDI");
      63           10 :         rt->ignoreTypes(true, true, true);  // Ignore SysEx, timing, active sensing
      64           10 :         midi_in_ = rt;
      65           10 :     } catch (const RtMidiError& e) {
      66            5 :         std::cerr << "[MidiManager] RtMidi init failed: " << e.getMessage() << "\n";
      67            5 :         return false;
      68            5 :     }
      69              : 
      70              :     // Auto-open the first available port (if any)
      71           10 :     auto ports = get_available_ports();
      72           10 :     if (!ports.empty()) {
      73            0 :         open_port(0);
      74            0 :     }
      75           10 :     return true;
      76           10 : }
      77              : 
      78          297 : void MidiManager::shutdown() {
      79          297 :     close_port();
      80          297 :     if (midi_in_) {
      81           10 :         delete static_cast<RtMidiIn*>(midi_in_);
      82           10 :         midi_in_ = nullptr;
      83            5 :     }
      84          297 : }
      85              : 
      86           22 : std::vector<std::string> MidiManager::get_available_ports() const {
      87           22 :     std::vector<std::string> result;
      88           22 :     if (!midi_in_) return result;
      89              : 
      90           12 :     auto* rt = static_cast<RtMidiIn*>(midi_in_);
      91            6 :     try {
      92           12 :         unsigned int count = rt->getPortCount();
      93           12 :         result.reserve(count);
      94           12 :         for (unsigned int i = 0; i < count; ++i) {
      95            0 :             result.push_back(rt->getPortName(i));
      96            0 :         }
      97            6 :     } catch (const RtMidiError& e) {
      98            0 :         std::cerr << "[MidiManager] Failed to enumerate MIDI ports: "
      99            0 :                   << e.getMessage() << "\n";
     100            0 :     }
     101            6 :     return result;
     102            9 : }
     103              : 
     104           12 : bool MidiManager::open_port(int port_index) {
     105           12 :     if (!midi_in_) return false;
     106              : 
     107            6 :     close_port();
     108              : 
     109            6 :     auto* rt = static_cast<RtMidiIn*>(midi_in_);
     110              : 
     111            3 :     try {
     112            6 :         unsigned int count = rt->getPortCount();
     113            6 :         if (port_index < 0 || static_cast<unsigned int>(port_index) >= count) return false;
     114              : 
     115            0 :         rt->setCallback(&MidiManager::midi_callback, this);
     116            0 :         rt->openPort(static_cast<unsigned int>(port_index), "Amplitron In");
     117            0 :         current_port_ = port_index;
     118            0 :         current_port_name_ = rt->getPortName(static_cast<unsigned int>(port_index));
     119            0 :         return true;
     120            0 :     } catch (const RtMidiError& e) {
     121            0 :         std::cerr << "[MidiManager] Failed to open port " << port_index
     122            0 :                   << ": " << e.getMessage() << "\n";
     123              :         // Ensure port is closed on error
     124            0 :         try {
     125            0 :             rt->closePort();
     126            0 :         } catch (...) {}
     127            0 :         current_port_ = -1;
     128            0 :         current_port_name_.clear();
     129            0 :         return false;
     130            0 :     }
     131            4 : }
     132              : 
     133          309 : void MidiManager::close_port() {
     134          309 :     if (!midi_in_ || current_port_ < 0) return;
     135              : 
     136            0 :     auto* rt = static_cast<RtMidiIn*>(midi_in_);
     137            0 :     try {
     138            0 :         rt->cancelCallback();
     139            0 :         rt->closePort();
     140            0 :     } catch (...) {}
     141            0 :     current_port_ = -1;
     142            0 :     current_port_name_.clear();
     143          104 : }
     144              : #endif
     145              : 
     146              : } // namespace Amplitron
        

Generated by: LCOV version 2.0-1