Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / midi / midi_manager_alsa.h
blob4b0e4c21b42f649c68fff766bf69400218490731
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef MEDIA_MIDI_MIDI_MANAGER_ALSA_H_
6 #define MEDIA_MIDI_MIDI_MANAGER_ALSA_H_
8 #include <alsa/asoundlib.h>
9 #include <vector>
11 #include "base/basictypes.h"
12 #include "base/containers/scoped_ptr_map.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/scoped_vector.h"
16 #include "base/synchronization/lock.h"
17 #include "base/threading/thread.h"
18 #include "base/values.h"
19 #include "device/udev_linux/scoped_udev.h"
20 #include "media/midi/midi_export.h"
21 #include "media/midi/midi_manager.h"
23 namespace media {
24 namespace midi {
26 class MIDI_EXPORT MidiManagerAlsa final : public MidiManager {
27 public:
28 MidiManagerAlsa();
29 ~MidiManagerAlsa() override;
31 // MidiManager implementation.
32 void StartInitialization() override;
33 void DispatchSendMidiData(MidiManagerClient* client,
34 uint32 port_index,
35 const std::vector<uint8>& data,
36 double timestamp) override;
38 private:
39 friend class MidiManagerAlsaTest;
40 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, ExtractManufacturer);
41 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, ToMidiPortState);
43 class AlsaCard;
44 typedef base::ScopedPtrMap<int, scoped_ptr<AlsaCard>> AlsaCardMap;
46 class MidiPort {
47 public:
48 enum class Type { kInput, kOutput };
50 // The Id class is used to keep the multiple strings separate
51 // but compare them all together for equality purposes.
52 // The individual strings that make up the Id can theoretically contain
53 // arbitrary characters, so unfortunately there is no simple way to
54 // concatenate them into a single string.
55 class Id final {
56 public:
57 Id();
58 Id(const std::string& bus,
59 const std::string& vendor_id,
60 const std::string& model_id,
61 const std::string& usb_interface_num,
62 const std::string& serial);
63 Id(const Id&);
64 ~Id();
65 bool operator==(const Id&) const;
66 bool empty() const;
68 std::string bus() const { return bus_; }
69 std::string vendor_id() const { return vendor_id_; }
70 std::string model_id() const { return model_id_; }
71 std::string usb_interface_num() const { return usb_interface_num_; }
72 std::string serial() const { return serial_; }
74 private:
75 std::string bus_;
76 std::string vendor_id_;
77 std::string model_id_;
78 std::string usb_interface_num_;
79 std::string serial_;
82 MidiPort(const std::string& path,
83 const Id& id,
84 int client_id,
85 int port_id,
86 int midi_device,
87 const std::string& client_name,
88 const std::string& port_name,
89 const std::string& manufacturer,
90 const std::string& version,
91 Type type);
92 ~MidiPort();
94 // Gets a Value representation of this object, suitable for serialization.
95 scoped_ptr<base::Value> Value() const;
97 // Gets a string version of Value in JSON format.
98 std::string JSONValue() const;
100 // Gets an opaque identifier for this object, suitable for using as the id
101 // field in MidiPort.id on the web. Note that this string does not store
102 // the full state.
103 std::string OpaqueKey() const;
105 // Checks for equality for connected ports.
106 bool MatchConnected(const MidiPort& query) const;
107 // Checks for equality for kernel cards with id, pass 1.
108 bool MatchCardPass1(const MidiPort& query) const;
109 // Checks for equality for kernel cards with id, pass 2.
110 bool MatchCardPass2(const MidiPort& query) const;
111 // Checks for equality for non-card clients, pass 1.
112 bool MatchNoCardPass1(const MidiPort& query) const;
113 // Checks for equality for non-card clients, pass 2.
114 bool MatchNoCardPass2(const MidiPort& query) const;
116 // accessors
117 std::string path() const { return path_; }
118 Id id() const { return id_; }
119 std::string client_name() const { return client_name_; }
120 std::string port_name() const { return port_name_; }
121 std::string manufacturer() const { return manufacturer_; }
122 std::string version() const { return version_; }
123 int client_id() const { return client_id_; }
124 int port_id() const { return port_id_; }
125 int midi_device() const { return midi_device_; }
126 Type type() const { return type_; }
127 uint32 web_port_index() const { return web_port_index_; }
128 bool connected() const { return connected_; }
130 // mutators
131 void set_web_port_index(uint32 web_port_index) {
132 web_port_index_ = web_port_index;
134 void set_connected(bool connected) { connected_ = connected; }
135 void Update(const std::string& path,
136 int client_id,
137 int port_id,
138 const std::string& client_name,
139 const std::string& port_name,
140 const std::string& manufacturer,
141 const std::string& version) {
142 path_ = path;
143 client_id_ = client_id;
144 port_id_ = port_id;
145 client_name_ = client_name;
146 port_name_ = port_name;
147 manufacturer_ = manufacturer;
148 version_ = version;
151 private:
152 // Immutable properties.
153 const Id id_;
154 const int midi_device_;
156 const Type type_;
158 // Mutable properties. These will get updated as ports move around or
159 // drivers change.
160 std::string path_;
161 int client_id_;
162 int port_id_;
163 std::string client_name_;
164 std::string port_name_;
165 std::string manufacturer_;
166 std::string version_;
168 // Index for MidiManager.
169 uint32 web_port_index_ = 0;
171 // Port is present in the ALSA system.
172 bool connected_ = true;
174 DISALLOW_COPY_AND_ASSIGN(MidiPort);
177 class MidiPortStateBase {
178 public:
179 typedef ScopedVector<MidiPort>::iterator iterator;
181 virtual ~MidiPortStateBase();
183 // Given a port, finds a port in the internal store.
184 iterator Find(const MidiPort& port);
186 // Given a port, finds a connected port, using exact matching.
187 iterator FindConnected(const MidiPort& port);
189 // Given a port, finds a disconnected port, using heuristic matching.
190 iterator FindDisconnected(const MidiPort& port);
192 iterator begin() { return ports_.begin(); }
193 iterator end() { return ports_.end(); }
195 protected:
196 MidiPortStateBase();
198 std::vector<MidiPort*>& ports() { return ports_.get(); }
200 private:
201 ScopedVector<MidiPort> ports_;
203 DISALLOW_COPY_AND_ASSIGN(MidiPortStateBase);
206 class TemporaryMidiPortState final : public MidiPortStateBase {
207 public:
208 // Removes a port from the list without deleting it.
209 iterator weak_erase(iterator position) { return ports().erase(position); }
211 void Insert(scoped_ptr<MidiPort> port);
214 class MidiPortState final : public MidiPortStateBase {
215 public:
216 MidiPortState();
218 // Inserts a port. Returns web_port_index.
219 uint32 Insert(scoped_ptr<MidiPort> port);
221 private:
222 uint32 num_input_ports_ = 0;
223 uint32 num_output_ports_ = 0;
226 class AlsaSeqState {
227 public:
228 enum class PortDirection { kInput, kOutput, kDuplex };
230 AlsaSeqState();
231 ~AlsaSeqState();
233 void ClientStart(int client_id,
234 const std::string& client_name,
235 snd_seq_client_type_t type);
236 bool ClientStarted(int client_id);
237 void ClientExit(int client_id);
238 void PortStart(int client_id,
239 int port_id,
240 const std::string& port_name,
241 PortDirection direction,
242 bool midi);
243 void PortExit(int client_id, int port_id);
244 snd_seq_client_type_t ClientType(int client_id) const;
245 scoped_ptr<TemporaryMidiPortState> ToMidiPortState(
246 const AlsaCardMap& alsa_cards);
248 int card_client_count() { return card_client_count_; }
250 private:
251 class Port {
252 public:
253 Port(const std::string& name, PortDirection direction, bool midi);
254 ~Port();
256 std::string name() const { return name_; }
257 PortDirection direction() const { return direction_; }
258 // True if this port is a MIDI port, instead of another kind of ALSA port.
259 bool midi() const { return midi_; }
261 private:
262 const std::string name_;
263 const PortDirection direction_;
264 const bool midi_;
266 DISALLOW_COPY_AND_ASSIGN(Port);
269 class Client {
270 public:
271 typedef base::ScopedPtrMap<int, scoped_ptr<Port>> PortMap;
273 Client(const std::string& name, snd_seq_client_type_t type);
274 ~Client();
276 std::string name() const { return name_; }
277 snd_seq_client_type_t type() const { return type_; }
278 void AddPort(int addr, scoped_ptr<Port> port);
279 void RemovePort(int addr);
280 PortMap::const_iterator begin() const;
281 PortMap::const_iterator end() const;
283 private:
284 const std::string name_;
285 const snd_seq_client_type_t type_;
286 PortMap ports_;
288 DISALLOW_COPY_AND_ASSIGN(Client);
291 typedef base::ScopedPtrMap<int, scoped_ptr<Client>> ClientMap;
293 ClientMap clients_;
295 // This is the current number of clients we know about that have
296 // cards. When this number matches alsa_card_midi_count_, we know
297 // we are in sync between ALSA and udev. Until then, we cannot generate
298 // MIDIConnectionEvents to web clients.
299 int card_client_count_ = 0;
301 DISALLOW_COPY_AND_ASSIGN(AlsaSeqState);
304 class AlsaCard {
305 public:
306 AlsaCard(udev_device* dev,
307 const std::string& name,
308 const std::string& longname,
309 const std::string& driver,
310 int midi_device_count);
311 ~AlsaCard();
312 std::string name() const { return name_; }
313 std::string longname() const { return longname_; }
314 std::string driver() const { return driver_; }
315 std::string path() const { return path_; }
316 std::string bus() const { return bus_; }
317 std::string vendor_id() const { return vendor_id_; }
318 std::string model_id() const { return model_id_; }
319 std::string usb_interface_num() const { return usb_interface_num_; }
320 std::string serial() const { return serial_; }
321 int midi_device_count() const { return midi_device_count_; }
322 std::string manufacturer() const { return manufacturer_; }
324 private:
325 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest, ExtractManufacturer);
327 // Extracts the manufacturer using heuristics and a variety of sources.
328 static std::string ExtractManufacturerString(
329 const std::string& udev_id_vendor,
330 const std::string& udev_id_vendor_id,
331 const std::string& udev_id_vendor_from_database,
332 const std::string& name,
333 const std::string& longname);
335 const std::string name_;
336 const std::string longname_;
337 const std::string driver_;
338 const std::string path_;
339 const std::string bus_;
340 const std::string vendor_id_;
341 const std::string model_id_;
342 const std::string usb_interface_num_;
343 const std::string serial_;
344 const int midi_device_count_;
345 const std::string manufacturer_;
347 DISALLOW_COPY_AND_ASSIGN(AlsaCard);
350 struct SndSeqDeleter {
351 void operator()(snd_seq_t* seq) const { snd_seq_close(seq); }
354 struct SndMidiEventDeleter {
355 void operator()(snd_midi_event_t* coder) const {
356 snd_midi_event_free(coder);
360 typedef base::hash_map<int, uint32> SourceMap;
361 typedef base::hash_map<uint32, int> OutPortMap;
363 // An internal callback that runs on MidiSendThread.
364 void SendMidiData(uint32 port_index, const std::vector<uint8>& data);
366 void ScheduleEventLoop();
367 void EventLoop();
368 void ProcessSingleEvent(snd_seq_event_t* event, double timestamp);
369 void ProcessClientStartEvent(int client_id);
370 void ProcessPortStartEvent(const snd_seq_addr_t& addr);
371 void ProcessClientExitEvent(const snd_seq_addr_t& addr);
372 void ProcessPortExitEvent(const snd_seq_addr_t& addr);
373 void ProcessUdevEvent(udev_device* dev);
374 void AddCard(udev_device* dev);
375 void RemoveCard(int number);
377 // Updates port_state_ and Web MIDI state from alsa_seq_state_.
378 void UpdatePortStateAndGenerateEvents();
380 // Enumerates ports. Call once after subscribing to the announce port.
381 void EnumerateAlsaPorts();
382 // Enumerates udev cards. Call once after initializing the udev monitor.
383 bool EnumerateUdevCards();
384 // Returns true if successful.
385 bool CreateAlsaOutputPort(uint32 port_index, int client_id, int port_id);
386 void DeleteAlsaOutputPort(uint32 port_index);
387 // Returns true if successful.
388 bool Subscribe(uint32 port_index, int client_id, int port_id);
390 AlsaSeqState alsa_seq_state_;
391 MidiPortState port_state_;
393 // ALSA seq handles.
394 scoped_ptr<snd_seq_t, SndSeqDeleter> in_client_;
395 int in_client_id_ = -1;
396 scoped_ptr<snd_seq_t, SndSeqDeleter> out_client_;
397 int out_client_id_ = -1;
399 // One input port, many output ports.
400 int in_port_id_ = -1;
401 OutPortMap out_ports_; // guarded by out_ports_lock_
402 base::Lock out_ports_lock_; // guards out_ports_
404 // Mapping from ALSA client:port to our index.
405 SourceMap source_map_;
407 // Mapping from card to devices.
408 AlsaCardMap alsa_cards_;
410 // This is the current count of midi devices across all cards we know
411 // about. When this number matches card_client_count_ in AlsaSeqState,
412 // we are safe to generate MIDIConnectionEvents. Otherwise we need to
413 // wait for our information from ALSA and udev to get back in sync.
414 int alsa_card_midi_count_ = 0;
416 // ALSA event -> MIDI coder.
417 scoped_ptr<snd_midi_event_t, SndMidiEventDeleter> decoder_;
419 // udev, for querying hardware devices.
420 device::ScopedUdevPtr udev_;
421 device::ScopedUdevMonitorPtr udev_monitor_;
423 base::Thread send_thread_;
424 base::Thread event_thread_;
426 bool event_thread_shutdown_ = false; // guarded by shutdown_lock_
427 base::Lock shutdown_lock_; // guards event_thread_shutdown_
429 DISALLOW_COPY_AND_ASSIGN(MidiManagerAlsa);
432 } // namespace midi
433 } // namespace media
435 #endif // MEDIA_MIDI_MIDI_MANAGER_ALSA_H_