UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
alsa.c File Reference
#include "config_unix.h"
#include "host.h"
#include "alsa_common.h"
#include "audio/audio_capture.h"
#include "audio/types.h"
#include "audio/utils.h"
#include "debug.h"
#include "lib_common.h"
#include "tv.h"
#include <stdlib.h>
#include <string.h>
#include <alsa/asoundlib.h>
Include dependency graph for alsa.c:

Go to the source code of this file.

Classes

struct  state_alsa_capture
 

Macros

#define ALSA_PCM_NEW_HW_PARAMS_API
 
#define DEFAULT_SAMPLE_RATE   48000
 
#define MOD_NAME   "[ALSA cap.] "
 

Functions

static void audio_cap_alsa_done (void *state)
 
static void audio_cap_alsa_help (void)
 
static void * audio_cap_alsa_init (struct module *parent, const char *cfg)
 
static void audio_cap_alsa_probe (struct device_info **available_devices, int *count, void(**deleter)(void *))
 
static struct audio_frame * audio_cap_alsa_read (void *state)
 
 REGISTER_MODULE (alsa, &acap_alsa_info, LIBRARY_CLASS_AUDIO_CAPTURE, AUDIO_CAPTURE_ABI_VERSION)
 

Variables

static const struct audio_capture_info acap_alsa_info
 
static const int bps_preference [] = { 2, 4, 3, 1 }
 

Detailed Description

Author
Martin Pulec pulec.nosp@m.@ces.nosp@m.net.c.nosp@m.z
Note
In code below, state_alsa_capture::frame::ch_count can differ from state_alsa_capture::min_device_channels, if device doesn't support mono and we want to capture it. In this case state_alsa_capture::frame::ch_count is set to 1 and min_device_channels to minimal number of channels that device supports.

Definition in file alsa.c.

Macro Definition Documentation

◆ ALSA_PCM_NEW_HW_PARAMS_API

#define ALSA_PCM_NEW_HW_PARAMS_API

Definition at line 48 of file alsa.c.

◆ DEFAULT_SAMPLE_RATE

#define DEFAULT_SAMPLE_RATE   48000

Definition at line 108 of file alsa.c.

Referenced by audio_cap_alsa_init().

◆ MOD_NAME

Function Documentation

◆ audio_cap_alsa_done()

static void audio_cap_alsa_done ( void * state)
static

Definition at line 381 of file alsa.c.

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}
void ** state
char * data
Definition types.h:102
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
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97

References state_alsa_capture::captured_samples, audio_frame::data, state_alsa_capture::frame, state_alsa_capture::handle, state_alsa_capture::start_time, state, state_alsa_capture::tmp_data, and tv_diff().

Here is the call graph for this function:

◆ audio_cap_alsa_help()

static void audio_cap_alsa_help ( void )
static

Definition at line 92 of file alsa.c.

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}
static void audio_alsa_list_devices(void)
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

References audio_alsa_list_devices(), color_printf(), TERM_BOLD, TERM_FG_RED, and TERM_RESET.

Referenced by audio_cap_alsa_init().

Here is the call graph for this function:

◆ audio_cap_alsa_init()

static void * audio_cap_alsa_init ( struct module * parent,
const char * cfg )
static

Definition at line 110 of file alsa.c.

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}
#define UNUSED(x)
int void * val
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 print_alsa_device_info(snd_pcm_t *handle, const char *module_name)
#define MOD_NAME
Definition alsa.c:67
#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
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
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 DEFAULT_AUDIO_CAPTURE_CHANNELS
Definition host.h:89
#define INIT_NOERR
Definition host.h:73
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
int sample_rate
Definition types.h:101
int ch_count
Definition types.h:104
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
snd_pcm_uframes_t frames
Definition alsa.c:74

References audio_cap_alsa_help(), audio_capture_bps, audio_capture_channels, audio_capture_sample_rate, audio_frame::bps, bps_preference, bps_to_snd_fmts, bug_msg(), audio_frame::ch_count, audio_frame::data, DEFAULT_AUDIO_CAPTURE_CHANNELS, DEFAULT_SAMPLE_RATE, state_alsa_capture::frame, state_alsa_capture::frames, get_rate_near(), state_alsa_capture::handle, INIT_NOERR, LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, log_msg(), audio_frame::max_size, state_alsa_capture::min_device_channels, MOD_NAME, name, state_alsa_capture::non_interleaved, params, parent, print_alsa_device_info(), audio_frame::sample_rate, state_alsa_capture::start_time, state_alsa_capture::tmp_data, UNUSED, and val.

Here is the call graph for this function:

◆ audio_cap_alsa_probe()

static void audio_cap_alsa_probe ( struct device_info ** available_devices,
int * count,
void(** deleter )(void *) )
static

Definition at line 83 of file alsa.c.

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}
int void(** deleter)(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
int count
Definition hd-rum.c:44

References audio_alsa_probe(), count, deleter, and name.

Here is the call graph for this function:

◆ audio_cap_alsa_read()

static struct audio_frame * audio_cap_alsa_read ( void * state)
static

Definition at line 324 of file alsa.c.

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}
#define LOG_LEVEL_FATAL
errors that prevent UG run
Definition debug.h:58
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
void exit_uv(int status)
Definition host.cpp:625
#define EXIT_FAIL_AUDIO
Definition host.h:70
int data_len
Definition types.h:103
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

References audio_frame::bps, state_alsa_capture::captured_samples, audio_frame::ch_count, audio_frame::data, audio_frame::data_len, demux_channel(), EXIT_FAIL_AUDIO, exit_uv(), state_alsa_capture::frame, state_alsa_capture::frames, state_alsa_capture::handle, LOG_LEVEL_FATAL, LOG_LEVEL_WARNING, log_msg(), state_alsa_capture::min_device_channels, MOD_NAME, state_alsa_capture::non_interleaved, signed2unsigned(), state, and state_alsa_capture::tmp_data.

Here is the call graph for this function:

◆ REGISTER_MODULE()

REGISTER_MODULE ( alsa ,
& acap_alsa_info,
LIBRARY_CLASS_AUDIO_CAPTURE ,
AUDIO_CAPTURE_ABI_VERSION  )

Variable Documentation

◆ acap_alsa_info

const struct audio_capture_info acap_alsa_info
static
Initial value:
= {
}
static void * audio_cap_alsa_init(struct module *parent, const char *cfg)
Definition alsa.c:110
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
static void audio_cap_alsa_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition alsa.c:83

Definition at line 397 of file alsa.c.

◆ bps_preference

const int bps_preference[] = { 2, 4, 3, 1 }
static

Definition at line 106 of file alsa.c.

106{ 2, 4, 3, 1 };

Referenced by audio_cap_alsa_init().