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>
12 #include "base/basictypes.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/stl_util.h"
17 #include "base/synchronization/lock.h"
18 #include "base/threading/thread.h"
19 #include "base/values.h"
20 #include "device/udev_linux/scoped_udev.h"
21 #include "media/midi/midi_export.h"
22 #include "media/midi/midi_manager.h"
27 class MIDI_EXPORT MidiManagerAlsa final
: public MidiManager
{
30 ~MidiManagerAlsa() override
;
32 // MidiManager implementation.
33 void StartInitialization() override
;
34 void DispatchSendMidiData(MidiManagerClient
* client
,
36 const std::vector
<uint8
>& data
,
37 double timestamp
) override
;
40 friend class MidiManagerAlsaTest
;
41 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest
, ExtractManufacturer
);
42 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest
, ToMidiPortState
);
45 typedef std::map
<int, AlsaCard
*> AlsaCardMap
;
49 enum class Type
{ kInput
, kOutput
};
51 // The Id class is used to keep the multiple strings separate
52 // but compare them all together for equality purposes.
53 // The individual strings that make up the Id can theoretically contain
54 // arbitrary characters, so unfortunately there is no simple way to
55 // concatenate them into a single string.
59 Id(const std::string
& bus
,
60 const std::string
& vendor_id
,
61 const std::string
& model_id
,
62 const std::string
& usb_interface_num
,
63 const std::string
& serial
);
66 bool operator==(const Id
&) const;
69 std::string
bus() const { return bus_
; }
70 std::string
vendor_id() const { return vendor_id_
; }
71 std::string
model_id() const { return model_id_
; }
72 std::string
usb_interface_num() const { return usb_interface_num_
; }
73 std::string
serial() const { return serial_
; }
77 std::string vendor_id_
;
78 std::string model_id_
;
79 std::string usb_interface_num_
;
83 MidiPort(const std::string
& path
,
88 const std::string
& client_name
,
89 const std::string
& port_name
,
90 const std::string
& manufacturer
,
91 const std::string
& version
,
95 // Gets a Value representation of this object, suitable for serialization.
96 scoped_ptr
<base::Value
> Value() const;
98 // Gets a string version of Value in JSON format.
99 std::string
JSONValue() const;
101 // Gets an opaque identifier for this object, suitable for using as the id
102 // field in MidiPort.id on the web. Note that this string does not store
104 std::string
OpaqueKey() const;
106 // Checks for equality for connected ports.
107 bool MatchConnected(const MidiPort
& query
) const;
108 // Checks for equality for kernel cards with id, pass 1.
109 bool MatchCardPass1(const MidiPort
& query
) const;
110 // Checks for equality for kernel cards with id, pass 2.
111 bool MatchCardPass2(const MidiPort
& query
) const;
112 // Checks for equality for non-card clients, pass 1.
113 bool MatchNoCardPass1(const MidiPort
& query
) const;
114 // Checks for equality for non-card clients, pass 2.
115 bool MatchNoCardPass2(const MidiPort
& query
) const;
118 std::string
path() const { return path_
; }
119 Id
id() const { return id_
; }
120 std::string
client_name() const { return client_name_
; }
121 std::string
port_name() const { return port_name_
; }
122 std::string
manufacturer() const { return manufacturer_
; }
123 std::string
version() const { return version_
; }
124 int client_id() const { return client_id_
; }
125 int port_id() const { return port_id_
; }
126 int midi_device() const { return midi_device_
; }
127 Type
type() const { return type_
; }
128 uint32
web_port_index() const { return web_port_index_
; }
129 bool connected() const { return connected_
; }
132 void set_web_port_index(uint32 web_port_index
) {
133 web_port_index_
= web_port_index
;
135 void set_connected(bool connected
) { connected_
= connected
; }
136 void Update(const std::string
& path
,
139 const std::string
& client_name
,
140 const std::string
& port_name
,
141 const std::string
& manufacturer
,
142 const std::string
& version
) {
144 client_id_
= client_id
;
146 client_name_
= client_name
;
147 port_name_
= port_name
;
148 manufacturer_
= manufacturer
;
153 // Immutable properties.
155 const int midi_device_
;
159 // Mutable properties. These will get updated as ports move around or
164 std::string client_name_
;
165 std::string port_name_
;
166 std::string manufacturer_
;
167 std::string version_
;
169 // Index for MidiManager.
170 uint32 web_port_index_
;
172 // Port is present in the ALSA system.
175 DISALLOW_COPY_AND_ASSIGN(MidiPort
);
178 class MidiPortStateBase
{
180 typedef ScopedVector
<MidiPort
>::iterator iterator
;
182 virtual ~MidiPortStateBase();
184 // Given a port, finds a port in the internal store.
185 iterator
Find(const MidiPort
& port
);
187 // Given a port, finds a connected port, using exact matching.
188 iterator
FindConnected(const MidiPort
& port
);
190 // Given a port, finds a disconnected port, using heuristic matching.
191 iterator
FindDisconnected(const MidiPort
& port
);
193 iterator
begin() { return ports_
.begin(); }
194 iterator
end() { return ports_
.end(); }
199 ScopedVector
<MidiPort
>* ports() { return &ports_
; }
202 ScopedVector
<MidiPort
> ports_
;
204 DISALLOW_COPY_AND_ASSIGN(MidiPortStateBase
);
207 class TemporaryMidiPortState final
: public MidiPortStateBase
{
209 // Removes a port from the list without deleting it.
210 iterator
weak_erase(iterator position
) {
211 return ports()->weak_erase(position
);
214 void Insert(scoped_ptr
<MidiPort
> port
);
217 class MidiPortState final
: public MidiPortStateBase
{
221 // Inserts a port. Returns web_port_index.
222 uint32
Insert(scoped_ptr
<MidiPort
> port
);
225 uint32 num_input_ports_
;
226 uint32 num_output_ports_
;
231 enum class PortDirection
{ kInput
, kOutput
, kDuplex
};
236 void ClientStart(int client_id
,
237 const std::string
& client_name
,
238 snd_seq_client_type_t type
);
239 bool ClientStarted(int client_id
);
240 void ClientExit(int client_id
);
241 void PortStart(int client_id
,
243 const std::string
& port_name
,
244 PortDirection direction
,
246 void PortExit(int client_id
, int port_id
);
247 snd_seq_client_type_t
ClientType(int client_id
) const;
248 scoped_ptr
<TemporaryMidiPortState
> ToMidiPortState(
249 const AlsaCardMap
& alsa_cards
);
251 int card_client_count() { return card_client_count_
; }
256 Port(const std::string
& name
, PortDirection direction
, bool midi
);
259 std::string
name() const { return name_
; }
260 PortDirection
direction() const { return direction_
; }
261 // True if this port is a MIDI port, instead of another kind of ALSA port.
262 bool midi() const { return midi_
; }
265 const std::string name_
;
266 const PortDirection direction_
;
269 DISALLOW_COPY_AND_ASSIGN(Port
);
274 typedef std::map
<int, Port
*> PortMap
;
276 Client(const std::string
& name
, snd_seq_client_type_t type
);
279 std::string
name() const { return name_
; }
280 snd_seq_client_type_t
type() const { return type_
; }
281 void AddPort(int addr
, scoped_ptr
<Port
> port
);
282 void RemovePort(int addr
);
283 PortMap::const_iterator
begin() const;
284 PortMap::const_iterator
end() const;
287 const std::string name_
;
288 const snd_seq_client_type_t type_
;
290 STLValueDeleter
<PortMap
> ports_deleter_
;
292 DISALLOW_COPY_AND_ASSIGN(Client
);
295 typedef std::map
<int, Client
*> ClientMap
;
298 STLValueDeleter
<ClientMap
> clients_deleter_
;
300 // This is the current number of clients we know about that have
301 // cards. When this number matches alsa_card_midi_count_, we know
302 // we are in sync between ALSA and udev. Until then, we cannot generate
303 // MIDIConnectionEvents to web clients.
304 int card_client_count_
;
306 DISALLOW_COPY_AND_ASSIGN(AlsaSeqState
);
311 AlsaCard(udev_device
* dev
,
312 const std::string
& name
,
313 const std::string
& longname
,
314 const std::string
& driver
,
315 int midi_device_count
);
317 std::string
name() const { return name_
; }
318 std::string
longname() const { return longname_
; }
319 std::string
driver() const { return driver_
; }
320 std::string
path() const { return path_
; }
321 std::string
bus() const { return bus_
; }
322 std::string
vendor_id() const { return vendor_id_
; }
323 std::string
model_id() const { return model_id_
; }
324 std::string
usb_interface_num() const { return usb_interface_num_
; }
325 std::string
serial() const { return serial_
; }
326 int midi_device_count() const { return midi_device_count_
; }
327 std::string
manufacturer() const { return manufacturer_
; }
330 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest
, ExtractManufacturer
);
332 // Extracts the manufacturer using heuristics and a variety of sources.
333 static std::string
ExtractManufacturerString(
334 const std::string
& udev_id_vendor
,
335 const std::string
& udev_id_vendor_id
,
336 const std::string
& udev_id_vendor_from_database
,
337 const std::string
& name
,
338 const std::string
& longname
);
340 const std::string name_
;
341 const std::string longname_
;
342 const std::string driver_
;
343 const std::string path_
;
344 const std::string bus_
;
345 const std::string vendor_id_
;
346 const std::string model_id_
;
347 const std::string usb_interface_num_
;
348 const std::string serial_
;
349 const int midi_device_count_
;
350 const std::string manufacturer_
;
352 DISALLOW_COPY_AND_ASSIGN(AlsaCard
);
355 typedef base::hash_map
<int, uint32
> SourceMap
;
356 typedef base::hash_map
<uint32
, int> OutPortMap
;
358 // An internal callback that runs on MidiSendThread.
359 void SendMidiData(uint32 port_index
, const std::vector
<uint8
>& data
);
361 void ScheduleEventLoop();
363 void ProcessSingleEvent(snd_seq_event_t
* event
, double timestamp
);
364 void ProcessClientStartEvent(int client_id
);
365 void ProcessPortStartEvent(const snd_seq_addr_t
& addr
);
366 void ProcessClientExitEvent(const snd_seq_addr_t
& addr
);
367 void ProcessPortExitEvent(const snd_seq_addr_t
& addr
);
368 void ProcessUdevEvent(udev_device
* dev
);
369 void AddCard(udev_device
* dev
);
370 void RemoveCard(int number
);
372 // Updates port_state_ and Web MIDI state from alsa_seq_state_.
373 void UpdatePortStateAndGenerateEvents();
375 // Enumerates ports. Call once after subscribing to the announce port.
376 void EnumerateAlsaPorts();
377 // Enumerates udev cards. Call once after initializing the udev monitor.
378 bool EnumerateUdevCards();
379 // Returns true if successful.
380 bool CreateAlsaOutputPort(uint32 port_index
, int client_id
, int port_id
);
381 void DeleteAlsaOutputPort(uint32 port_index
);
382 // Returns true if successful.
383 bool Subscribe(uint32 port_index
, int client_id
, int port_id
);
385 AlsaSeqState alsa_seq_state_
;
386 MidiPortState port_state_
;
389 snd_seq_t
* in_client_
;
391 snd_seq_t
* out_client_
;
394 // One input port, many output ports.
396 OutPortMap out_ports_
; // guarded by out_ports_lock_
397 base::Lock out_ports_lock_
; // guards out_ports_
399 // Mapping from ALSA client:port to our index.
400 SourceMap source_map_
;
402 // Mapping from card to devices.
403 AlsaCardMap alsa_cards_
;
404 STLValueDeleter
<AlsaCardMap
> alsa_cards_deleter_
;
406 // This is the current count of midi devices across all cards we know
407 // about. When this number matches card_client_count_ in AlsaSeqState,
408 // we are safe to generate MIDIConnectionEvents. Otherwise we need to
409 // wait for our information from ALSA and udev to get back in sync.
410 int alsa_card_midi_count_
;
412 // ALSA event -> MIDI coder.
413 snd_midi_event_t
* decoder_
;
415 // udev, for querying hardware devices.
416 device::ScopedUdevPtr udev_
;
417 device::ScopedUdevMonitorPtr udev_monitor_
;
419 base::Thread send_thread_
;
420 base::Thread event_thread_
;
422 bool event_thread_shutdown_
; // guarded by shutdown_lock_
423 base::Lock shutdown_lock_
; // guards event_thread_shutdown_
425 DISALLOW_COPY_AND_ASSIGN(MidiManagerAlsa
);
431 #endif // MEDIA_MIDI_MIDI_MANAGER_ALSA_H_