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

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

Powered by Google App Engine
This is Rietveld 408576698