]> git.sesse.net Git - nageru/blob - nageru/alsa_input.cpp
IWYU-fix nageru/*.cpp.
[nageru] / nageru / alsa_input.cpp
1 #include "alsa_input.h"
2
3 #include <alsa/error.h>
4 #include <alsa/global.h>
5 #include <alsa/pcm.h>
6 #include <assert.h>
7 #include <chrono>
8 #include <errno.h>
9 #include <pthread.h>
10 #include <stdio.h>
11 #include <thread>
12 #include <cstdint>
13
14 #include "alsa_pool.h"
15 #include "bmusb/bmusb.h"
16
17 using namespace std;
18 using namespace std::chrono;
19 using namespace std::placeholders;
20
21 #define RETURN_ON_ERROR(msg, expr) do {                                                    \
22         int err = (expr);                                                                  \
23         if (err < 0) {                                                                     \
24                 fprintf(stderr, "[%s] " msg ": %s\n", device.c_str(), snd_strerror(err));  \
25                 if (err == -ENODEV) return CaptureEndReason::DEVICE_GONE;                  \
26                 return CaptureEndReason::OTHER_ERROR;                                      \
27         }                                                                                  \
28 } while (false)
29
30 #define RETURN_FALSE_ON_ERROR(msg, expr) do {                                              \
31         int err = (expr);                                                                  \
32         if (err < 0) {                                                                     \
33                 fprintf(stderr, "[%s] " msg ": %s\n", device.c_str(), snd_strerror(err));  \
34                 return false;                                                              \
35         }                                                                                  \
36 } while (false)
37
38 #define WARN_ON_ERROR(msg, expr) do {                                                      \
39         int err = (expr);                                                                  \
40         if (err < 0) {                                                                     \
41                 fprintf(stderr, "[%s] " msg ": %s\n", device.c_str(), snd_strerror(err));  \
42         }                                                                                  \
43 } while (false)
44
45 ALSAInput::ALSAInput(const char *device, unsigned sample_rate, unsigned num_channels, audio_callback_t audio_callback, ALSAPool *parent_pool, unsigned internal_dev_index)
46         : device(device),
47           sample_rate(sample_rate),
48           num_channels(num_channels),
49           audio_callback(audio_callback),
50           parent_pool(parent_pool),
51           internal_dev_index(internal_dev_index)
52 {
53 }
54
55 bool ALSAInput::open_device()
56 {
57         RETURN_FALSE_ON_ERROR("snd_pcm_open()", snd_pcm_open(&pcm_handle, device.c_str(), SND_PCM_STREAM_CAPTURE, 0));
58
59         // Set format.
60         snd_pcm_hw_params_t *hw_params;
61         snd_pcm_hw_params_alloca(&hw_params);
62         if (!set_base_params(device.c_str(), pcm_handle, hw_params, &sample_rate)) {
63                 return false;
64         }
65
66         RETURN_FALSE_ON_ERROR("snd_pcm_hw_params_set_channels()", snd_pcm_hw_params_set_channels(pcm_handle, hw_params, num_channels));
67
68         // Fragment size of 64 samples (about 1 ms at 48 kHz; a frame at 60
69         // fps/48 kHz is 800 samples.) We ask for 64 such periods in our buffer
70         // (~85 ms buffer); more than that, and our jitter is probably so high
71         // that the resampling queue can't keep up anyway.
72         // The entire thing with periods and such is a bit mysterious to me;
73         // seemingly I can get 96 frames at a time with no problems even if
74         // the period size is 64 frames. And if I set num_periods to e.g. 1,
75         // I can't have a big buffer.
76         num_periods = 16;
77         int dir = 0;
78         RETURN_FALSE_ON_ERROR("snd_pcm_hw_params_set_periods_near()", snd_pcm_hw_params_set_periods_near(pcm_handle, hw_params, &num_periods, &dir));
79         period_size = 64;
80         dir = 0;
81         RETURN_FALSE_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));
82         buffer_frames = 64 * 64;
83         RETURN_FALSE_ON_ERROR("snd_pcm_hw_params_set_buffer_size_near()", snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hw_params, &buffer_frames));
84         RETURN_FALSE_ON_ERROR("snd_pcm_hw_params()", snd_pcm_hw_params(pcm_handle, hw_params));
85         //snd_pcm_hw_params_free(hw_params);
86
87         // Figure out which format the card actually chose.
88         RETURN_FALSE_ON_ERROR("snd_pcm_hw_params_current()", snd_pcm_hw_params_current(pcm_handle, hw_params));
89         snd_pcm_format_t chosen_format;
90         RETURN_FALSE_ON_ERROR("snd_pcm_hw_params_get_format()", snd_pcm_hw_params_get_format(hw_params, &chosen_format));
91
92         audio_format.num_channels = num_channels;
93         audio_format.bits_per_sample = 0;
94         switch (chosen_format) {
95         case SND_PCM_FORMAT_S16_LE:
96                 audio_format.bits_per_sample = 16;
97                 break;
98         case SND_PCM_FORMAT_S24_LE:
99                 audio_format.bits_per_sample = 24;
100                 break;
101         case SND_PCM_FORMAT_S32_LE:
102                 audio_format.bits_per_sample = 32;
103                 break;
104         default:
105                 assert(false);
106         }
107         audio_format.sample_rate = sample_rate;
108         //printf("num_periods=%u period_size=%u buffer_frames=%u sample_rate=%u bits_per_sample=%d\n",
109         //      num_periods, unsigned(period_size), unsigned(buffer_frames), sample_rate, audio_format.bits_per_sample);
110
111         buffer.reset(new uint8_t[buffer_frames * num_channels * audio_format.bits_per_sample / 8]);
112
113         snd_pcm_sw_params_t *sw_params;
114         snd_pcm_sw_params_alloca(&sw_params);
115         RETURN_FALSE_ON_ERROR("snd_pcm_sw_params_current()", snd_pcm_sw_params_current(pcm_handle, sw_params));
116         RETURN_FALSE_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));
117         RETURN_FALSE_ON_ERROR("snd_pcm_sw_params_set_tstamp_mode", snd_pcm_sw_params_set_tstamp_mode(pcm_handle, sw_params, SND_PCM_TSTAMP_ENABLE));
118         RETURN_FALSE_ON_ERROR("snd_pcm_sw_params_set_tstamp_type", snd_pcm_sw_params_set_tstamp_type(pcm_handle, sw_params, SND_PCM_TSTAMP_TYPE_MONOTONIC));
119
120         RETURN_FALSE_ON_ERROR("snd_pcm_sw_params()", snd_pcm_sw_params(pcm_handle, sw_params));
121
122         RETURN_FALSE_ON_ERROR("snd_pcm_nonblock()", snd_pcm_nonblock(pcm_handle, 1));
123         RETURN_FALSE_ON_ERROR("snd_pcm_prepare()", snd_pcm_prepare(pcm_handle));
124         return true;
125 }
126
127 bool ALSAInput::set_base_params(const char *device_name, snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hw_params, unsigned *sample_rate)
128 {
129         int err;
130         err = snd_pcm_hw_params_any(pcm_handle, hw_params);
131         if (err < 0) {
132                 fprintf(stderr, "[%s] snd_pcm_hw_params_any(): %s\n", device_name, snd_strerror(err));
133                 return false;
134         }
135         err = snd_pcm_hw_params_set_access(pcm_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
136         if (err < 0) {
137                 fprintf(stderr, "[%s] snd_pcm_hw_params_set_access(): %s\n", device_name, snd_strerror(err));
138                 return false;
139         }
140         snd_pcm_format_mask_t *format_mask;
141         snd_pcm_format_mask_alloca(&format_mask);
142         snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S16_LE);
143         snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S24_LE);
144         snd_pcm_format_mask_set(format_mask, SND_PCM_FORMAT_S32_LE);
145         err = snd_pcm_hw_params_set_format_mask(pcm_handle, hw_params, format_mask);
146         if (err < 0) {
147                 fprintf(stderr, "[%s] snd_pcm_hw_params_set_format_mask(): %s\n", device_name, snd_strerror(err));
148                 return false;
149         }
150         err = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params, sample_rate, 0);
151         if (err < 0) {
152                 fprintf(stderr, "[%s] snd_pcm_hw_params_set_rate_near(): %s\n", device_name, snd_strerror(err));
153                 return false;
154         }
155         return true;
156 }
157
158 ALSAInput::~ALSAInput()
159 {
160         if (pcm_handle) {
161                 WARN_ON_ERROR("snd_pcm_close()", snd_pcm_close(pcm_handle));
162         }
163 }
164
165 void ALSAInput::start_capture_thread()
166 {
167         assert(!device.empty());
168         should_quit.unquit();
169         capture_thread = thread(&ALSAInput::capture_thread_func, this);
170 }
171
172 void ALSAInput::stop_capture_thread()
173 {
174         should_quit.quit();
175         capture_thread.join();
176 }
177
178 void ALSAInput::capture_thread_func()
179 {
180         if (!done_init) {
181                 char thread_name[16];
182                 snprintf(thread_name, sizeof(thread_name), "ALSA_C_%d", internal_dev_index);
183                 pthread_setname_np(pthread_self(), thread_name);
184
185                 done_init = true;
186         }
187
188         parent_pool->set_card_state(internal_dev_index, ALSAPool::Device::State::STARTING);
189
190         // If the device hasn't been opened already, we need to do so
191         // before we can capture.
192         while (!should_quit.should_quit() && pcm_handle == nullptr) {
193                 if (!open_device()) {
194                         fprintf(stderr, "[%s] Waiting one second and trying again...\n",
195                                 device.c_str());
196                         should_quit.sleep_for(seconds(1));
197                 }
198         }
199
200         if (should_quit.should_quit()) {
201                 // Don't call free_card(); that would be a deadlock.
202                 if (pcm_handle) {
203                         WARN_ON_ERROR("snd_pcm_close()", snd_pcm_close(pcm_handle));
204                 }
205                 pcm_handle = nullptr;
206                 return;
207         }
208
209         // Do the actual capture. (Termination condition within loop.)
210         for ( ;; ) {
211                 switch (do_capture()) {
212                 case CaptureEndReason::REQUESTED_QUIT:
213                         // Don't call free_card(); that would be a deadlock.
214                         WARN_ON_ERROR("snd_pcm_close()", snd_pcm_close(pcm_handle));
215                         pcm_handle = nullptr;
216                         return;
217                 case CaptureEndReason::DEVICE_GONE:
218                         parent_pool->free_card(internal_dev_index);
219                         WARN_ON_ERROR("snd_pcm_close()", snd_pcm_close(pcm_handle));
220                         pcm_handle = nullptr;
221                         return;
222                 case CaptureEndReason::OTHER_ERROR:
223                         parent_pool->set_card_state(internal_dev_index, ALSAPool::Device::State::STARTING);
224                         fprintf(stderr, "[%s] Sleeping one second and restarting capture...\n",
225                                 device.c_str());
226                         should_quit.sleep_for(seconds(1));
227                         break;
228                 }
229         }
230 }
231
232 ALSAInput::CaptureEndReason ALSAInput::do_capture()
233 {
234         parent_pool->set_card_state(internal_dev_index, ALSAPool::Device::State::STARTING);
235         RETURN_ON_ERROR("snd_pcm_start()", snd_pcm_start(pcm_handle));
236         parent_pool->set_card_state(internal_dev_index, ALSAPool::Device::State::RUNNING);
237
238         snd_pcm_status_t *status;
239         snd_pcm_status_alloca(&status);
240         while (!should_quit.should_quit()) {
241                 int ret = snd_pcm_wait(pcm_handle, /*timeout=*/100);
242                 if (ret == 0) continue;  // Timeout.
243                 if (ret == -EPIPE) {
244                         fprintf(stderr, "[%s] ALSA overrun\n", device.c_str());
245                         snd_pcm_prepare(pcm_handle);
246                         snd_pcm_start(pcm_handle);
247                         continue;
248                 }
249                 RETURN_ON_ERROR("snd_pcm_wait()", ret);
250
251                 ret = snd_pcm_status(pcm_handle, status);
252                 RETURN_ON_ERROR("snd_pcm_status()", ret);
253
254                 snd_pcm_sframes_t avail = snd_pcm_status_get_avail(status);
255                 snd_htimestamp_t alsa_ts;
256                 snd_pcm_status_get_htstamp(status, &alsa_ts);
257
258                 snd_pcm_sframes_t frames = snd_pcm_readi(pcm_handle, buffer.get(), avail);
259                 if (frames == -EPIPE) {
260                         fprintf(stderr, "[%s] ALSA overrun\n", device.c_str());
261                         snd_pcm_prepare(pcm_handle);
262                         snd_pcm_start(pcm_handle);
263                         continue;
264                 }
265                 if (frames == 0) {
266                         fprintf(stderr, "snd_pcm_readi() returned 0\n");
267                         break;
268                 }
269                 RETURN_ON_ERROR("snd_pcm_readi()", frames);
270
271                 // NOTE: This assumes steady_clock::time_point is the same as clock_gettime(CLOCK_MONOTONIC).
272                 const steady_clock::time_point ts = steady_clock::time_point(seconds(alsa_ts.tv_sec) + nanoseconds(alsa_ts.tv_nsec));
273                 bool success;
274                 do {
275                         if (should_quit.should_quit()) return CaptureEndReason::REQUESTED_QUIT;
276                         success = audio_callback(buffer.get(), frames, audio_format, ts);
277                 } while (!success);
278         }
279         return CaptureEndReason::REQUESTED_QUIT;
280 }