LCOV - code coverage report
Current view: top level - src/midi - midi_manager.h (source / functions) Coverage Total Hit
Test: merged.info Lines: 85.7 % 7 6
Test Date: 2026-06-07 15:51:50 Functions: 85.7 % 7 6

            Line data    Source code
       1              : #pragma once
       2              : #include "audio/utils/spsc_queue.h"
       3              : #include "midi/i_midi_manager.h"
       4              : 
       5              : namespace Amplitron {
       6              : 
       7              : #ifdef AMPLITRON_NO_MIDI
       8              : 
       9              : // Stub implementation for non-desktop platforms (web, mobile)
      10              : class MidiManager : public IMidiManager {
      11              :     friend class TestAccessor;
      12              : 
      13              :    public:
      14              :     MidiManager() = default;
      15              :     ~MidiManager() = default;
      16              : 
      17              :     bool initialize() override { return false; }
      18              :     void shutdown() override {}
      19              : 
      20              :     std::vector<std::string> get_available_ports() const override { return {}; }
      21              :     bool open_port(int) override { return false; }
      22              :     void close_port() override {}
      23              :     int current_port() const override { return -1; }
      24              :     std::string current_port_name() const override { return ""; }
      25              :     bool is_port_open() const override { return false; }
      26              : 
      27              :     void add_mapping(const MidiMapping&) override {}
      28              :     void remove_mapping(int) override {}
      29              :     void remove_mapping_for_param(const std::string&, const std::string&) override {}
      30              :     void clear_mappings() override {}
      31              :     const std::vector<MidiMapping>& mappings() const override {
      32              :         static std::vector<MidiMapping> empty;
      33              :         return empty;
      34              :     }
      35              : 
      36              :     void install_default_mappings() override {}
      37              : 
      38              :     void start_learn(MidiTargetType, const std::string&, const std::string&) override {}
      39              :     void cancel_learn() override {}
      40              :     bool is_learning() const override { return false; }
      41              :     std::string learn_status() const override { return ""; }
      42              :     const std::string& learn_effect_name() const override {
      43              :         static std::string empty;
      44              :         return empty;
      45              :     }
      46              :     const std::string& learn_param_name() const override {
      47              :         static std::string empty;
      48              :         return empty;
      49              :     }
      50              : 
      51              :     void poll(IAudioEngine&) override {}
      52              :     void save_config() const override {}
      53              :     void load_config() override {}
      54              : 
      55              :     void inject_event(const MidiEvent&) override {}
      56              : };
      57              : 
      58              : #else
      59              : 
      60              : /**
      61              :  * @brief MIDI input manager with CC-to-parameter mapping and MIDI learn.
      62              :  *
      63              :  * Runs a lock-free SPSC queue between RtMidi's callback thread and the
      64              :  * GUI thread. The GUI thread calls poll() each frame to drain events and
      65              :  * route CC values through the existing engine.push_param_change() path.
      66              :  */
      67              : class MidiManager : public IMidiManager {
      68              :     friend class TestAccessor;
      69              : 
      70              :    public:
      71              :     MidiManager();
      72              :     ~MidiManager();
      73              : 
      74              :     /** @brief Open the first available MIDI input port. @return true on success. */
      75              :     bool initialize() override;
      76              : 
      77              :     /** @brief Close the MIDI port and release resources. */
      78              :     void shutdown() override;
      79              : 
      80              :     // --- Port management ---
      81              : 
      82              :     /** @brief List available MIDI input port names. */
      83              :     std::vector<std::string> get_available_ports() const override;
      84              : 
      85              :     /** @brief Open a specific MIDI input port by index. @return true on success. */
      86              :     bool open_port(int port_index) override;
      87              : 
      88              :     /** @brief Close the currently open port. */
      89              :     void close_port() override;
      90              : 
      91              :     /** @brief Return the index of the currently open port, or -1 if none. */
      92            0 :     int current_port() const override { return current_port_; }
      93              : 
      94              :     /** @brief Return the name of the currently open port, or empty string. */
      95            6 :     std::string current_port_name() const override { return current_port_name_; }
      96              : 
      97              :     /** @brief Return true if a MIDI port is currently open. */
      98           18 :     bool is_port_open() const override { return current_port_ >= 0; }
      99              : 
     100              :     // --- Mapping management ---
     101              : 
     102              :     void add_mapping(const MidiMapping& mapping) override;
     103              :     void remove_mapping(int index) override;
     104              :     void remove_mapping_for_param(const std::string& effect_name,
     105              :                                   const std::string& param_name) override;
     106              :     void clear_mappings() override;
     107          783 :     const std::vector<MidiMapping>& mappings() const override { return mappings_; }
     108              : 
     109              :     /** @brief Install default CC mappings (CC7, CC11, CC64, CC74). */
     110              :     void install_default_mappings() override;
     111              : 
     112              :     // --- MIDI Learn ---
     113              : 
     114              :     /**
     115              :      * @brief Enter learn mode: the next CC event received will be bound to the given target.
     116              :      */
     117              :     void start_learn(MidiTargetType type, const std::string& effect_name,
     118              :                      const std::string& param_name) override;
     119              : 
     120              :     /** @brief Cancel learn mode without creating a mapping. */
     121              :     void cancel_learn() override;
     122              : 
     123              :     /** @brief Return true if learn mode is active. */
     124         1485 :     bool is_learning() const override { return learn_active_; }
     125              : 
     126              :     /** @brief Human-readable status for the learn indicator, or empty. */
     127              :     std::string learn_status() const override;
     128              : 
     129           18 :     const std::string& learn_effect_name() const override { return learn_effect_name_; }
     130           18 :     const std::string& learn_param_name() const override { return learn_param_name_; }
     131              : 
     132              :     // --- Poll (called from GUI thread each frame) ---
     133              : 
     134              :     /**
     135              :      * @brief Drain the MIDI event queue and apply CC mappings.
     136              :      *
     137              :      * For each CC event:
     138              :      * - If learn mode is active, captures the CC and creates a mapping.
     139              :      * - Otherwise, resolves the mapping target and pushes the value to the engine.
     140              :      */
     141              :     void poll(IAudioEngine& engine) override;
     142              : 
     143              :     // --- Persistence ---
     144              : 
     145              :     /** @brief Save mappings and port preference to midi_config.json. */
     146              :     void save_config() const override;
     147              : 
     148              :     /** @brief Load mappings and port preference from midi_config.json. */
     149              :     void load_config() override;
     150              : 
     151              :     /**
     152              :      * @brief Push a MIDI event into the queue from test code.
     153              :      *
     154              :      * This is public so unit tests can inject events without hardware.
     155              :      */
     156              :     void inject_event(const MidiEvent& event) override;
     157              : 
     158              :    private:
     159              :     static void midi_callback(double timestamp, std::vector<unsigned char>* message,
     160              :                               void* user_data);
     161              : 
     162              :     static std::string get_config_path();
     163              : 
     164              :     void* midi_in_ = nullptr;  // RtMidiIn* (opaque to avoid header dependency)
     165              :     int current_port_ = -1;
     166              :     std::string current_port_name_;
     167              : 
     168              :     SPSCQueue<MidiEvent, 256> midi_queue_;
     169              :     std::vector<MidiMapping> mappings_;
     170              : 
     171              :     // Learn state
     172              :     bool learn_active_ = false;
     173              :     MidiTargetType learn_target_type_ = MidiTargetType::EffectParam;
     174              :     std::string learn_effect_name_;
     175              :     std::string learn_param_name_;
     176              : 
     177              :     // Helpers
     178              :     void apply_mapping(const MidiMapping& mapping, int cc_value, IAudioEngine& engine);
     179              :     std::string mappings_to_json() const;
     180              :     bool mappings_from_json(const std::string& json);
     181              : };
     182              : 
     183              : #endif  // AMPLITRON_NO_MIDI
     184              : 
     185              : }  // namespace Amplitron
        

Generated by: LCOV version 2.0-1