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 "media/midi/midi_manager.h"
24 class MEDIA_EXPORT MidiManagerAlsa final
: public MidiManager
{
27 ~MidiManagerAlsa() override
;
29 // MidiManager implementation.
30 void StartInitialization() override
;
31 void DispatchSendMidiData(MidiManagerClient
* client
,
33 const std::vector
<uint8
>& data
,
34 double timestamp
) override
;
37 friend class MidiManagerAlsaTest
;
38 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest
, ExtractManufacturer
);
39 FRIEND_TEST_ALL_PREFIXES(MidiManagerAlsaTest
, ToMidiPortState
);
43 enum class Type
{ kInput
, kOutput
};
45 MidiPort(const std::string
& path
,
46 const std::string
& id
,
50 const std::string
& client_name
,
51 const std::string
& port_name
,
52 const std::string
& manufacturer
,
53 const std::string
& version
,
57 // Gets a Value representation of this object, suitable for serialization.
58 scoped_ptr
<base::Value
> Value() const;
60 // Gets a string version of Value in JSON format.
61 std::string
JSONValue() const;
63 // Gets an opaque identifier for this object, suitable for using as the id
64 // field in MidiPort.id on the web. Note that this string does not store
66 std::string
OpaqueKey() const;
68 // Checks for equality for connected ports.
69 bool MatchConnected(const MidiPort
& query
) const;
70 // Checks for equality for kernel cards with id, pass 1.
71 bool MatchCardPass1(const MidiPort
& query
) const;
72 // Checks for equality for kernel cards with id, pass 2.
73 bool MatchCardPass2(const MidiPort
& query
) const;
74 // Checks for equality for non-card clients, pass 1.
75 bool MatchNoCardPass1(const MidiPort
& query
) const;
76 // Checks for equality for non-card clients, pass 2.
77 bool MatchNoCardPass2(const MidiPort
& query
) const;
80 std::string
path() const { return path_
; }
81 std::string
id() const { return id_
; }
82 std::string
client_name() const { return client_name_
; }
83 std::string
port_name() const { return port_name_
; }
84 std::string
manufacturer() const { return manufacturer_
; }
85 std::string
version() const { return version_
; }
86 int client_id() const { return client_id_
; }
87 int port_id() const { return port_id_
; }
88 int midi_device() const { return midi_device_
; }
89 Type
type() const { return type_
; }
90 uint32
web_port_index() const { return web_port_index_
; }
91 bool connected() const { return connected_
; }
94 void set_web_port_index(uint32 web_port_index
) {
95 web_port_index_
= web_port_index
;
97 void set_connected(bool connected
) { connected_
= connected
; }
98 void Update(const std::string
& path
,
101 const std::string
& client_name
,
102 const std::string
& port_name
,
103 const std::string
& manufacturer
,
104 const std::string
& version
) {
106 client_id_
= client_id
;
108 client_name_
= client_name
;
109 port_name_
= port_name
;
110 manufacturer_
= manufacturer
;
115 // Immutable properties.
116 const std::string id_
;
117 const int midi_device_
;
121 // Mutable properties. These will get updated as ports move around or
126 std::string client_name_
;
127 std::string port_name_
;
128 std::string manufacturer_
;
129 std::string version_
;
131 // Index for MidiManager.
132 uint32 web_port_index_
;
134 // Port is present in the ALSA system.
137 DISALLOW_COPY_AND_ASSIGN(MidiPort
);
140 class MidiPortStateBase
{
142 typedef ScopedVector
<MidiPort
>::iterator iterator
;
144 virtual ~MidiPortStateBase();
146 // Given a port, finds a port in the internal store.
147 iterator
Find(const MidiPort
& port
);
149 // Given a port, finds a connected port, using exact matching.
150 iterator
FindConnected(const MidiPort
& port
);
152 // Given a port, finds a disconnected port, using heuristic matching.
153 iterator
FindDisconnected(const MidiPort
& port
);
155 iterator
begin() { return ports_
.begin(); }
156 iterator
end() { return ports_
.end(); }
161 ScopedVector
<MidiPort
>* ports();
164 ScopedVector
<MidiPort
> ports_
;
166 DISALLOW_COPY_AND_ASSIGN(MidiPortStateBase
);
169 class TemporaryMidiPortState final
: public MidiPortStateBase
{
171 // Removes a port from the list without deleting it.
172 iterator
weak_erase(iterator position
) {
173 return ports()->weak_erase(position
);
176 void Insert(scoped_ptr
<MidiPort
> port
);
179 class MidiPortState final
: public MidiPortStateBase
{
183 // Inserts a port. Returns web_port_index.
184 uint32
Insert(scoped_ptr
<MidiPort
> port
);
187 uint32 num_input_ports_
;
188 uint32 num_output_ports_
;
193 enum class PortDirection
{ kInput
, kOutput
, kDuplex
};
198 void ClientStart(int client_id
,
199 const std::string
& client_name
,
200 snd_seq_client_type_t type
);
201 bool ClientStarted(int client_id
);
202 void ClientExit(int client_id
);
203 void PortStart(int client_id
,
205 const std::string
& port_name
,
206 PortDirection direction
,
208 void PortExit(int client_id
, int port_id
);
209 snd_seq_client_type_t
ClientType(int client_id
) const;
210 scoped_ptr
<TemporaryMidiPortState
> ToMidiPortState();
215 Port(const std::string
& name
, PortDirection direction
, bool midi
);
218 std::string
name() const;
219 PortDirection
direction() const;
220 // True if this port is a MIDI port, instead of another kind of ALSA port.
224 const std::string name_
;
225 const PortDirection direction_
;
228 DISALLOW_COPY_AND_ASSIGN(Port
);
233 typedef std::map
<int, Port
*> PortMap
;
235 Client(const std::string
& name
, snd_seq_client_type_t type
);
238 std::string
name() const;
239 snd_seq_client_type_t
type() const;
240 void AddPort(int addr
, scoped_ptr
<Port
> port
);
241 void RemovePort(int addr
);
242 PortMap::const_iterator
begin() const;
243 PortMap::const_iterator
end() const;
246 const std::string name_
;
247 const snd_seq_client_type_t type_
;
249 STLValueDeleter
<PortMap
> ports_deleter_
;
251 DISALLOW_COPY_AND_ASSIGN(Client
);
254 typedef std::map
<int, Client
*> ClientMap
;
257 STLValueDeleter
<ClientMap
> clients_deleter_
;
259 DISALLOW_COPY_AND_ASSIGN(AlsaSeqState
);
262 typedef base::hash_map
<int, uint32
> SourceMap
;
263 typedef base::hash_map
<uint32
, int> OutPortMap
;
265 // Extracts the manufacturer using heuristics and a variety of sources.
266 static std::string
ExtractManufacturerString(
267 const std::string
& udev_id_vendor
,
268 const std::string
& udev_id_vendor_id
,
269 const std::string
& udev_id_vendor_from_database
,
270 const std::string
& alsa_name
,
271 const std::string
& alsa_longname
);
273 // An internal callback that runs on MidiSendThread.
274 void SendMidiData(uint32 port_index
, const std::vector
<uint8
>& data
);
276 void ScheduleEventLoop();
278 void ProcessSingleEvent(snd_seq_event_t
* event
, double timestamp
);
279 void ProcessClientStartEvent(int client_id
);
280 void ProcessPortStartEvent(const snd_seq_addr_t
& addr
);
281 void ProcessClientExitEvent(const snd_seq_addr_t
& addr
);
282 void ProcessPortExitEvent(const snd_seq_addr_t
& addr
);
284 // Updates port_state_ and Web MIDI state from alsa_seq_state_.
285 void UpdatePortStateAndGenerateEvents();
287 // Enumerates ports. Call once after subscribing to the announce port.
288 void EnumerateAlsaPorts();
289 // Returns true if successful.
290 bool CreateAlsaOutputPort(uint32 port_index
, int client_id
, int port_id
);
291 void DeleteAlsaOutputPort(uint32 port_index
);
292 // Returns true if successful.
293 bool Subscribe(uint32 port_index
, int client_id
, int port_id
);
295 AlsaSeqState alsa_seq_state_
;
296 MidiPortState port_state_
;
299 snd_seq_t
* in_client_
;
301 snd_seq_t
* out_client_
;
304 // One input port, many output ports.
306 OutPortMap out_ports_
; // guarded by out_ports_lock_
307 base::Lock out_ports_lock_
; // guards out_ports_
309 // Mapping from ALSA client:port to our index.
310 SourceMap source_map_
;
312 // ALSA event -> MIDI coder.
313 snd_midi_event_t
* decoder_
;
315 base::Thread send_thread_
;
316 base::Thread event_thread_
;
318 bool event_thread_shutdown_
; // guarded by shutdown_lock_
319 base::Lock shutdown_lock_
; // guards event_thread_shutdown_
321 DISALLOW_COPY_AND_ASSIGN(MidiManagerAlsa
);
326 #endif // MEDIA_MIDI_MIDI_MANAGER_ALSA_H_