]> git.sesse.net Git - nageru/blob - shared/midi_device.h
Fix a deadlock in Futatabi when using MIDI devices.
[nageru] / shared / midi_device.h
1 #ifndef _MIDI_DEVICE_H
2 #define _MIDI_DEVICE_H 1
3
4 // MIDIDevice is a class that pools incoming MIDI messages from
5 // all MIDI devices in the system, decodes them and sends them on.
6
7 #include <atomic>
8 #include <map>
9 #include <mutex>
10 #include <thread>
11
12 typedef struct snd_seq_addr snd_seq_addr_t;
13 typedef struct snd_seq_event snd_seq_event_t;
14 typedef struct _snd_seq snd_seq_t;
15
16 class MIDIReceiver {
17 public:
18         // Pitch bend events are received as a virtual controller with
19         // range -8192..8191 instead of 0..127 (but see the comment
20         // in map_controller_to_float() in midi_mapper.cpp).
21         static constexpr int PITCH_BEND_CONTROLLER = 128;
22
23         virtual ~MIDIReceiver() {}
24         virtual void controller_received(int controller, int value) = 0;
25         virtual void note_on_received(int note) = 0;
26         virtual void update_num_subscribers(unsigned num_subscribers) = 0;
27 };
28
29 class MIDIDevice {
30 public:
31         struct LightKey {
32                 enum { NOTE, CONTROLLER } type;
33                 unsigned number;
34
35                 bool operator< (const LightKey& other) const
36                 {
37                         if (type != other.type) {
38                                 return type < other.type;
39                         }
40                         return number < other.number;
41                 }
42         };
43
44         MIDIDevice(MIDIReceiver *receiver);
45         ~MIDIDevice();
46         void start_thread();
47
48         void update_lights(const std::map<LightKey, uint8_t> &active_lights)
49         {
50                 std::lock_guard<std::recursive_mutex> lock(mu);
51                 update_lights_lock_held(active_lights);
52         }
53
54 private:
55         void thread_func();
56         void handle_event(snd_seq_t *seq, snd_seq_event_t *event);
57         void subscribe_to_port_lock_held(snd_seq_t *seq, const snd_seq_addr_t &addr);
58         void update_lights_lock_held(const std::map<LightKey, uint8_t> &active_lights);
59
60         std::atomic<bool> should_quit{false};
61         int should_quit_fd;
62
63         // Recursive because the MIDI receiver may update_lights() back while we are sending it stuff.
64         // TODO: Do we need this anymore after receiver is not under the lock?
65         mutable std::recursive_mutex mu;
66         MIDIReceiver *receiver;  // _Not_ under <mu>; everything on it should be thread-safe.
67
68         std::thread midi_thread;
69         std::map<LightKey, uint8_t> current_light_status;  // Keyed by note number. Under <mu>.
70         snd_seq_t *alsa_seq{nullptr};  // Under <mu>.
71         int alsa_queue_id{-1};  // Under <mu>.
72         std::atomic<int> num_subscribed_ports{0};
73 };
74
75 #endif  // !defined(_MIDI_DEVICE_H)