X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=shared%2Fmidi_device.cpp;h=ab84fda6db51099e3ecf03013bc08a7b0a60947a;hb=0a981d13d1c4dd006ac3a6a2e45691d095990ccc;hp=822beaeadbd4d95b3a03fe294c3da069603953e5;hpb=d1e616378bda406c96acf7c2dc975b7ab2e8165e;p=nageru diff --git a/shared/midi_device.cpp b/shared/midi_device.cpp index 822beae..ab84fda 100644 --- a/shared/midi_device.cpp +++ b/shared/midi_device.cpp @@ -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 lock(mu); + lock_guard 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 lock(mu); + lock_guard lock(mu); subscribe_to_port_lock_held(seq, *snd_seq_port_info_get_addr(pinfo)); } } @@ -154,12 +154,17 @@ void MIDIDevice::handle_event(snd_seq_t *seq, snd_seq_event_t *event) return; } - lock_guard lock(mu); + lock_guard lock(mu); switch (event->type) { case SND_SEQ_EVENT_CONTROLLER: { receiver->controller_received(event->data.control.param, event->data.control.value); break; } + case SND_SEQ_EVENT_PITCHBEND: { + // Note, -8192 to 8191 instead of 0 to 127. + receiver->controller_received(MIDIReceiver::PITCH_BEND_CONTROLLER, event->data.control.value); + break; + } case SND_SEQ_EVENT_NOTEON: { receiver->note_on_received(event->data.note.note); break; @@ -228,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 active_lights; - for (const auto &it : current_light_status) { - if (it.second) { - active_lights.insert(it.first); - } - } + map active_lights = move(current_light_status); current_light_status.clear(); update_lights_lock_held(active_lights); } -void MIDIDevice::update_lights_lock_held(const set &active_lights) +void MIDIDevice::update_lights_lock_held(const map &active_lights) { if (alsa_seq == nullptr) { return; } unsigned num_events = 0; - for (unsigned note_num = 1; note_num <= 127; ++note_num) { - 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)); }