]> git.sesse.net Git - nageru/blobdiff - shared/midi_device.cpp
Fix a comment typo.
[nageru] / shared / midi_device.cpp
index ac0fea271d12d8837d3f53a3fe67d3eed22819c2..ab84fda6db51099e3ecf03013bc08a7b0a60947a 100644 (file)
@@ -21,7 +21,7 @@ MIDIDevice::~MIDIDevice()
        const uint64_t one = 1;
        if (write(should_quit_fd, &one, sizeof(one)) != sizeof(one)) {
                perror("write(should_quit_fd)");
-               exit(1);
+               abort();
        }
        midi_thread.join();
        close(should_quit_fd);
@@ -73,7 +73,7 @@ void MIDIDevice::thread_func()
 
        // The sequencer object is now ready to be used from other threads.
        {
-               lock_guard<mutex> lock(mu);
+               lock_guard<recursive_mutex> lock(mu);
                alsa_seq = seq;
                alsa_queue_id = queue_id;
        }
@@ -97,7 +97,7 @@ void MIDIDevice::thread_func()
                while (snd_seq_query_next_port(seq, pinfo) >= 0) {
                        constexpr int mask = SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
                        if ((snd_seq_port_info_get_capability(pinfo) & mask) == mask) {
-                               lock_guard<mutex> lock(mu);
+                               lock_guard<recursive_mutex> lock(mu);
                                subscribe_to_port_lock_held(seq, *snd_seq_port_info_get_addr(pinfo));
                        }
                }
@@ -154,7 +154,7 @@ void MIDIDevice::handle_event(snd_seq_t *seq, snd_seq_event_t *event)
                return;
        }
 
-       lock_guard<mutex> lock(mu);
+       lock_guard<recursive_mutex> lock(mu);
        switch (event->type) {
        case SND_SEQ_EVENT_CONTROLLER: {
                receiver->controller_received(event->data.control.param, event->data.control.value);
@@ -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));
 }