]> git.sesse.net Git - nageru/blob - alsa_pool.cpp
Move ALSAPool into its own file; it is pretty large now.
[nageru] / alsa_pool.cpp
1 #include "alsa_pool.h"
2
3 #include <alsa/control.h>
4 #include <alsa/error.h>
5 #include <alsa/pcm.h>
6 #include <assert.h>
7 #include <limits.h>
8 #include <stdio.h>
9 #include <sys/inotify.h>
10 #include <unistd.h>
11 #include <algorithm>
12 #include <memory>
13
14 #include "alsa_input.h"
15 #include "audio_mixer.h"
16 #include "defs.h"
17 #include "input_mapping.h"
18 #include "state.pb.h"
19
20 using namespace std;
21 using namespace std::placeholders;
22
23 ALSAPool::~ALSAPool()
24 {
25         for (Device &device : devices) {
26                 if (device.input != nullptr) {
27                         device.input->stop_capture_thread();
28                 }
29         }
30 }
31
32 std::vector<ALSAPool::Device> ALSAPool::get_devices()
33 {
34         lock_guard<mutex> lock(mu);
35         for (Device &device : devices) {
36                 device.held = true;
37         }
38         return devices;
39 }
40
41 void ALSAPool::hold_device(unsigned index)
42 {
43         lock_guard<mutex> lock(mu);
44         assert(index < devices.size());
45         devices[index].held = true;
46 }
47
48 void ALSAPool::release_device(unsigned index)
49 {
50         lock_guard<mutex> lock(mu);
51         if (index < devices.size()) {
52                 devices[index].held = false;
53         }
54 }
55
56 void ALSAPool::enumerate_devices()
57 {
58         // Enumerate all cards.
59         for (int card_index = -1; snd_card_next(&card_index) == 0 && card_index >= 0; ) {
60                 char address[256];
61                 snprintf(address, sizeof(address), "hw:%d", card_index);
62
63                 snd_ctl_t *ctl;
64                 int err = snd_ctl_open(&ctl, address, 0);
65                 if (err < 0) {
66                         printf("%s: %s\n", address, snd_strerror(err));
67                         continue;
68                 }
69                 unique_ptr<snd_ctl_t, decltype(snd_ctl_close)*> ctl_closer(ctl, snd_ctl_close);
70
71                 // Enumerate all devices on this card.
72                 for (int dev_index = -1; snd_ctl_pcm_next_device(ctl, &dev_index) == 0 && dev_index >= 0; ) {
73                         probe_device_with_retry(card_index, dev_index);
74                 }
75         }
76 }
77
78 void ALSAPool::probe_device_with_retry(unsigned card_index, unsigned dev_index)
79 {
80         char address[256];
81         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
82
83         lock_guard<mutex> lock(add_device_mutex);
84         if (add_device_tries_left.count(address)) {
85                 // Some thread is already busy retrying this,
86                 // so just reset its count.
87                 add_device_tries_left[address] = num_retries;
88                 return;
89         }
90
91         // Try (while still holding the lock) to add the device synchronously.
92         ProbeResult result = probe_device_once(card_index, dev_index);
93         if (result == ProbeResult::SUCCESS) {
94                 return;
95         } else if (result == ProbeResult::FAILURE) {
96                 return;
97         }
98         assert(result == ProbeResult::DEFER);
99
100         // Add failed for whatever reason (probably just that the device
101         // isn't up yet. Set up a count so that nobody else starts a thread,
102         // then start it ourselves.
103         fprintf(stderr, "Trying %s again in one second...\n", address);
104         add_device_tries_left[address] = num_retries;
105         thread(&ALSAPool::probe_device_retry_thread_func, this, card_index, dev_index).detach();
106 }
107
108 void ALSAPool::probe_device_retry_thread_func(unsigned card_index, unsigned dev_index)
109 {
110         char address[256];
111         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
112
113         for ( ;; ) {  // Termination condition within the loop.
114                 sleep(1);
115
116                 // See if there are any retries left.
117                 lock_guard<mutex> lock(add_device_mutex);
118                 if (!add_device_tries_left.count(address) ||
119                     add_device_tries_left[address] == 0) {
120                         add_device_tries_left.erase(address);
121                         fprintf(stderr, "Giving up probe of %s.\n", address);
122                         return;
123                 }
124
125                 // Seemingly there were. Give it a try (we still hold the mutex).
126                 ProbeResult result = probe_device_once(card_index, dev_index);
127                 if (result == ProbeResult::SUCCESS) {
128                         add_device_tries_left.erase(address);
129                         fprintf(stderr, "Probe of %s succeeded.\n", address);
130                         return;
131                 } else if (result == ProbeResult::FAILURE || --add_device_tries_left[address] == 0) {
132                         add_device_tries_left.erase(address);
133                         fprintf(stderr, "Giving up probe of %s.\n", address);
134                         return;
135                 }
136
137                 // Failed again.
138                 assert(result == ProbeResult::DEFER);
139                 fprintf(stderr, "Trying %s again in one second (%d tries left)...\n",
140                         address, add_device_tries_left[address]);
141         }
142 }
143
144 ALSAPool::ProbeResult ALSAPool::probe_device_once(unsigned card_index, unsigned dev_index)
145 {
146         char address[256];
147         snprintf(address, sizeof(address), "hw:%d", card_index);
148         snd_ctl_t *ctl;
149         int err = snd_ctl_open(&ctl, address, 0);
150         if (err < 0) {
151                 printf("%s: %s\n", address, snd_strerror(err));
152                 return ALSAPool::ProbeResult::DEFER;
153         }
154         unique_ptr<snd_ctl_t, decltype(snd_ctl_close)*> ctl_closer(ctl, snd_ctl_close);
155
156         snd_pcm_info_t *pcm_info;
157         snd_pcm_info_alloca(&pcm_info);
158         snd_pcm_info_set_device(pcm_info, dev_index);
159         snd_pcm_info_set_subdevice(pcm_info, 0);
160         snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_CAPTURE);
161         if (snd_ctl_pcm_info(ctl, pcm_info) < 0) {
162                 // Not available for capture.
163                 printf("%s: Not available for capture.\n", address);
164                 return ALSAPool::ProbeResult::DEFER;
165         }
166
167         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
168
169         unsigned num_channels = 0;
170
171         // Find all channel maps for this device, and pick out the one
172         // with the most channels.
173         snd_pcm_chmap_query_t **cmaps = snd_pcm_query_chmaps_from_hw(card_index, dev_index, 0, SND_PCM_STREAM_CAPTURE);
174         if (cmaps != nullptr) {
175                 for (snd_pcm_chmap_query_t **ptr = cmaps; *ptr; ++ptr) {
176                         num_channels = max(num_channels, (*ptr)->map.channels);
177                 }
178                 snd_pcm_free_chmaps(cmaps);
179         }
180         if (num_channels == 0) {
181                 // Device had no channel maps. We need to open it to query.
182                 // TODO: Do this asynchronously.
183                 snd_pcm_t *pcm_handle;
184                 int err = snd_pcm_open(&pcm_handle, address, SND_PCM_STREAM_CAPTURE, 0);
185                 if (err < 0) {
186                         printf("%s: %s\n", address, snd_strerror(err));
187                         return ALSAPool::ProbeResult::DEFER;
188                 }
189                 snd_pcm_hw_params_t *hw_params;
190                 snd_pcm_hw_params_alloca(&hw_params);
191                 unsigned sample_rate;
192                 if (!ALSAInput::set_base_params(address, pcm_handle, hw_params, &sample_rate)) {
193                         snd_pcm_close(pcm_handle);
194                         return ALSAPool::ProbeResult::DEFER;
195                 }
196                 err = snd_pcm_hw_params_get_channels_max(hw_params, &num_channels);
197                 if (err < 0) {
198                         fprintf(stderr, "[%s] snd_pcm_hw_params_get_channels_max(): %s\n",
199                                 address, snd_strerror(err));
200                         snd_pcm_close(pcm_handle);
201                         return ALSAPool::ProbeResult::DEFER;
202                 }
203                 snd_pcm_close(pcm_handle);
204         }
205
206         if (num_channels == 0) {
207                 printf("%s: No channel maps with channels\n", address);
208                 return ALSAPool::ProbeResult::FAILURE;
209         }
210
211         snd_ctl_card_info_t *card_info;
212         snd_ctl_card_info_alloca(&card_info);
213         snd_ctl_card_info(ctl, card_info);
214
215         string name = snd_ctl_card_info_get_name(card_info);
216         string info = snd_pcm_info_get_name(pcm_info);
217
218         unsigned internal_dev_index;
219         string display_name;
220         {
221                 lock_guard<mutex> lock(mu);
222                 internal_dev_index = find_free_device_index(name, info, num_channels, address);
223                 devices[internal_dev_index].address = address;
224                 devices[internal_dev_index].name = name;
225                 devices[internal_dev_index].info = info;
226                 devices[internal_dev_index].num_channels = num_channels;
227                 // Note: Purposefully does not overwrite held.
228
229                 display_name = devices[internal_dev_index].display_name();
230         }
231
232         fprintf(stderr, "%s: Probed successfully.\n", address);
233
234         reset_device(internal_dev_index);  // Restarts it if it is held (ie., we just replaced a dead card).
235
236         DeviceSpec spec{InputSourceType::ALSA_INPUT, internal_dev_index};
237         global_audio_mixer->set_display_name(spec, display_name);
238         global_audio_mixer->trigger_state_changed_callback();
239
240         return ALSAPool::ProbeResult::SUCCESS;
241 }
242
243 void ALSAPool::unplug_device(unsigned card_index, unsigned dev_index)
244 {
245         char address[256];
246         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
247         for (unsigned i = 0; i < devices.size(); ++i) {
248                 if (devices[i].state != Device::State::EMPTY &&
249                     devices[i].state != Device::State::DEAD &&
250                     devices[i].address == address) {
251                         free_card(i);
252                 }
253         }
254 }
255
256 void ALSAPool::init()
257 {
258         thread(&ALSAPool::inotify_thread_func, this).detach();
259         enumerate_devices();
260 }
261
262 void ALSAPool::inotify_thread_func()
263 {
264         int inotify_fd = inotify_init();
265         if (inotify_fd == -1) {
266                 perror("inotify_init()");
267                 fprintf(stderr, "No hotplug of ALSA devices available.\n");
268                 return;
269         }
270
271         int watch_fd = inotify_add_watch(inotify_fd, "/dev/snd", IN_MOVE | IN_CREATE | IN_DELETE);
272         if (watch_fd == -1) {
273                 perror("inotify_add_watch()");
274                 fprintf(stderr, "No hotplug of ALSA devices available.\n");
275                 close(inotify_fd);
276                 return;
277         }
278
279         int size = sizeof(inotify_event) + NAME_MAX + 1;
280         unique_ptr<char[]> buf(new char[size]);
281         for ( ;; ) {
282                 int ret = read(inotify_fd, buf.get(), size);
283                 if (ret < int(sizeof(inotify_event))) {
284                         fprintf(stderr, "inotify read unexpectedly returned %d, giving up hotplug of ALSA devices.\n",
285                                 int(ret));
286                         close(watch_fd);
287                         close(inotify_fd);
288                         return;
289                 }
290
291                 for (int i = 0; i < ret; ) {
292                         const inotify_event *event = reinterpret_cast<const inotify_event *>(&buf[i]);
293                         i += sizeof(inotify_event) + event->len;
294
295                         if (event->mask & IN_Q_OVERFLOW) {
296                                 fprintf(stderr, "WARNING: inotify overflowed, may lose ALSA hotplug events.\n");
297                                 continue;
298                         }
299                         unsigned card, device;
300                         char type;
301                         if (sscanf(event->name, "pcmC%uD%u%c", &card, &device, &type) == 3 && type == 'c') {
302                                 if (event->mask & (IN_MOVED_FROM | IN_DELETE)) {
303                                         printf("Deleted capture device: Card %u, device %u\n", card, device);
304                                         unplug_device(card, device);
305                                 }
306                                 if (event->mask & (IN_MOVED_TO | IN_CREATE)) {
307                                         printf("Adding capture device: Card %u, device %u\n", card, device);
308                                         probe_device_with_retry(card, device);
309                                 }
310                         }
311                 }
312         }
313 }
314
315 void ALSAPool::reset_device(unsigned index)
316 {
317         lock_guard<mutex> lock(mu);
318         Device *device = &devices[index];
319         if (inputs[index] != nullptr) {
320                 inputs[index]->stop_capture_thread();
321         }
322         if (!device->held) {
323                 inputs[index].reset();
324         } else {
325                 // TODO: Put on a background thread instead of locking?
326                 auto callback = bind(&AudioMixer::add_audio, global_audio_mixer, DeviceSpec{InputSourceType::ALSA_INPUT, index}, _1, _2, _3, _4);
327                 inputs[index].reset(new ALSAInput(device->address.c_str(), OUTPUT_FREQUENCY, device->num_channels, callback, this, index));
328                 inputs[index]->start_capture_thread();
329         }
330         device->input = inputs[index].get();
331 }
332
333 unsigned ALSAPool::get_capture_frequency(unsigned index)
334 {
335         lock_guard<mutex> lock(mu);
336         assert(devices[index].held);
337         if (devices[index].input)
338                 return devices[index].input->get_sample_rate();
339         else
340                 return OUTPUT_FREQUENCY;
341 }
342
343 ALSAPool::Device::State ALSAPool::get_card_state(unsigned index)
344 {
345         lock_guard<mutex> lock(mu);
346         assert(devices[index].held);
347         return devices[index].state;
348 }
349
350 void ALSAPool::set_card_state(unsigned index, ALSAPool::Device::State state)
351 {
352         {
353                 lock_guard<mutex> lock(mu);
354                 devices[index].state = state;
355         }
356
357         DeviceSpec spec{InputSourceType::ALSA_INPUT, index};
358         bool silence = (state != ALSAPool::Device::State::RUNNING);
359         while (!global_audio_mixer->silence_card(spec, silence))
360                 ;
361         global_audio_mixer->trigger_state_changed_callback();
362 }
363
364 unsigned ALSAPool::find_free_device_index(const string &name, const string &info, unsigned num_channels, const string &address)
365 {
366         // First try to find an exact match on a dead card.
367         for (unsigned i = 0; i < devices.size(); ++i) {
368                 if (devices[i].state == Device::State::DEAD &&
369                     devices[i].address == address &&
370                     devices[i].name == name &&
371                     devices[i].info == info &&
372                     devices[i].num_channels == num_channels) {
373                         devices[i].state = Device::State::READY;
374                         return i;
375                 }
376         }
377
378         // Then try to find a match on everything but the address
379         // (probably that devices were plugged back in a different order).
380         // If we have two cards that are equal, this might get them mixed up,
381         // but we don't have anything better.
382         for (unsigned i = 0; i < devices.size(); ++i) {
383                 if (devices[i].state == Device::State::DEAD &&
384                     devices[i].name == name &&
385                     devices[i].info == info &&
386                     devices[i].num_channels == num_channels) {
387                         devices[i].state = Device::State::READY;
388                         return i;
389                 }
390         }
391
392         // OK, so we didn't find a match; see if there are any empty slots.
393         for (unsigned i = 0; i < devices.size(); ++i) {
394                 if (devices[i].state == Device::State::EMPTY) {
395                         devices[i].state = Device::State::READY;
396                         devices[i].held = false;
397                         return i;
398                 }
399         }
400
401         // Failing that, we just insert the new device at the end.
402         Device new_dev;
403         new_dev.state = Device::State::READY;
404         new_dev.held = false;
405         devices.push_back(new_dev);
406         inputs.emplace_back(nullptr);
407         return devices.size() - 1;
408 }
409
410 unsigned ALSAPool::create_dead_card(const string &name, const string &info, unsigned num_channels)
411 {
412         lock_guard<mutex> lock(mu);
413
414         // See if there are any empty slots. If not, insert one at the end.
415         vector<Device>::iterator free_device =
416                 find_if(devices.begin(), devices.end(),
417                         [](const Device &device) { return device.state == Device::State::EMPTY; });
418         if (free_device == devices.end()) {
419                 devices.push_back(Device());
420                 inputs.emplace_back(nullptr);
421                 free_device = devices.end() - 1;
422         }
423
424         free_device->state = Device::State::DEAD;
425         free_device->name = name;
426         free_device->info = info;
427         free_device->num_channels = num_channels;
428         free_device->held = true;
429
430         return distance(devices.begin(), free_device);
431 }
432
433 void ALSAPool::serialize_device(unsigned index, DeviceSpecProto *serialized)
434 {
435         lock_guard<mutex> lock(mu);
436         assert(index < devices.size());
437         assert(devices[index].held);
438         serialized->set_type(DeviceSpecProto::ALSA_INPUT);
439         serialized->set_index(index);
440         serialized->set_display_name(devices[index].display_name());
441         serialized->set_alsa_name(devices[index].name);
442         serialized->set_alsa_info(devices[index].info);
443         serialized->set_num_channels(devices[index].num_channels);
444         serialized->set_address(devices[index].address);
445 }
446
447 void ALSAPool::free_card(unsigned index)
448 {
449         DeviceSpec spec{InputSourceType::ALSA_INPUT, index};
450         while (!global_audio_mixer->silence_card(spec, true))
451                 ;
452
453         {
454                 lock_guard<mutex> lock(mu);
455                 if (devices[index].held) {
456                         devices[index].state = Device::State::DEAD;
457                 } else {
458                         devices[index].state = Device::State::EMPTY;
459                         inputs[index].reset();
460                 }
461                 while (!devices.empty() && devices.back().state == Device::State::EMPTY) {
462                         devices.pop_back();
463                         inputs.pop_back();
464                 }
465         }
466
467         global_audio_mixer->trigger_state_changed_callback();
468 }