]> git.sesse.net Git - nageru/commitdiff
Add infrastructure for MIDI lights that are controllers instead of notes.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 17 Jan 2019 21:18:13 +0000 (22:18 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Thu, 17 Jan 2019 23:42:04 +0000 (00:42 +0100)
futatabi/midi_mapper.cpp
nageru/midi_mapper.cpp
nageru/midi_mapper.h
shared/midi_device.cpp
shared/midi_device.h
shared/midi_mapper_util.h

index 36a2e43f9224232570cc2f02d4e9b8168e5e4943..93e367a77aa1ad0987c1a0f40e38372f90c0f14f 100644 (file)
@@ -206,7 +206,7 @@ void MIDIMapper::refresh_lights()
 
 void MIDIMapper::update_lights_lock_held()
 {
-       map<unsigned, uint8_t> active_lights;  // Desired state.
+       map<MIDIDevice::LightKey, uint8_t> active_lights;  // Desired state.
        if (current_controller_bank == 0) {
                activate_mapped_light(*mapping_proto, MIDIMappingProto::kBank1IsSelectedFieldNumber, &active_lights);
        }
index 1fdb72e6c4006a86910588ba37c47fb0a548b3c1..d0a42ec60274e05c00d98ad528162ad3519c5b94 100644 (file)
@@ -318,7 +318,7 @@ void MIDIMapper::update_lights_lock_held()
                return;
        }
 
-       map<unsigned, uint8_t> active_lights;  // Desired state.
+       map<MIDIDevice::LightKey, uint8_t> active_lights;  // Desired state.
        if (current_controller_bank == 0) {
                activate_lights_all_buses(MIDIMappingBusProto::kBank1IsSelectedFieldNumber, &active_lights);
        }
@@ -363,7 +363,7 @@ void MIDIMapper::update_lights_lock_held()
        midi_device.update_lights(active_lights);
 }
 
-void MIDIMapper::activate_lights_all_buses(int field_number, map<unsigned, uint8_t> *active_lights)
+void MIDIMapper::activate_lights_all_buses(int field_number, map<MIDIDevice::LightKey, uint8_t> *active_lights)
 {
        for (size_t bus_idx = 0; bus_idx < size_t(mapping_proto->bus_mapping_size()); ++bus_idx) {
                const MIDIMappingBusProto &bus_mapping = mapping_proto->bus_mapping(bus_idx);
index 3ecdee263afe9e8935b1f85ee7b08ff6f89afd74..9630780dfaee51c3e63759daeb7d7c4e79666c85 100644 (file)
@@ -109,7 +109,7 @@ private:
        void update_highlights();
 
        void update_lights_lock_held();
-       void activate_lights_all_buses(int field_number, std::map<unsigned, uint8_t> *active_lights);
+       void activate_lights_all_buses(int field_number, std::map<MIDIDevice::LightKey, uint8_t> *active_lights);
 
        std::atomic<bool> should_quit{false};
        int should_quit_fd;
index a2329617250bf4b483831e15e6e96945581244b7..e4b1a116e17a27ecff7b7768d6e19fa7cf3fcdfb 100644 (file)
@@ -233,42 +233,63 @@ void MIDIDevice::subscribe_to_port_lock_held(snd_seq_t *seq, const snd_seq_addr_
        }
 
        // The current status of the device is unknown, so refresh it.
-       map<unsigned, uint8_t> active_lights = move(current_light_status);
+       map<LightKey, uint8_t> active_lights = move(current_light_status);
        current_light_status.clear();
        update_lights_lock_held(active_lights);
 }
 
-void MIDIDevice::update_lights_lock_held(const map<unsigned, uint8_t> &active_lights)
+void MIDIDevice::update_lights_lock_held(const map<LightKey, uint8_t> &active_lights)
 {
        if (alsa_seq == nullptr) {
                return;
        }
 
        unsigned num_events = 0;
-       for (unsigned note_num = 1; note_num <= 127; ++note_num) {  // Note: Pitch bend is ignored.
-               const auto it = active_lights.find(note_num);
-               uint8_t velocity = (it == active_lights.end()) ? 0 : it->second;
-               if (current_light_status.count(note_num) &&
-                   current_light_status[note_num] == velocity) {
-                       // Already known to be in the desired state.
-                       continue;
-               }
+       for (auto type : { LightKey::NOTE, LightKey::CONTROLLER }) {
+               for (unsigned num = 1; num <= 127; ++num) {  // Note: Pitch bend is ignored.
+                       LightKey key{type, num};
+                       const auto it = active_lights.find(key);
+                       uint8_t value;  // Velocity for notes, controller value for controllers.
+
+                       // Notes have a natural “off”, while controllers don't really.
+                       // For some reason, not all devices respond to note off.
+                       // Use note-on with value of 0 (which is equivalent) instead.
+                       if (it == active_lights.end()) {
+                               // Notes have a natural “off”, while controllers don't really,
+                               // so just skip them if we have no set value.
+                               if (type == LightKey::CONTROLLER) continue;
+
+                               // For some reason, not all devices respond to note off.
+                               // Use note-on with value of 0 (which is equivalent) instead.
+                               value = 0;
+                       } else {
+                               value = it->second;
+                       }
+                       if (current_light_status.count(key) &&
+                           current_light_status[key] == value) {
+                               // Already known to be in the desired state.
+                               continue;
+                       }
 
-               snd_seq_event_t ev;
-               snd_seq_ev_clear(&ev);
-
-               // Some devices drop events if we throw them onto them
-               // too quickly. Add a 1 ms delay for each.
-               snd_seq_real_time_t tm{0, num_events++ * 1000000};
-               snd_seq_ev_schedule_real(&ev, alsa_queue_id, true, &tm);
-               snd_seq_ev_set_source(&ev, 0);
-               snd_seq_ev_set_subs(&ev);
-
-               // For some reason, not all devices respond to note off.
-               // Use note-on with velocity of 0 (which is equivalent) instead.
-               snd_seq_ev_set_noteon(&ev, /*channel=*/0, note_num, velocity);
-               WARN_ON_ERROR("snd_seq_event_output", snd_seq_event_output(alsa_seq, &ev));
-               current_light_status[note_num] = velocity;
+                       snd_seq_event_t ev;
+                       snd_seq_ev_clear(&ev);
+
+                       // Some devices drop events if we throw them onto them
+                       // too quickly. Add a 1 ms delay for each.
+                       snd_seq_real_time_t tm{0, num_events++ * 1000000};
+                       snd_seq_ev_schedule_real(&ev, alsa_queue_id, true, &tm);
+                       snd_seq_ev_set_source(&ev, 0);
+                       snd_seq_ev_set_subs(&ev);
+
+                       if (type == LightKey::NOTE) {
+                               snd_seq_ev_set_noteon(&ev, /*channel=*/0, num, value);
+                               current_light_status[key] = value;
+                       } else {
+                               snd_seq_ev_set_controller(&ev, /*channel=*/0, num, value);
+                               current_light_status[key] = value;
+                       }
+                       WARN_ON_ERROR("snd_seq_event_output", snd_seq_event_output(alsa_seq, &ev));
+               }
        }
        WARN_ON_ERROR("snd_seq_drain_output", snd_seq_drain_output(alsa_seq));
 }
index 9c76d18b9d1a667c18cf2111f9fc36d4ac63ee80..a9f33d65c6516d835d909f3fa2407fa200b115c6 100644 (file)
@@ -28,11 +28,24 @@ public:
 
 class MIDIDevice {
 public:
+       struct LightKey {
+               enum { NOTE, CONTROLLER } type;
+               unsigned number;
+
+               bool operator< (const LightKey& other) const
+               {
+                       if (type != other.type) {
+                               return type < other.type;
+                       }
+                       return number < other.number;
+               }
+       };
+
        MIDIDevice(MIDIReceiver *receiver);
        ~MIDIDevice();
        void start_thread();
 
-       void update_lights(const std::map<unsigned, uint8_t> &active_lights)
+       void update_lights(const std::map<LightKey, uint8_t> &active_lights)
        {
                std::lock_guard<std::mutex> lock(mu);
                update_lights_lock_held(active_lights);
@@ -42,7 +55,7 @@ private:
        void thread_func();
        void handle_event(snd_seq_t *seq, snd_seq_event_t *event);
        void subscribe_to_port_lock_held(snd_seq_t *seq, const snd_seq_addr_t &addr);
-       void update_lights_lock_held(const std::map<unsigned, uint8_t> &active_lights);
+       void update_lights_lock_held(const std::map<LightKey, uint8_t> &active_lights);
 
        std::atomic<bool> should_quit{false};
        int should_quit_fd;
@@ -51,7 +64,7 @@ private:
        MIDIReceiver *receiver;  // Under <mu>.
 
        std::thread midi_thread;
-       std::map<unsigned, uint8_t> current_light_status;  // Keyed by note number. Under <mu>.
+       std::map<LightKey, uint8_t> current_light_status;  // Keyed by note number. Under <mu>.
        snd_seq_t *alsa_seq{nullptr};  // Under <mu>.
        int alsa_queue_id{-1};  // Under <mu>.
        std::atomic<int> num_subscribed_ports{0};
index 07f2a53b4241dd7f69b71b62e12de93e83775659..9120fc8719998f97575193e4e739c25aa7f65f58 100644 (file)
@@ -49,7 +49,7 @@ inline bool match_bank_helper(const Proto &msg, int bank_field_number, int bank)
 
 // Find what MIDI note the given light (as given by field_number) is mapped to, and enable it.
 template <class Proto>
-void activate_mapped_light(const Proto &msg, int field_number, std::map<unsigned, uint8_t> *active_lights)
+void activate_mapped_light(const Proto &msg, int field_number, std::map<MIDIDevice::LightKey, uint8_t> *active_lights)
 {
        using namespace google::protobuf;
        const FieldDescriptor *descriptor = msg.GetDescriptor()->FindFieldByNumber(field_number);
@@ -59,7 +59,8 @@ void activate_mapped_light(const Proto &msg, int field_number, std::map<unsigned
        }
        const MIDILightProto &light_proto =
                static_cast<const MIDILightProto &>(reflection->GetMessage(msg, descriptor));
-       active_lights->emplace(light_proto.note_number(), light_proto.velocity());
+       active_lights->emplace(MIDIDevice::LightKey{MIDIDevice::LightKey::NOTE, unsigned(light_proto.note_number())},
+               light_proto.velocity());
 }
 
 inline double map_controller_to_float(int controller, int val)