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>
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"
26 class MIDI_EXPORT MidiManagerAlsa final
: public MidiManager
{
29 ~MidiManagerAlsa() override
;
31 // MidiManager implementation.
32 void StartInitialization() override
;
33 void DispatchSendMidiData(MidiManagerClient
* client
,
35 const std::vector
<uint8
>& data
,
36 double timestamp
) override
;
39 friend class MidiManagerAlsaTest
;
40 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest
, ExtractManufacturer
);
41 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest
, ToMidiPortState
);
44 typedef base::ScopedPtrMap
<int, scoped_ptr
<AlsaCard
>> AlsaCardMap
;
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.
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
);
65 bool operator==(const Id
&) 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_
; }
76 std::string vendor_id_
;
77 std::string model_id_
;
78 std::string usb_interface_num_
;
82 MidiPort(const std::string
& path
,
87 const std::string
& client_name
,
88 const std::string
& port_name
,
89 const std::string
& manufacturer
,
90 const std::string
& version
,
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
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;
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_
; }
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
,
138 const std::string
& client_name
,
139 const std::string
& port_name
,
140 const std::string
& manufacturer
,
141 const std::string
& version
) {
143 client_id_
= client_id
;
145 client_name_
= client_name
;
146 port_name_
= port_name
;
147 manufacturer_
= manufacturer
;
152 // Immutable properties.
154 const int midi_device_
;
158 // Mutable properties. These will get updated as ports move around or
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
{
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(); }
198 std::vector
<MidiPort
*>& ports() { return ports_
.get(); }
201 ScopedVector
<MidiPort
> ports_
;
203 DISALLOW_COPY_AND_ASSIGN(MidiPortStateBase
);
206 class TemporaryMidiPortState final
: public MidiPortStateBase
{
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
{
218 // Inserts a port. Returns web_port_index.
219 uint32
Insert(scoped_ptr
<MidiPort
> port
);
222 uint32 num_input_ports_
= 0;
223 uint32 num_output_ports_
= 0;
228 enum class PortDirection
{ kInput
, kOutput
, kDuplex
};
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
,
240 const std::string
& port_name
,
241 PortDirection direction
,
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_
; }
253 Port(const std::string
& name
, PortDirection direction
, bool midi
);
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_
; }
262 const std::string name_
;
263 const PortDirection direction_
;
266 DISALLOW_COPY_AND_ASSIGN(Port
);
271 typedef base::ScopedPtrMap
<int, scoped_ptr
<Port
>> PortMap
;
273 Client(const std::string
& name
, snd_seq_client_type_t type
);
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;
284 const std::string name_
;
285 const snd_seq_client_type_t type_
;
288 DISALLOW_COPY_AND_ASSIGN(Client
);
291 typedef base::ScopedPtrMap
<int, scoped_ptr
<Client
>> ClientMap
;
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
);
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
);
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_
; }
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();
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_
;
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
);
435 #endif // MEDIA_MIDI_MIDI_MANAGER_ALSA_H_