]> git.sesse.net Git - nageru/blobdiff - shared/midi_device.cpp
Move controller_spin_box.h to shared, so that the upcoming Futatabi MIDI editor can...
[nageru] / shared / midi_device.cpp
index ac0fea271d12d8837d3f53a3fe67d3eed22819c2..e4b1a116e17a27ecff7b7768d6e19fa7cf3fcdfb 100644 (file)
@@ -233,46 +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.
-       set<unsigned> active_lights;
-       for (const auto &it : current_light_status) {
-               if (it.second) {
-                       active_lights.insert(it.first);
-               }
-       }
+       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 set<unsigned> &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.
-               bool active = active_lights.count(note_num);
-               if (current_light_status.count(note_num) &&
-                   current_light_status[note_num] == active) {
-                       // 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, active ? 1 : 0);
-               WARN_ON_ERROR("snd_seq_event_output", snd_seq_event_output(alsa_seq, &ev));
-               current_light_status[note_num] = active;
+                       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));
 }