UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
alsa.c
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2011-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
47/* Use the newer ALSA API */
48#define ALSA_PCM_NEW_HW_PARAMS_API
49
50#include "config_unix.h"
51
52#include "host.h"
53
54#include "alsa_common.h"
55#include "audio/audio_capture.h"
56#include "audio/types.h"
57#include "audio/utils.h"
58
59#include "debug.h"
60#include "lib_common.h"
61#include "tv.h"
62#include <stdlib.h>
63#include <string.h>
64
65#include <alsa/asoundlib.h>
66
67#define MOD_NAME "[ALSA cap.] "
68
70 snd_pcm_t *handle;
72 char *tmp_data;
73
74 snd_pcm_uframes_t frames;
75 unsigned int min_device_channels;
76
77 struct timeval start_time;
78 long long int captured_samples;
79
81};
82
83static void audio_cap_alsa_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
84{
85 *deleter = free;
86 const char *whitelist[] = {"pulse", "dsnoop"};
87 audio_alsa_probe(available_devices, count, whitelist, sizeof(whitelist) / sizeof(*whitelist));
88 strcpy((*available_devices)[0].dev, "");
89 strcpy((*available_devices)[0].name, "Default Linux audio input");
90}
91
92static void audio_cap_alsa_help(void)
93{
94 printf("Usage\n");
96 color_printf(TERM_BOLD "\t-s alsa:<device>\n" TERM_RESET);
97 color_printf(TERM_BOLD "\t-s alsa:<device>:opts=<opts>\n" TERM_RESET);
98 color_printf(TERM_BOLD "\t-s alsa:opts=<opts>\n\n" TERM_RESET);
99 color_printf(TERM_BOLD "\t<opts>" TERM_RESET " can be in format key1=value1:key2=value2, options are:\n");
100 color_printf(TERM_BOLD "\t\tframes=<frames>" TERM_RESET " number of audio frames captured at a moment\n");
101
102 printf("\nAvailable ALSA capture devices\n");
104}
105
106static const int bps_preference[] = { 2, 4, 3, 1 };
107
108#define DEFAULT_SAMPLE_RATE 48000
109
110static void * audio_cap_alsa_init(struct module *parent, const char *cfg)
111{
112 UNUSED(parent);
113 if (strcmp(cfg, "help") == 0) {
115 return INIT_NOERR;
116 }
117 int rc;
118 snd_pcm_hw_params_t *params;
119 unsigned int val;
120 int dir;
121 const char *name = "default";
122 char *opts = NULL;
123 int format;
124 char *tmp = NULL;
125
126 struct state_alsa_capture *s = calloc(1, sizeof(struct state_alsa_capture));
127
128 if (strlen(cfg) > 0) {
129 tmp = strdup(cfg);
130 if (strncmp(tmp, "opts=", strlen("opts")) == 0) {
131 opts = tmp + strlen("opts=");
132 } else {
133 name = tmp;
134 if (strstr(tmp, ":opts=") != NULL) {
135 opts = strstr(tmp, ":opts=") + strlen(":opts=");
136 *strstr(tmp, ":opts=") = '\0';
137 }
138 }
139 }
140
141 gettimeofday(&s->start_time, NULL);
145 s->tmp_data = NULL;
146
147 /* Set period size to 128 frames or more. */
148 s->frames = 128;
149
150 if (opts) {
151 char *item, *save_ptr;
152 while ((item = strtok_r(opts, ":", &save_ptr)) != NULL) {
153 if (strncmp(item, "frames=", strlen("frames=")) == 0) {
154 s->frames = atoi(item + strlen("frames="));
155 } else {
156 fprintf(stderr, "[ALSA cap.] Unknown option: %s\n", item);
157 goto error;
158 }
159 opts = NULL;
160 }
161 }
162
163 /* Open PCM device for recording (capture). */
164 rc = snd_pcm_open(&s->handle, name,
165 SND_PCM_STREAM_CAPTURE, 0);
166 if (rc < 0) {
167 fprintf(stderr, MOD_NAME "unable to open pcm device: %s\n",
168 snd_strerror(rc));
169 goto error;
170 }
172
173 /* Allocate a hardware parameters object. */
174 snd_pcm_hw_params_alloca(&params);
175
176 /* Fill it in with default values. */
177 rc = snd_pcm_hw_params_any(s->handle, params);
178 if (rc < 0) {
179 fprintf(stderr, MOD_NAME "unable to set default parameters: %s\n",
180 snd_strerror(rc));
181 goto error;
182 }
183
184 /* Find appropriate parameters if not given by user */
185 if (s->frame.bps < 0 || s->frame.bps > 4) {
186 log_msg(LOG_LEVEL_ERROR, "[ALSA] %d bits per second are not supported by UG.\n",
187 s->frame.bps * 8);
188 goto error;
189 }
190
191 if (s->frame.bps == 0) {
192 for (unsigned int i = 0; i < sizeof bps_preference / sizeof(bps_preference[0]); i++) {
193 if (!snd_pcm_hw_params_test_format(s->handle, params, bps_to_snd_fmts[bps_preference[i]])) {
194 s->frame.bps = bps_preference[i];
195 break;
196 }
197 }
198 if (s->frame.bps == 0) {
200 "Cannot find suitable sample format. ");
201 goto error;
202 }
203 }
204
205 if (s->frame.sample_rate == 0) {
207 if (s->frame.sample_rate == 0) {
208 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Cannot find usable sample rate!\n");
209 goto error;
210 }
211 }
212
213 if (!snd_pcm_hw_params_test_access(s->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) {
214 s->non_interleaved = false;
215 } else if (!snd_pcm_hw_params_test_access(s->handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
216 if (s->frame.ch_count > 1) {
217 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Non-interleaved mode "
218 "available only when capturing mono!\n");
219 goto error;
220 } else {
221 s->non_interleaved = true;
222 }
223 } else {
224 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unsupported access mode!\n");
225 goto error;
226
227 }
228
229 /* Set the desired hardware parameters. */
230
231 /* Access mode */
232 rc = snd_pcm_hw_params_set_access(s->handle, params,
233 s->non_interleaved ? SND_PCM_ACCESS_RW_NONINTERLEAVED : SND_PCM_ACCESS_RW_INTERLEAVED);
234 if (rc < 0) {
235 fprintf(stderr, MOD_NAME "unable to set interleaved mode: %s\n",
236 snd_strerror(rc));
237 goto error;
238 }
239
240 /* Sample format */
241 format = bps_to_snd_fmts[s->frame.bps];
242 rc = snd_pcm_hw_params_set_format(s->handle, params,
243 format);
244 if (rc < 0) {
245 fprintf(stderr, MOD_NAME "unable to set capture format: %s\n",
246 snd_strerror(rc));
247 goto error;
248 }
249
250 /* we want to resample if device doesn't support default sample rate */
251 val = 1;
252 rc = snd_pcm_hw_params_set_rate_resample(s->handle,
253 params, val);
254 if(rc < 0) {
255 fprintf(stderr, MOD_NAME "Warning: Unable to set resampling: %s\n",
256 snd_strerror(rc));
257 }
258
259 /* set sampling rate */
260 val = s->frame.sample_rate;
261 dir = 0;
262 rc = snd_pcm_hw_params_set_rate_near(s->handle, params,
263 &val, &dir);
264 if (rc < 0) {
265 fprintf(stderr, "[ALSA cap.] unable to set sampling rate (%s %d): %s\n",
266 dir == 0 ? "=" : (dir == -1 ? "<" : ">"),
267 val, snd_strerror(rc));
268 goto error;
269 }
270
271 /* Channels count */
272 rc = snd_pcm_hw_params_set_channels(s->handle, params, s->frame.ch_count);
273 if (rc < 0) {
274 if (s->frame.ch_count == 1) { // some devices cannot do mono
275 snd_pcm_hw_params_set_channels_first(s->handle, params, &s->min_device_channels);
276 } else {
277 fprintf(stderr, MOD_NAME "unable to set channel count: %s\n",
278 snd_strerror(rc));
279 goto error;
280 }
281 }
282
283 /* This must be set after setting of sample rate for Chat 150 which increases
284 * value to 1024. But if this setting precedes, setting sample rate of 48000
285 * fails (1024 period) or does not work properly (128).
286 * */
287 dir = 0;
288 rc = snd_pcm_hw_params_set_period_size_near(s->handle,
289 params, &s->frames, &dir);
290 if (rc < 0) {
291 fprintf(stderr, "[ALSA cap.] unable to set frame period (%ld): %s\n",
292 s->frames, snd_strerror(rc));
293 }
294
295 /* Write the parameters to the driver */
296 rc = snd_pcm_hw_params(s->handle, params);
297 if (rc < 0) {
298 fprintf(stderr, MOD_NAME "unable to set hw parameters: %s\n",
299 snd_strerror(rc));
300 goto error;
301 }
302
303 /* Use a buffer large enough to hold one period */
304 snd_pcm_hw_params_get_period_size(params, &s->frames, &dir);
305 s->frame.max_size = s->frames * s->frame.ch_count * s->frame.bps;
306 s->frame.data = (char *) malloc(s->frame.max_size);
307
308 s->tmp_data = malloc(s->frames * s->min_device_channels * s->frame.bps);
309
310 log_msg(LOG_LEVEL_NOTICE, "ALSA capture configuration: %d channel%s, %d Bps, %d Hz, "
311 "%ld samples per frame.\n", s->frame.ch_count,
312 s->frame.ch_count == 1 ? "" : "s", s->frame.bps,
313 s->frame.sample_rate, s->frames);
314
315 free(tmp);
316 return s;
317
318error:
319 free(s);
320 free(tmp);
321 return NULL;
322}
323
325{
326 struct state_alsa_capture *s = (struct state_alsa_capture *) state;
327 int rc;
328 char *discard_data;
329
330 char *read_ptr[s->min_device_channels];
331 read_ptr[0] = s->frame.data;
332 if((int) s->min_device_channels > s->frame.ch_count && s->frame.ch_count == 1) {
333 read_ptr[0] = s->tmp_data;
334 }
335
336 if (s->non_interleaved) {
337 assert(s->frame.ch_count == 1);
338 discard_data = (char *) alloca(s->frames * s->frame.bps * (s->min_device_channels-1));
339 for (unsigned int i = 1; i < s->min_device_channels; ++i) {
340 read_ptr[i] = discard_data + (i - 1) * s->frames * s->frame.bps;
341 }
342 rc = snd_pcm_readn(s->handle, (void **) read_ptr, s->frames);
343 } else {
344 rc = snd_pcm_readi(s->handle, read_ptr[0], s->frames);
345 }
346 if (rc == -EPIPE) {
347 /* EPIPE means overrun */
348 log_msg(LOG_LEVEL_WARNING, MOD_NAME "overrun occurred\n");
349 snd_pcm_prepare(s->handle);
350 } else if (rc < 0) {
351 log_msg(LOG_LEVEL_WARNING, MOD_NAME "error from read: %s\n", snd_strerror(rc));
352 if (rc == -ENODEV) { // someone pulled the device (eg. videoconferencing
353 // microphone) out - taking that as a fatal error
354 log_msg(LOG_LEVEL_FATAL, MOD_NAME "Device removed, exiting!\n");
356 }
357 } else if (rc != (int)s->frames) {
358 log_msg(LOG_LEVEL_WARNING, MOD_NAME "short read, read %d frames\n", rc);
359 }
360
361 if(rc > 0) {
362 if ((int) s->min_device_channels > s->frame.ch_count && s->frame.ch_count == 1) {
363 demux_channel(s->frame.data, (char *) s->tmp_data, s->frame.bps,
364 rc * s->frame.bps * s->min_device_channels,
365 s->min_device_channels, /* channels (originally) */
366 0 /* we want first channel */
367 );
368 }
369 s->frame.data_len = rc * s->frame.bps * s->frame.ch_count;
370 if (s->frame.bps == 1) {
371 // should be unsigned2signed but it works in both directions
373 }
374 s->captured_samples += rc;
375 return &s->frame;
376 } else {
377 return NULL;
378 }
379}
380
381static void audio_cap_alsa_done(void *state)
382{
383 struct state_alsa_capture *s = (struct state_alsa_capture *) state;
384 struct timeval t;
385
386 gettimeofday(&t, NULL);
387 printf("[ALSA cap.] Captured %lld samples in %f seconds (%f samples per second).\n",
390 snd_pcm_drop(s->handle);
391 snd_pcm_close(s->handle);
392 free(s->frame.data);
393 free(s->tmp_data);
394 free(s);
395}
396
403
405
#define UNUSED(x)
void ** state
int void * val
int void(** deleter)(void *))
static const snd_pcm_format_t bps_to_snd_fmts[]
static int get_rate_near(snd_pcm_t *handle, snd_pcm_hw_params_t *params, unsigned int approx_val)
static void audio_alsa_list_devices(void)
static void audio_alsa_probe(struct device_info **available_devices, int *count, const char **whitelist, size_t whitelist_size)
Definition alsa_common.h:50
static void print_alsa_device_info(snd_pcm_t *handle, const char *module_name)
This file contains definition of some common audio types.
#define AUDIO_CAPTURE_ABI_VERSION
static void * audio_cap_alsa_init(struct module *parent, const char *cfg)
Definition alsa.c:110
static const struct audio_capture_info acap_alsa_info
Definition alsa.c:397
static struct audio_frame * audio_cap_alsa_read(void *state)
Definition alsa.c:324
static void audio_cap_alsa_done(void *state)
Definition alsa.c:381
#define MOD_NAME
Definition alsa.c:67
static void audio_cap_alsa_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition alsa.c:83
#define DEFAULT_SAMPLE_RATE
Definition alsa.c:108
static const int bps_preference[]
Definition alsa.c:106
static void audio_cap_alsa_help(void)
Definition alsa.c:92
int color_printf(const char *format,...)
#define TERM_BOLD
Definition color_out.h:48
#define TERM_FG_RED
Definition color_out.h:50
#define TERM_RESET
Definition color_out.h:47
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
void bug_msg(int level, const char *format,...)
prints the error with the advice to report the bug
Definition debug.cpp:379
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_FATAL
errors that prevent UG run
Definition debug.h:58
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
int count
Definition hd-rum.c:44
void exit_uv(int status)
Definition host.cpp:625
unsigned int audio_capture_bps
Definition host.cpp:164
unsigned int audio_capture_sample_rate
Definition host.cpp:165
static struct @19 params[100]
unsigned int audio_capture_channels
Definition host.cpp:163
#define EXIT_FAIL_AUDIO
Definition host.h:70
#define DEFAULT_AUDIO_CAPTURE_CHANNELS
Definition host.h:89
#define INIT_NOERR
Definition host.h:73
@ LIBRARY_CLASS_AUDIO_CAPTURE
Definition lib_common.h:76
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
int sample_rate
Definition types.h:101
char * data
Definition types.h:102
int ch_count
Definition types.h:104
int data_len
Definition types.h:103
int bps
Definition types.h:100
int max_size
Definition types.h:105
Definition hd-rum.c:27
bool non_interleaved
Definition alsa.c:80
unsigned int min_device_channels
Definition alsa.c:75
long long int captured_samples
Definition alsa.c:78
struct timeval start_time
Definition alsa.c:77
char * tmp_data
Definition alsa.c:72
snd_pcm_t * handle
Definition alsa.c:70
struct audio_frame frame
Definition alsa.c:71
snd_pcm_uframes_t frames
Definition alsa.c:74
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97
void signed2unsigned(char *out, const char *in, int in_len)
Definition utils.cpp:515
void demux_channel(char *out, char *in, int bps, int in_len, int in_stream_channels, int pos_in_stream)
Definition utils.cpp:334