]> git.sesse.net Git - nageru/blob - alsa_input.cpp
Refactor ALSA code in preparation for hotplug.
[nageru] / alsa_input.cpp
1 #include "alsa_input.h"
2 #include "audio_mixer.h"
3 #include "defs.h"
4
5 #include <sys/inotify.h>
6
7 #include <functional>
8 #include <unordered_map>
9
10 using namespace std;
11 using namespace std::placeholders;
12
13 namespace {
14
15 bool set_base_params(const char *device_name, snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hw_params, unsigned *sample_rate)
16 {
17         int err;
18         err = snd_pcm_hw_params_any(pcm_handle, hw_params);
19         if (err < 0) {
20                 fprintf(stderr, "[%s] snd_pcm_hw_params_any(): %s\n", device_name, snd_strerror(err));
21                 return false;
22         }
23         err = snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
24         if (err < 0) {
25                 fprintf(stderr, "[%s] snd_pcm_hw_params_set_access(): %s\n", device_name, snd_strerror(err));
26                 return false;
27         }
28         snd_pcm_format_mask_t *format_mask;
29         snd_pcm_format_mask_alloca(&format_mask);
30         snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S16_LE);
31         snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S24_LE);
32         snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S32_LE);
33         err = snd_pcm_hw_params_set_format_mask(pcm_handle, hw_params, format_mask);
34         if (err < 0) {
35                 fprintf(stderr, "[%s] snd_pcm_hw_params_set_format_mask(): %s\n", device_name, snd_strerror(err));
36                 return false;
37         }
38         err = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, sample_rate, 0);
39         if (err < 0) {
40                 fprintf(stderr, "[%s] snd_pcm_hw_params_set_rate_near(): %s\n", device_name, snd_strerror(err));
41                 return false;
42         }
43         return true;
44 }
45
46 }  // namespace
47
48 ALSAInput::ALSAInput(const char *device, unsigned sample_rate, unsigned num_channels, audio_callback_t audio_callback)
49         : device(device), sample_rate(sample_rate), num_channels(num_channels), audio_callback(audio_callback)
50 {
51         die_on_error(device, snd_pcm_open(&pcm_handle, device, SND_PCM_STREAM_CAPTURE, 0));
52
53         // Set format.
54         snd_pcm_hw_params_t *hw_params;
55         snd_pcm_hw_params_alloca(&hw_params);
56         if (!set_base_params(device, pcm_handle, hw_params, &sample_rate)) {
57                 exit(1);
58         }
59
60         die_on_error("snd_pcm_hw_params_set_channels()", snd_pcm_hw_params_set_channels(pcm_handle, hw_params, num_channels));
61
62         // Fragment size of 64 samples (about 1 ms at 48 kHz; a frame at 60
63         // fps/48 kHz is 800 samples.) We ask for 64 such periods in our buffer
64         // (~85 ms buffer); more than that, and our jitter is probably so high
65         // that the resampling queue can't keep up anyway.
66         // The entire thing with periods and such is a bit mysterious to me;
67         // seemingly I can get 96 frames at a time with no problems even if
68         // the period size is 64 frames. And if I set num_periods to e.g. 1,
69         // I can't have a big buffer.
70         num_periods = 16;
71         int dir = 0;
72         die_on_error("snd_pcm_hw_params_set_periods_near()", snd_pcm_hw_params_set_periods_near(pcm_handle, hw_params, &num_periods, &dir));
73         period_size = 64;
74         dir = 0;
75         die_on_error("snd_pcm_hw_params_set_period_size_near()", snd_pcm_hw_params_set_period_size_near(pcm_handle, hw_params, &period_size, &dir));
76         buffer_frames = 64 * 64;
77         die_on_error("snd_pcm_hw_params_set_buffer_size_near()", snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hw_params, &buffer_frames));
78         die_on_error("snd_pcm_hw_params()", snd_pcm_hw_params(pcm_handle, hw_params));
79         //snd_pcm_hw_params_free(hw_params);
80
81         // Figure out which format the card actually chose.
82         die_on_error("snd_pcm_hw_params_current()", snd_pcm_hw_params_current(pcm_handle, hw_params));
83         snd_pcm_format_t chosen_format;
84         die_on_error("snd_pcm_hw_params_get_format()", snd_pcm_hw_params_get_format(hw_params, &chosen_format));
85
86         audio_format.num_channels = num_channels;
87         audio_format.bits_per_sample = 0;
88         switch (chosen_format) {
89         case SND_PCM_FORMAT_S16_LE:
90                 audio_format.bits_per_sample = 16;
91                 break;
92         case SND_PCM_FORMAT_S24_LE:
93                 audio_format.bits_per_sample = 24;
94                 break;
95         case SND_PCM_FORMAT_S32_LE:
96                 audio_format.bits_per_sample = 32;
97                 break;
98         default:
99                 assert(false);
100         }
101         //printf("num_periods=%u period_size=%u buffer_frames=%u sample_rate=%u bits_per_sample=%d\n",
102         //      num_periods, unsigned(period_size), unsigned(buffer_frames), sample_rate, audio_format.bits_per_sample);
103
104         buffer.reset(new uint8_t[buffer_frames * num_channels * audio_format.bits_per_sample / 8]);
105
106         snd_pcm_sw_params_t *sw_params;
107         snd_pcm_sw_params_alloca(&sw_params);
108         die_on_error("snd_pcm_sw_params_current()", snd_pcm_sw_params_current(pcm_handle, sw_params));
109         die_on_error("snd_pcm_sw_params_set_start_threshold", snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params, num_periods * period_size / 2));
110         die_on_error("snd_pcm_sw_params()", snd_pcm_sw_params(pcm_handle, sw_params));
111
112         die_on_error("snd_pcm_nonblock()", snd_pcm_nonblock(pcm_handle, 1));
113         die_on_error("snd_pcm_prepare()", snd_pcm_prepare(pcm_handle));
114 }
115
116 ALSAInput::~ALSAInput()
117 {
118         die_on_error("snd_pcm_close()", snd_pcm_close(pcm_handle));
119 }
120
121 void ALSAInput::start_capture_thread()
122 {
123         should_quit = false;
124         capture_thread = thread(&ALSAInput::capture_thread_func, this);
125 }
126
127 void ALSAInput::stop_capture_thread()
128 {
129         should_quit = true;
130         capture_thread.join();
131 }
132
133 void ALSAInput::capture_thread_func()
134 {
135         die_on_error("snd_pcm_start()", snd_pcm_start(pcm_handle));
136         uint64_t num_frames_output = 0;
137         while (!should_quit) {
138                 int ret = snd_pcm_wait(pcm_handle, /*timeout=*/100);
139                 if (ret == 0) continue;  // Timeout.
140                 if (ret == -EPIPE) {
141                         fprintf(stderr, "[%s] ALSA overrun\n", device.c_str());
142                         snd_pcm_prepare(pcm_handle);
143                         snd_pcm_start(pcm_handle);
144                         continue;
145                 }
146                 die_on_error("snd_pcm_wait()", ret);
147
148                 snd_pcm_sframes_t frames = snd_pcm_readi(pcm_handle, buffer.get(), buffer_frames);
149                 if (frames == -EPIPE) {
150                         fprintf(stderr, "[%s] ALSA overrun\n", device.c_str());
151                         snd_pcm_prepare(pcm_handle);
152                         snd_pcm_start(pcm_handle);
153                         continue;
154                 }
155                 if (frames == 0) {
156                         fprintf(stderr, "snd_pcm_readi() returned 0\n");
157                         break;
158                 }
159                 die_on_error("snd_pcm_readi()", frames);
160
161                 const int64_t prev_pts = frames_to_pts(num_frames_output);
162                 const int64_t pts = frames_to_pts(num_frames_output + frames);
163                 bool success;
164                 do {
165                         if (should_quit) return;
166                         success = audio_callback(buffer.get(), frames, audio_format, pts - prev_pts);
167                 } while (!success);
168                 num_frames_output += frames;
169         }
170 }
171
172 int64_t ALSAInput::frames_to_pts(uint64_t n) const
173 {
174         return (n * TIMEBASE) / sample_rate;
175 }
176
177 void ALSAInput::die_on_error(const char *func_name, int err)
178 {
179         if (err < 0) {
180                 fprintf(stderr, "[%s] %s: %s\n", device.c_str(), func_name, snd_strerror(err));
181                 exit(1);
182         }
183 }
184
185 ALSAPool::~ALSAPool()
186 {
187         for (Device &device : devices) {
188                 if (device.input != nullptr) {
189                         device.input->stop_capture_thread();
190                         delete device.input;
191                 }
192         }
193 }
194
195 std::vector<ALSAPool::Device> ALSAPool::get_devices()
196 {
197         lock_guard<mutex> lock(mu);
198         for (Device &device : devices) {
199                 device.held = true;
200         }
201         return devices;
202 }
203
204 void ALSAPool::hold_device(unsigned index)
205 {
206         lock_guard<mutex> lock(mu);
207         assert(index < devices.size());
208         devices[index].held = true;
209 }
210
211 void ALSAPool::release_device(unsigned index)
212 {
213         lock_guard<mutex> lock(mu);
214         if (index < devices.size()) {
215                 devices[index].held = false;
216         }
217 }
218
219 void ALSAPool::enumerate_devices()
220 {
221         // Enumerate all cards.
222         for (int card_index = -1; snd_card_next(&card_index) == 0 && card_index >= 0; ) {
223                 char address[256];
224                 snprintf(address, sizeof(address), "hw:%d", card_index);
225
226                 snd_ctl_t *ctl;
227                 int err = snd_ctl_open(&ctl, address, 0);
228                 if (err < 0) {
229                         printf("%s: %s\n", address, snd_strerror(err));
230                         continue;
231                 }
232                 unique_ptr<snd_ctl_t, decltype(snd_ctl_close)*> ctl_closer(ctl, snd_ctl_close);
233
234                 // Enumerate all devices on this card.
235                 for (int dev_index = -1; snd_ctl_pcm_next_device(ctl, &dev_index) == 0 && dev_index >= 0; ) {
236                         add_device(card_index, dev_index);
237                 }
238         }
239 }
240
241 bool ALSAPool::add_device(unsigned card_index, unsigned dev_index)
242 {
243         char address[256];
244         snprintf(address, sizeof(address), "hw:%d", card_index);
245         snd_ctl_t *ctl;
246         int err = snd_ctl_open(&ctl, address, 0);
247         if (err < 0) {
248                 printf("%s: %s\n", address, snd_strerror(err));
249                 return false;
250         }
251         unique_ptr<snd_ctl_t, decltype(snd_ctl_close)*> ctl_closer(ctl, snd_ctl_close);
252
253         snd_pcm_info_t *pcm_info;
254         snd_pcm_info_alloca(&pcm_info);
255         snd_pcm_info_set_device(pcm_info, dev_index);
256         snd_pcm_info_set_subdevice(pcm_info, 0);
257         snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_CAPTURE);
258         if (snd_ctl_pcm_info(ctl, pcm_info) < 0) {
259                 // Not available for capture.
260                 return false;
261         }
262
263         snprintf(address, sizeof(address), "hw:%d,%d", card_index, dev_index);
264
265         unsigned num_channels = 0;
266
267         // Find all channel maps for this device, and pick out the one
268         // with the most channels.
269         snd_pcm_chmap_query_t **cmaps = snd_pcm_query_chmaps_from_hw(card_index, dev_index, 0, SND_PCM_STREAM_CAPTURE);
270         if (cmaps != nullptr) {
271                 for (snd_pcm_chmap_query_t **ptr = cmaps; *ptr; ++ptr) {
272                         num_channels = max(num_channels, (*ptr)->map.channels);
273                 }
274                 snd_pcm_free_chmaps(cmaps);
275         }
276         if (num_channels == 0) {
277                 // Device had no channel maps. We need to open it to query.
278                 // TODO: Do this asynchronously.
279                 snd_pcm_t *pcm_handle;
280                 int err = snd_pcm_open(&pcm_handle, address, SND_PCM_STREAM_CAPTURE, 0);
281                 if (err < 0) {
282                         // TODO: When we go to hotplug support, we should support some
283                         // retry here, as the device could legitimately be busy.
284                         printf("%s: %s\n", address, snd_strerror(err));
285                         return false;
286                 }
287                 snd_pcm_hw_params_t *hw_params;
288                 snd_pcm_hw_params_alloca(&hw_params);
289                 unsigned sample_rate;
290                 if (!set_base_params(address, pcm_handle, hw_params, &sample_rate)) {
291                         snd_pcm_close(pcm_handle);
292                         return false;
293                 }
294                 err = snd_pcm_hw_params_get_channels_max(hw_params, &num_channels);
295                 if (err < 0) {
296                         fprintf(stderr, "[%s] snd_pcm_hw_params_get_channels_max(): %s\n",
297                                 address, snd_strerror(err));
298                         snd_pcm_close(pcm_handle);
299                         return false;
300                 }
301                 snd_pcm_close(pcm_handle);
302         }
303
304         if (num_channels == 0) {
305                 printf("%s: No channel maps with channels\n", address);
306                 return true;
307         }
308
309         snd_ctl_card_info_t *card_info;
310         snd_ctl_card_info_alloca(&card_info);
311         snd_ctl_card_info(ctl, card_info);
312
313         Device dev;
314         dev.address = address;
315         dev.name = snd_ctl_card_info_get_name(card_info);
316         dev.info = snd_pcm_info_get_name(pcm_info);
317         dev.num_channels = num_channels;
318         dev.state = Device::State::RUNNING;
319
320         lock_guard<mutex> lock(mu);
321         devices.push_back(std::move(dev));
322         return true;
323 }
324
325 void ALSAPool::init()
326 {
327         enumerate_devices();
328 }
329
330 void ALSAPool::reset_device(unsigned index)
331 {
332         lock_guard<mutex> lock(mu);
333         Device *device = &devices[index];
334         if (inputs[index] != nullptr) {
335                 inputs[index]->stop_capture_thread();
336         }
337         if (!device->held) {
338                 inputs[index].reset();
339         } else {
340                 // TODO: Put on a background thread instead of locking?
341                 inputs[index].reset(new ALSAInput(device->address.c_str(), OUTPUT_FREQUENCY, device->num_channels, bind(&AudioMixer::add_audio, global_audio_mixer, DeviceSpec{InputSourceType::ALSA_INPUT, index}, _1, _2, _3, _4)));
342                 inputs[index]->start_capture_thread();
343         }
344         device->input = inputs[index].get();
345 }
346
347 unsigned ALSAPool::get_capture_frequency(unsigned index)
348 {
349         lock_guard<mutex> lock(mu);
350         assert(devices[index].held);
351         if (devices[index].input)
352                 return devices[index].input->get_sample_rate();
353         else
354                 return OUTPUT_FREQUENCY;
355 }