Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Side by Side Diff: media/midi/midi_manager_alsa.cc

Issue 1007613006: Rename CardInfo to MidiDevice and factor out some device probing logic (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Final fixes Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/midi/midi_manager_alsa.h ('k') | media/midi/midi_manager_alsa_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/midi/midi_manager_alsa.h" 5 #include "media/midi/midi_manager_alsa.h"
6 6
7 #include <alsa/asoundlib.h> 7 #include <alsa/asoundlib.h>
8 #include <stdlib.h> 8 #include <stdlib.h>
9 #include <algorithm> 9 #include <algorithm>
10 #include <string> 10 #include <string>
(...skipping 12 matching lines...) Expand all
23 namespace media { 23 namespace media {
24 24
25 namespace { 25 namespace {
26 26
27 // Per-output buffer. This can be smaller, but then large sysex messages 27 // Per-output buffer. This can be smaller, but then large sysex messages
28 // will be (harmlessly) split across multiple seq events. This should 28 // will be (harmlessly) split across multiple seq events. This should
29 // not have any real practical effect, except perhaps to slightly reorder 29 // not have any real practical effect, except perhaps to slightly reorder
30 // realtime messages with respect to sysex. 30 // realtime messages with respect to sysex.
31 const size_t kSendBufferSize = 256; 31 const size_t kSendBufferSize = 256;
32 32
33 // Minimum client id for which we will have ALSA card devices for. When we
34 // are searching for card devices (used to get the path, id, and manufacturer),
35 // we don't want to get confused by kernel clients that do not have a card.
36 // See seq_clientmgr.c in the ALSA code for this.
37 // TODO(agoode): Add proper client -> card export from the kernel to avoid
38 // hardcoding.
39 const int kMinimumClientIdForCards = 16;
40
33 // Constants for the capabilities we search for in inputs and outputs. 41 // Constants for the capabilities we search for in inputs and outputs.
34 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html. 42 // See http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html.
35 const unsigned int kRequiredInputPortCaps = 43 const unsigned int kRequiredInputPortCaps =
36 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; 44 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
37 const unsigned int kRequiredOutputPortCaps = 45 const unsigned int kRequiredOutputPortCaps =
38 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; 46 SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
39 47
40 int AddrToInt(const snd_seq_addr_t* addr) { 48 int AddrToInt(const snd_seq_addr_t* addr) {
41 return (addr->client << 8) | addr->port; 49 return (addr->client << 8) | addr->port;
42 } 50 }
(...skipping 10 matching lines...) Expand all
53 udev_(device::udev_new()), 61 udev_(device::udev_new()),
54 #endif // defined(USE_UDEV) 62 #endif // defined(USE_UDEV)
55 send_thread_("MidiSendThread"), 63 send_thread_("MidiSendThread"),
56 event_thread_("MidiEventThread"), 64 event_thread_("MidiEventThread"),
57 event_thread_shutdown_(false) { 65 event_thread_shutdown_(false) {
58 // Initialize decoder. 66 // Initialize decoder.
59 snd_midi_event_new(0, &decoder_); 67 snd_midi_event_new(0, &decoder_);
60 snd_midi_event_no_status(decoder_, 1); 68 snd_midi_event_no_status(decoder_, 1);
61 } 69 }
62 70
71 MidiManagerAlsa::~MidiManagerAlsa() {
72 // Tell the event thread it will soon be time to shut down. This gives
73 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
74 // message is lost.
75 {
76 base::AutoLock lock(shutdown_lock_);
77 event_thread_shutdown_ = true;
78 }
79
80 // Stop the send thread.
81 send_thread_.Stop();
82
83 // Close the out client. This will trigger the event thread to stop,
84 // because of SND_SEQ_EVENT_CLIENT_EXIT.
85 if (out_client_)
86 snd_seq_close(out_client_);
87
88 // Wait for the event thread to stop.
89 event_thread_.Stop();
90
91 // Close the in client.
92 if (in_client_)
93 snd_seq_close(in_client_);
94
95 // Free the decoder.
96 snd_midi_event_free(decoder_);
97
98 // Free the encoders.
99 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i)
100 snd_midi_event_free(*i);
101 }
102
63 void MidiManagerAlsa::StartInitialization() { 103 void MidiManagerAlsa::StartInitialization() {
64 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341. 104 // TODO(agoode): Move off I/O thread. See http://crbug.com/374341.
65 105
66 // Create client handles. 106 // Create client handles.
67 int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0); 107 int err = snd_seq_open(&in_client_, "hw", SND_SEQ_OPEN_INPUT, 0);
68 if (err != 0) { 108 if (err != 0) {
69 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err); 109 VLOG(1) << "snd_seq_open fails: " << snd_strerror(err);
70 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); 110 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
71 } 111 }
72 int in_client_id = snd_seq_client_id(in_client_); 112 int in_client_id = snd_seq_client_id(in_client_);
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 announce_dest.port = in_port_; 151 announce_dest.port = in_port_;
112 snd_seq_port_subscribe_set_sender(subs, &announce_sender); 152 snd_seq_port_subscribe_set_sender(subs, &announce_sender);
113 snd_seq_port_subscribe_set_dest(subs, &announce_dest); 153 snd_seq_port_subscribe_set_dest(subs, &announce_dest);
114 err = snd_seq_subscribe_port(in_client_, subs); 154 err = snd_seq_subscribe_port(in_client_, subs);
115 if (err != 0) { 155 if (err != 0) {
116 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: " 156 VLOG(1) << "snd_seq_subscribe_port on the announce port fails: "
117 << snd_strerror(err); 157 << snd_strerror(err);
118 return CompleteInitialization(MIDI_INITIALIZATION_ERROR); 158 return CompleteInitialization(MIDI_INITIALIZATION_ERROR);
119 } 159 }
120 160
121 // Extract the list of manufacturers for the hardware MIDI 161 EnumeratePorts();
122 // devices. This won't work for all devices. It is also brittle until 162
123 // hotplug is implemented. (See http://crbug.com/431489.) 163 event_thread_.Start();
124 ScopedVector<CardInfo> cards; 164 event_thread_.message_loop()->PostTask(
165 FROM_HERE,
166 base::Bind(&MidiManagerAlsa::ScheduleEventLoop, base::Unretained(this)));
167
168 CompleteInitialization(MIDI_OK);
169 }
170
171 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client,
172 uint32 port_index,
173 const std::vector<uint8>& data,
174 double timestamp) {
175 if (out_ports_.size() <= port_index)
176 return;
177
178 // Not correct right now. http://crbug.com/374341.
179 if (!send_thread_.IsRunning())
180 send_thread_.Start();
181
182 base::TimeDelta delay;
183 if (timestamp != 0.0) {
184 base::TimeTicks time_to_send =
185 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
186 timestamp * base::Time::kMicrosecondsPerSecond);
187 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
188 }
189
190 send_thread_.message_loop()->PostDelayedTask(
191 FROM_HERE, base::Bind(&MidiManagerAlsa::SendMidiData,
192 base::Unretained(this), port_index, data),
193 delay);
194
195 // Acknowledge send.
196 send_thread_.message_loop()->PostTask(
197 FROM_HERE, base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
198 base::Unretained(client), data.size()));
199 }
200
201 MidiManagerAlsa::MidiDevice::MidiDevice(const MidiManagerAlsa* outer,
202 const std::string& alsa_name,
203 const std::string& alsa_longname,
204 const std::string& alsa_driver,
205 int card_index)
206 : alsa_name_(alsa_name), alsa_driver_(alsa_driver) {
207 // Get udev properties if available.
208 std::string udev_id_vendor;
209 std::string udev_id_vendor_id;
210 std::string udev_id_vendor_from_database;
211
212 #if defined(USE_UDEV)
213 const std::string sysname = base::StringPrintf("card%i", card_index);
214 device::ScopedUdevDevicePtr udev_device(
215 device::udev_device_new_from_subsystem_sysname(outer->udev_.get(),
216 "sound", sysname.c_str()));
217 udev_id_vendor = device::UdevDecodeString(
218 device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_VENDOR_ENC"));
219 udev_id_vendor_id =
220 device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_VENDOR_ID");
221 udev_id_vendor_from_database = device::UdevDeviceGetPropertyValue(
222 udev_device.get(), "ID_VENDOR_FROM_DATABASE");
223
224 udev_id_path_ =
225 device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_PATH");
226 udev_id_id_ = device::UdevDeviceGetPropertyValue(udev_device.get(), "ID_ID");
227 #endif // defined(USE_UDEV)
228
229 manufacturer_ = ExtractManufacturerString(udev_id_vendor, udev_id_vendor_id,
230 udev_id_vendor_from_database,
231 alsa_name, alsa_longname);
232 }
233
234 MidiManagerAlsa::MidiDevice::~MidiDevice() {
235 }
236
237 const std::string MidiManagerAlsa::MidiDevice::alsa_name() const {
238 return alsa_name_;
239 }
240
241 const std::string MidiManagerAlsa::MidiDevice::manufacturer() const {
242 return manufacturer_;
243 }
244
245 const std::string MidiManagerAlsa::MidiDevice::alsa_driver() const {
246 return alsa_driver_;
247 }
248
249 const std::string MidiManagerAlsa::MidiDevice::udev_id_path() const {
250 return udev_id_path_;
251 }
252
253 const std::string MidiManagerAlsa::MidiDevice::udev_id_id() const {
254 return udev_id_id_;
255 }
256
257 // static
258 std::string MidiManagerAlsa::MidiDevice::ExtractManufacturerString(
259 const std::string& udev_id_vendor,
260 const std::string& udev_id_vendor_id,
261 const std::string& udev_id_vendor_from_database,
262 const std::string& alsa_name,
263 const std::string& alsa_longname) {
264 // Let's try to determine the manufacturer. Here is the ordered preference
265 // in extraction:
266 // 1. Vendor name from the USB device iManufacturer string, from
267 // the udev property ID_VENDOR_ENC.
268 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
269 // 3. Heuristic from ALSA.
270
271 // Is the vendor string not just the USB vendor hex id?
272 if (udev_id_vendor != udev_id_vendor_id) {
273 return udev_id_vendor;
274 }
275
276 // Is there a vendor string in the hardware database?
277 if (!udev_id_vendor_from_database.empty()) {
278 return udev_id_vendor_from_database;
279 }
280
281 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
282 // We assume that card longname is in the format of
283 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
284 // a manufacturer name here.
285 size_t at_index = alsa_longname.rfind(" at ");
286 if (std::string::npos != at_index) {
287 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
288 if (std::string::npos != name_index)
289 return alsa_longname.substr(0, name_index - 1);
290 }
291
292 // Failure.
293 return "";
294 }
295
296 ScopedVector<MidiManagerAlsa::MidiDevice> MidiManagerAlsa::AllMidiDevices() {
297 ScopedVector<MidiDevice> devices;
125 snd_ctl_card_info_t* card; 298 snd_ctl_card_info_t* card;
126 snd_rawmidi_info_t* midi_out; 299 snd_rawmidi_info_t* midi_out;
127 snd_rawmidi_info_t* midi_in; 300 snd_rawmidi_info_t* midi_in;
128 snd_ctl_card_info_alloca(&card); 301 snd_ctl_card_info_alloca(&card);
129 snd_rawmidi_info_alloca(&midi_out); 302 snd_rawmidi_info_alloca(&midi_out);
130 snd_rawmidi_info_alloca(&midi_in); 303 snd_rawmidi_info_alloca(&midi_in);
131 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0; ) { 304 for (int card_index = -1; !snd_card_next(&card_index) && card_index >= 0;) {
132 const std::string id = base::StringPrintf("hw:CARD=%i", card_index); 305 const std::string id = base::StringPrintf("hw:CARD=%i", card_index);
133 snd_ctl_t* handle; 306 snd_ctl_t* handle;
134 int err = snd_ctl_open(&handle, id.c_str(), 0); 307 int err = snd_ctl_open(&handle, id.c_str(), 0);
135 if (err != 0) { 308 if (err != 0) {
136 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err); 309 VLOG(1) << "snd_ctl_open fails: " << snd_strerror(err);
137 continue; 310 continue;
138 } 311 }
139 err = snd_ctl_card_info(handle, card); 312 err = snd_ctl_card_info(handle, card);
140 if (err != 0) { 313 if (err != 0) {
141 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err); 314 VLOG(1) << "snd_ctl_card_info fails: " << snd_strerror(err);
142 snd_ctl_close(handle); 315 snd_ctl_close(handle);
143 continue; 316 continue;
144 } 317 }
145 // Enumerate any rawmidi devices (not subdevices) and extract CardInfo. 318 // Enumerate any rawmidi devices (not subdevices) and extract MidiDevice.
146 for (int device = -1; 319 for (int device = -1;
147 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0; ) { 320 !snd_ctl_rawmidi_next_device(handle, &device) && device >= 0;) {
148 bool output; 321 bool output;
149 bool input; 322 bool input;
150 snd_rawmidi_info_set_device(midi_out, device); 323 snd_rawmidi_info_set_device(midi_out, device);
151 snd_rawmidi_info_set_subdevice(midi_out, 0); 324 snd_rawmidi_info_set_subdevice(midi_out, 0);
152 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT); 325 snd_rawmidi_info_set_stream(midi_out, SND_RAWMIDI_STREAM_OUTPUT);
153 output = snd_ctl_rawmidi_info(handle, midi_out) == 0; 326 output = snd_ctl_rawmidi_info(handle, midi_out) == 0;
154 snd_rawmidi_info_set_device(midi_in, device); 327 snd_rawmidi_info_set_device(midi_in, device);
155 snd_rawmidi_info_set_subdevice(midi_in, 0); 328 snd_rawmidi_info_set_subdevice(midi_in, 0);
156 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT); 329 snd_rawmidi_info_set_stream(midi_in, SND_RAWMIDI_STREAM_INPUT);
157 input = snd_ctl_rawmidi_info(handle, midi_in) == 0; 330 input = snd_ctl_rawmidi_info(handle, midi_in) == 0;
158 if (!output && !input) 331 if (!output && !input)
159 continue; 332 continue;
160 333
161 // Compute and save Alsa and udev properties. 334 // Compute and save ALSA and udev properties.
162 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in; 335 snd_rawmidi_info_t* midi = midi_out ? midi_out : midi_in;
163 cards.push_back(new CardInfo( 336 devices.push_back(new MidiDevice(this, snd_rawmidi_info_get_name(midi),
164 this, 337 snd_ctl_card_info_get_longname(card),
165 snd_rawmidi_info_get_name(midi), 338 snd_ctl_card_info_get_driver(card),
166 snd_ctl_card_info_get_longname(card), 339 card_index));
167 snd_ctl_card_info_get_driver(card),
168 card_index));
169 } 340 }
170 snd_ctl_close(handle); 341 snd_ctl_close(handle);
171 } 342 }
172 343
173 // Enumerate all ports in all clients. 344 return devices.Pass();
345 }
346
347 void MidiManagerAlsa::EnumeratePorts() {
348 ScopedVector<MidiDevice> devices = AllMidiDevices();
349
350 snd_seq_port_subscribe_t* subs;
351 snd_seq_port_subscribe_alloca(&subs);
352
353 int in_client_id = snd_seq_client_id(in_client_);
354
355 // Enumerate all clients.
174 snd_seq_client_info_t* client_info; 356 snd_seq_client_info_t* client_info;
175 snd_seq_client_info_alloca(&client_info); 357 snd_seq_client_info_alloca(&client_info);
176 snd_seq_port_info_t* port_info; 358 snd_seq_port_info_t* port_info;
177 snd_seq_port_info_alloca(&port_info); 359 snd_seq_port_info_alloca(&port_info);
178 360
361 // Enumerate clients.
179 snd_seq_client_info_set_client(client_info, -1); 362 snd_seq_client_info_set_client(client_info, -1);
180 // Enumerate clients.
181 uint32 current_input = 0; 363 uint32 current_input = 0;
182 unsigned int current_card = 0; 364 unsigned int current_device = 0;
183 while (!snd_seq_query_next_client(in_client_, client_info)) { 365 while (!snd_seq_query_next_client(in_client_, client_info)) {
184 int client_id = snd_seq_client_info_get_client(client_info); 366 int client_id = snd_seq_client_info_get_client(client_info);
185 if ((client_id == in_client_id) || (client_id == out_client_id_)) { 367 if ((client_id == in_client_id) || (client_id == out_client_id_)) {
186 // Skip our own clients. 368 // Skip our own clients.
187 continue; 369 continue;
188 } 370 }
371
372 // Get client metadata.
189 const std::string client_name = snd_seq_client_info_get_name(client_info); 373 const std::string client_name = snd_seq_client_info_get_name(client_info);
190 snd_seq_port_info_set_client(port_info, client_id); 374 snd_seq_port_info_set_client(port_info, client_id);
191 snd_seq_port_info_set_port(port_info, -1); 375 snd_seq_port_info_set_port(port_info, -1);
192 376
193 std::string manufacturer; 377 std::string manufacturer;
194 std::string driver; 378 std::string driver;
195 // In the current Alsa kernel implementation, hardware clients match the 379 std::string udev_id_path;
196 // cards in the same order. 380 std::string udev_id_id;
381
382 // Join kernel clients against the list of MidiDevices.
383 // In the current ALSA kernel implementation, kernel clients match the
384 // kernel devices in the same order, for devices with client_id over
385 // kMinimumClientIdForCards.
197 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) && 386 if ((snd_seq_client_info_get_type(client_info) == SND_SEQ_KERNEL_CLIENT) &&
198 (current_card < cards.size())) { 387 (current_device < devices.size()) &&
199 const CardInfo* info = cards[current_card]; 388 (client_id >= kMinimumClientIdForCards)) {
200 if (info->alsa_name() == client_name) { 389 const MidiDevice* device = devices[current_device];
201 manufacturer = info->manufacturer(); 390 manufacturer = device->manufacturer();
202 driver = info->alsa_driver(); 391 driver = device->alsa_driver();
203 current_card++; 392 udev_id_path = device->udev_id_path();
204 } 393 udev_id_id = device->udev_id_id();
394 current_device++;
205 } 395 }
206 // Enumerate ports. 396 // Enumerate ports.
207 while (!snd_seq_query_next_port(in_client_, port_info)) { 397 while (!snd_seq_query_next_port(in_client_, port_info)) {
208 unsigned int port_type = snd_seq_port_info_get_type(port_info); 398 unsigned int port_type = snd_seq_port_info_get_type(port_info);
209 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) { 399 if (port_type & SND_SEQ_PORT_TYPE_MIDI_GENERIC) {
210 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info); 400 const snd_seq_addr_t* addr = snd_seq_port_info_get_addr(port_info);
211 const std::string name = snd_seq_port_info_get_name(port_info); 401 const std::string name = snd_seq_port_info_get_name(port_info);
212 const std::string id = base::StringPrintf("%d:%d %s", 402 const std::string id_suffix =
213 addr->client, 403 base::StringPrintf("%d:%d/%s/%s", addr->client, addr->port,
214 addr->port, 404 udev_id_path.c_str(), udev_id_id.c_str());
215 name.c_str());
216 std::string version; 405 std::string version;
217 if (!driver.empty()) { 406 if (!driver.empty()) {
218 version = driver + " / "; 407 version = driver + " / ";
219 } 408 }
220 version += base::StringPrintf("ALSA library version %d.%d.%d", 409 version += base::StringPrintf("ALSA library version %d.%d.%d",
221 SND_LIB_MAJOR, 410 SND_LIB_MAJOR,
222 SND_LIB_MINOR, 411 SND_LIB_MINOR,
223 SND_LIB_SUBMINOR); 412 SND_LIB_SUBMINOR);
224 unsigned int caps = snd_seq_port_info_get_capability(port_info); 413 unsigned int caps = snd_seq_port_info_get_capability(port_info);
225 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) { 414 if ((caps & kRequiredInputPortCaps) == kRequiredInputPortCaps) {
226 // Subscribe to this port. 415 // Subscribe to this port.
227 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info); 416 const snd_seq_addr_t* sender = snd_seq_port_info_get_addr(port_info);
228 snd_seq_addr_t dest; 417 snd_seq_addr_t dest;
229 dest.client = snd_seq_client_id(in_client_); 418 dest.client = snd_seq_client_id(in_client_);
230 dest.port = in_port_; 419 dest.port = in_port_;
231 snd_seq_port_subscribe_set_sender(subs, sender); 420 snd_seq_port_subscribe_set_sender(subs, sender);
232 snd_seq_port_subscribe_set_dest(subs, &dest); 421 snd_seq_port_subscribe_set_dest(subs, &dest);
233 err = snd_seq_subscribe_port(in_client_, subs); 422 int err = snd_seq_subscribe_port(in_client_, subs);
234 if (err != 0) { 423 if (err != 0) {
235 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); 424 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
236 } else { 425 } else {
237 source_map_[AddrToInt(sender)] = current_input++; 426 source_map_[AddrToInt(sender)] = current_input++;
238 AddInputPort(MidiPortInfo( 427 AddInputPort(
239 id, manufacturer, name, version, MIDI_PORT_OPENED)); 428 MidiPortInfo(base::StringPrintf("input/%s", id_suffix.c_str()),
429 manufacturer, name, version, MIDI_PORT_OPENED));
240 } 430 }
241 } 431 }
242 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) { 432 if ((caps & kRequiredOutputPortCaps) == kRequiredOutputPortCaps) {
243 // Create a port for us to send on. 433 // Create a port for us to send on.
244 int out_port = 434 int out_port =
245 snd_seq_create_simple_port(out_client_, NULL, 435 snd_seq_create_simple_port(out_client_, NULL,
246 SND_SEQ_PORT_CAP_READ | 436 SND_SEQ_PORT_CAP_READ |
247 SND_SEQ_PORT_CAP_NO_EXPORT, 437 SND_SEQ_PORT_CAP_NO_EXPORT,
248 SND_SEQ_PORT_TYPE_MIDI_GENERIC | 438 SND_SEQ_PORT_TYPE_MIDI_GENERIC |
249 SND_SEQ_PORT_TYPE_APPLICATION); 439 SND_SEQ_PORT_TYPE_APPLICATION);
250 if (out_port < 0) { 440 if (out_port < 0) {
251 VLOG(1) << "snd_seq_create_simple_port fails: " 441 VLOG(1) << "snd_seq_create_simple_port fails: "
252 << snd_strerror(out_port); 442 << snd_strerror(out_port);
253 // Skip this output port for now. 443 // Skip this output port for now.
254 continue; 444 continue;
255 } 445 }
256 446
257 // Activate port subscription. 447 // Activate port subscription.
258 snd_seq_addr_t sender; 448 snd_seq_addr_t sender;
259 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info); 449 const snd_seq_addr_t* dest = snd_seq_port_info_get_addr(port_info);
260 sender.client = snd_seq_client_id(out_client_); 450 sender.client = snd_seq_client_id(out_client_);
261 sender.port = out_port; 451 sender.port = out_port;
262 snd_seq_port_subscribe_set_sender(subs, &sender); 452 snd_seq_port_subscribe_set_sender(subs, &sender);
263 snd_seq_port_subscribe_set_dest(subs, dest); 453 snd_seq_port_subscribe_set_dest(subs, dest);
264 err = snd_seq_subscribe_port(out_client_, subs); 454 int err = snd_seq_subscribe_port(out_client_, subs);
265 if (err != 0) { 455 if (err != 0) {
266 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err); 456 VLOG(1) << "snd_seq_subscribe_port fails: " << snd_strerror(err);
267 snd_seq_delete_simple_port(out_client_, out_port); 457 snd_seq_delete_simple_port(out_client_, out_port);
268 } else { 458 } else {
269 snd_midi_event_t* encoder; 459 snd_midi_event_t* encoder;
270 snd_midi_event_new(kSendBufferSize, &encoder); 460 snd_midi_event_new(kSendBufferSize, &encoder);
271 encoders_.push_back(encoder); 461 encoders_.push_back(encoder);
272 out_ports_.push_back(out_port); 462 out_ports_.push_back(out_port);
273 AddOutputPort(MidiPortInfo( 463 AddOutputPort(
274 id, manufacturer, name, version, MIDI_PORT_OPENED)); 464 MidiPortInfo(base::StringPrintf("output/%s", id_suffix.c_str()),
465 manufacturer, name, version, MIDI_PORT_OPENED));
275 } 466 }
276 } 467 }
277 } 468 }
278 } 469 }
279 } 470 }
280
281 event_thread_.Start();
282 event_thread_.message_loop()->PostTask(
283 FROM_HERE,
284 base::Bind(&MidiManagerAlsa::EventReset, base::Unretained(this)));
285
286 CompleteInitialization(MIDI_OK);
287 }
288
289 MidiManagerAlsa::~MidiManagerAlsa() {
290 // Tell the event thread it will soon be time to shut down. This gives
291 // us assurance the thread will stop in case the SND_SEQ_EVENT_CLIENT_EXIT
292 // message is lost.
293 {
294 base::AutoLock lock(shutdown_lock_);
295 event_thread_shutdown_ = true;
296 }
297
298 // Stop the send thread.
299 send_thread_.Stop();
300
301 // Close the out client. This will trigger the event thread to stop,
302 // because of SND_SEQ_EVENT_CLIENT_EXIT.
303 if (out_client_)
304 snd_seq_close(out_client_);
305
306 // Wait for the event thread to stop.
307 event_thread_.Stop();
308
309 // Close the in client.
310 if (in_client_)
311 snd_seq_close(in_client_);
312
313 // Free the decoder.
314 snd_midi_event_free(decoder_);
315
316 // Free the encoders.
317 for (EncoderList::iterator i = encoders_.begin(); i != encoders_.end(); ++i)
318 snd_midi_event_free(*i);
319 }
320
321 MidiManagerAlsa::CardInfo::CardInfo(
322 const MidiManagerAlsa* outer,
323 const std::string& alsa_name, const std::string& alsa_longname,
324 const std::string& alsa_driver, int card_index)
325 : alsa_name_(alsa_name), alsa_driver_(alsa_driver) {
326 // Get udev properties if available.
327 std::string udev_id_vendor;
328 std::string udev_id_vendor_id;
329 std::string udev_id_vendor_from_database;
330
331 #if defined(USE_UDEV)
332 const std::string sysname = base::StringPrintf("card%i", card_index);
333 device::ScopedUdevDevicePtr udev_device(
334 device::udev_device_new_from_subsystem_sysname(
335 outer->udev_.get(), "sound", sysname.c_str()));
336 udev_id_vendor = device::UdevDecodeString(device::UdevDeviceGetPropertyValue(
337 udev_device.get(), "ID_VENDOR_ENC"));
338 udev_id_vendor_id = device::UdevDeviceGetPropertyValue(
339 udev_device.get(), "ID_VENDOR_ID");
340 udev_id_vendor_from_database = device::UdevDeviceGetPropertyValue(
341 udev_device.get(), "ID_VENDOR_FROM_DATABASE");
342
343 udev_id_path_ = device::UdevDeviceGetPropertyValue(
344 udev_device.get(), "ID_PATH");
345 udev_id_id_ = device::UdevDeviceGetPropertyValue(
346 udev_device.get(), "ID_ID");
347 #endif // defined(USE_UDEV)
348
349 manufacturer_ = ExtractManufacturerString(
350 udev_id_vendor, udev_id_vendor_id, udev_id_vendor_from_database,
351 alsa_name, alsa_longname);
352 }
353
354 MidiManagerAlsa::CardInfo::~CardInfo() {
355 }
356
357 const std::string MidiManagerAlsa::CardInfo::alsa_name() const {
358 return alsa_name_;
359 }
360
361 const std::string MidiManagerAlsa::CardInfo::manufacturer() const {
362 return manufacturer_;
363 }
364
365 const std::string MidiManagerAlsa::CardInfo::alsa_driver() const {
366 return alsa_driver_;
367 }
368
369 const std::string MidiManagerAlsa::CardInfo::udev_id_path() const {
370 return udev_id_path_;
371 }
372
373 const std::string MidiManagerAlsa::CardInfo::udev_id_id() const {
374 return udev_id_id_;
375 } 471 }
376 472
377 void MidiManagerAlsa::SendMidiData(uint32 port_index, 473 void MidiManagerAlsa::SendMidiData(uint32 port_index,
378 const std::vector<uint8>& data) { 474 const std::vector<uint8>& data) {
379 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread()); 475 DCHECK(send_thread_.message_loop_proxy()->BelongsToCurrentThread());
380 476
381 snd_midi_event_t* encoder = encoders_[port_index]; 477 snd_midi_event_t* encoder = encoders_[port_index];
382 for (unsigned int i = 0; i < data.size(); i++) { 478 for (unsigned int i = 0; i < data.size(); i++) {
383 snd_seq_event_t event; 479 snd_seq_event_t event;
384 int result = snd_midi_event_encode_byte(encoder, data[i], &event); 480 int result = snd_midi_event_encode_byte(encoder, data[i], &event);
385 if (result == 1) { 481 if (result == 1) {
386 // Full event, send it. 482 // Full event, send it.
387 snd_seq_ev_set_source(&event, out_ports_[port_index]); 483 snd_seq_ev_set_source(&event, out_ports_[port_index]);
388 snd_seq_ev_set_subs(&event); 484 snd_seq_ev_set_subs(&event);
389 snd_seq_ev_set_direct(&event); 485 snd_seq_ev_set_direct(&event);
390 snd_seq_event_output_direct(out_client_, &event); 486 snd_seq_event_output_direct(out_client_, &event);
391 } 487 }
392 } 488 }
393 } 489 }
394 490
395 void MidiManagerAlsa::DispatchSendMidiData(MidiManagerClient* client, 491 void MidiManagerAlsa::ScheduleEventLoop() {
396 uint32 port_index,
397 const std::vector<uint8>& data,
398 double timestamp) {
399 if (out_ports_.size() <= port_index)
400 return;
401
402 // Not correct right now. http://crbug.com/374341.
403 if (!send_thread_.IsRunning())
404 send_thread_.Start();
405
406 base::TimeDelta delay;
407 if (timestamp != 0.0) {
408 base::TimeTicks time_to_send =
409 base::TimeTicks() + base::TimeDelta::FromMicroseconds(
410 timestamp * base::Time::kMicrosecondsPerSecond);
411 delay = std::max(time_to_send - base::TimeTicks::Now(), base::TimeDelta());
412 }
413
414 send_thread_.message_loop()->PostDelayedTask(
415 FROM_HERE,
416 base::Bind(&MidiManagerAlsa::SendMidiData, base::Unretained(this),
417 port_index, data), delay);
418
419 // Acknowledge send.
420 send_thread_.message_loop()->PostTask(
421 FROM_HERE,
422 base::Bind(&MidiManagerClient::AccumulateMidiBytesSent,
423 base::Unretained(client), data.size()));
424 }
425
426 void MidiManagerAlsa::EventReset() {
427 event_thread_.message_loop()->PostTask( 492 event_thread_.message_loop()->PostTask(
428 FROM_HERE, 493 FROM_HERE,
429 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this))); 494 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
430 } 495 }
431 496
432 void MidiManagerAlsa::EventLoop() { 497 void MidiManagerAlsa::EventLoop() {
433 // Read available incoming MIDI data. 498 // Read available incoming MIDI data.
434 snd_seq_event_t* event; 499 snd_seq_event_t* event;
435 int err = snd_seq_event_input(in_client_, &event); 500 int err = snd_seq_event_input(in_client_, &event);
436 double timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); 501 double timestamp = (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
502
503 // Handle errors.
437 if (err == -ENOSPC) { 504 if (err == -ENOSPC) {
438 VLOG(1) << "snd_seq_event_input detected buffer overrun"; 505 VLOG(1) << "snd_seq_event_input detected buffer overrun";
506 // We've lost events: check another way to see if we need to shut down.
507 base::AutoLock lock(shutdown_lock_);
508 if (!event_thread_shutdown_)
509 ScheduleEventLoop();
510 return;
511 } else if (err < 0) {
512 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err);
513 // TODO(agoode): Use RecordAction() or similar to log this.
514 return;
515 }
439 516
440 // We've lost events: check another way to see if we need to shut down. 517 // Handle announce events.
441 base::AutoLock lock(shutdown_lock_); 518 if (event->source.client == SND_SEQ_CLIENT_SYSTEM &&
442 if (event_thread_shutdown_) { 519 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE) {
443 return; 520 switch (event->type) {
444 } 521 case SND_SEQ_EVENT_CLIENT_START:
445 } else if (err < 0) { 522 // TODO(agoode): rescan hardware devices.
446 VLOG(1) << "snd_seq_event_input fails: " << snd_strerror(err); 523 break;
447 return; 524
448 } else { 525 case SND_SEQ_EVENT_CLIENT_EXIT:
449 // Check for disconnection of out client. This means "shut down". 526 // Check for disconnection of our "out" client. This means "shut down".
450 if (event->source.client == SND_SEQ_CLIENT_SYSTEM && 527 if (event->data.addr.client == out_client_id_)
451 event->source.port == SND_SEQ_PORT_SYSTEM_ANNOUNCE && 528 return;
452 event->type == SND_SEQ_EVENT_CLIENT_EXIT && 529
453 event->data.addr.client == out_client_id_) { 530 // TODO(agoode): remove all ports for a client.
454 return; 531 break;
532
533 case SND_SEQ_EVENT_PORT_START:
534 // TODO(agoode): add port.
535 break;
536
537 case SND_SEQ_EVENT_PORT_EXIT:
538 // TODO(agoode): remove port.
539 break;
455 } 540 }
541 }
456 542
457 std::map<int, uint32>::iterator source_it = 543 ProcessSingleEvent(event, timestamp);
458 source_map_.find(AddrToInt(&event->source)); 544
459 if (source_it != source_map_.end()) { 545 // Do again.
460 uint32 source = source_it->second; 546 ScheduleEventLoop();
461 if (event->type == SND_SEQ_EVENT_SYSEX) { 547 }
462 // Special! Variable-length sysex. 548
463 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr), 549 void MidiManagerAlsa::ProcessSingleEvent(snd_seq_event_t* event,
464 event->data.ext.len, 550 double timestamp) {
465 timestamp); 551 std::map<int, uint32>::iterator source_it =
552 source_map_.find(AddrToInt(&event->source));
553 if (source_it != source_map_.end()) {
554 uint32 source = source_it->second;
555 if (event->type == SND_SEQ_EVENT_SYSEX) {
556 // Special! Variable-length sysex.
557 ReceiveMidiData(source, static_cast<const uint8*>(event->data.ext.ptr),
558 event->data.ext.len, timestamp);
559 } else {
560 // Otherwise, decode this and send that on.
561 unsigned char buf[12];
562 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
563 if (count <= 0) {
564 if (count != -ENOENT) {
565 // ENOENT means that it's not a MIDI message, which is not an
566 // error, but other negative values are errors for us.
567 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
568 // TODO(agoode): Record this failure.
569 }
466 } else { 570 } else {
467 // Otherwise, decode this and send that on. 571 ReceiveMidiData(source, buf, count, timestamp);
468 unsigned char buf[12];
469 long count = snd_midi_event_decode(decoder_, buf, sizeof(buf), event);
470 if (count <= 0) {
471 if (count != -ENOENT) {
472 // ENOENT means that it's not a MIDI message, which is not an
473 // error, but other negative values are errors for us.
474 VLOG(1) << "snd_midi_event_decoder fails " << snd_strerror(count);
475 }
476 } else {
477 ReceiveMidiData(source, buf, count, timestamp);
478 }
479 } 572 }
480 } 573 }
481 } 574 }
482
483 // Do again.
484 event_thread_.message_loop()->PostTask(
485 FROM_HERE,
486 base::Bind(&MidiManagerAlsa::EventLoop, base::Unretained(this)));
487 }
488
489 // static
490 std::string MidiManagerAlsa::CardInfo::ExtractManufacturerString(
491 const std::string& udev_id_vendor,
492 const std::string& udev_id_vendor_id,
493 const std::string& udev_id_vendor_from_database,
494 const std::string& alsa_name,
495 const std::string& alsa_longname) {
496 // Let's try to determine the manufacturer. Here is the ordered preference
497 // in extraction:
498 // 1. Vendor name from the USB device iManufacturer string, from
499 // the udev property ID_VENDOR_ENC.
500 // 2. Vendor name from the udev database (property ID_VENDOR_FROM_DATABASE).
501 // 3. Heuristic from ALSA.
502
503 // Is the vendor string not just the USB vendor hex id?
504 if (udev_id_vendor != udev_id_vendor_id) {
505 return udev_id_vendor;
506 }
507
508 // Is there a vendor string in the hardware database?
509 if (!udev_id_vendor_from_database.empty()) {
510 return udev_id_vendor_from_database;
511 }
512
513 // Ok, udev gave us nothing useful, or was unavailable. So try a heuristic.
514 // We assume that card longname is in the format of
515 // "<manufacturer> <name> at <bus>". Otherwise, we give up to detect
516 // a manufacturer name here.
517 size_t at_index = alsa_longname.rfind(" at ");
518 if (std::string::npos != at_index) {
519 size_t name_index = alsa_longname.rfind(alsa_name, at_index - 1);
520 if (std::string::npos != name_index)
521 return alsa_longname.substr(0, name_index - 1);
522 }
523
524 // Failure.
525 return "";
526 } 575 }
527 576
528 MidiManager* MidiManager::Create() { 577 MidiManager* MidiManager::Create() {
529 return new MidiManagerAlsa(); 578 return new MidiManagerAlsa();
530 } 579 }
531 580
532 } // namespace media 581 } // namespace media
OLDNEW
« no previous file with comments | « media/midi/midi_manager_alsa.h ('k') | media/midi/midi_manager_alsa_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698