UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
alsa.c File Reference
#include <alloca.h>
#include <alsa/asoundlib.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "alsa_common.h"
#include "audio/audio_playback.h"
#include "audio/types.h"
#include "audio/utils.h"
#include "debug.h"
#include "host.h"
#include "lib_common.h"
#include "tv.h"
#include "types.h"
#include "utils/color_out.h"
#include "utils/audio_buffer.h"
Include dependency graph for alsa.c:

Go to the source code of this file.

Classes

struct  state_alsa_playback
 

Macros

#define BUF_LEN_DEFAULT_MS   60
 
#define BUF_LEN_DEFAULT_SYNC_MS   200
 
#define CHECK_OK(cmd)
 
#define EXIT_IF_FAILED(cmd, name)
 
#define MOD_NAME   "[ALSA play.] "
 
#define SCRATCHPAD_SIZE   (1024*1024)
 

Enumerations

enum  playback_mode_t { THREAD = 0 , SYNC , ASYNC }
 

Functions

 ADD_TO_PARAM ("alsa-play-period-size", "* alsa-play-period-size=<frames>\n" " ALSA playback period size in frames (default is device minimum) .\n")
 
 ADD_TO_PARAM ("alsa-playback-api", "* alsa-playback-api={thread|sync|async}\n" " ALSA API.\n")
 
 ADD_TO_PARAM ("alsa-playback-buffer", "* alsa-playback-buffer=<len>\n" " Buffer length. Can be used to balance robustness and latency, in microseconds.\n")
 
static void alsa_play_async_callback (snd_async_handler_t *handler)
 
static bool audio_play_alsa_ctl (void *state, int request, void *data, size_t *len)
 
static void audio_play_alsa_done (void *state)
 
static void audio_play_alsa_help (void)
 
static void * audio_play_alsa_init (const char *cfg)
 
static void audio_play_alsa_probe (struct device_info **available_devices, int *count, void(**deleter)(void *))
 
static void audio_play_alsa_put_frame (void *state, const struct audio_frame *frame)
 
static bool audio_play_alsa_query_format (struct state_alsa_playback *s, void *data, size_t *len)
 
static bool audio_play_alsa_reconfigure (void *state, struct audio_desc desc)
 
static void audio_play_alsa_write_frame (void *state, const struct audio_frame *frame)
 
static long get_sched_latency_ns (void)
 
static snd_config_t * init_local_config_with_workaround (char const *pcm_node_name)
 
static bool is_default_pulse (void)
 
 REGISTER_MODULE (alsa, &aplay_alsa_info, LIBRARY_CLASS_AUDIO_PLAYBACK, AUDIO_PLAYBACK_ABI_VERSION)
 
static void set_device_buffer (snd_pcm_t *handle, playback_mode_t playback_mode, snd_pcm_hw_params_t *params)
 
static void * worker (void *args)
 
static void write_fill (struct state_alsa_playback *s)
 
static int write_samples (snd_pcm_t *handle, const char *data, int bps, int ch_count, int frames, bool noninterleaved, playback_mode_t playback_mode, char *tmp_buffer)
 

Variables

static const struct audio_playback_info aplay_alsa_info
 

Detailed Description

Author
Martin Pulec pulec.nosp@m.@ces.nosp@m.net.c.nosp@m.z

Definition in file alsa.c.

Macro Definition Documentation

◆ BUF_LEN_DEFAULT_MS

#define BUF_LEN_DEFAULT_MS   60

Definition at line 72 of file alsa.c.

Referenced by set_device_buffer().

◆ BUF_LEN_DEFAULT_SYNC_MS

#define BUF_LEN_DEFAULT_SYNC_MS   200

Definition at line 73 of file alsa.c.

Referenced by set_device_buffer().

◆ CHECK_OK

#define CHECK_OK ( cmd)
Value:
do {\
int rc = cmd;\
if (rc < 0) {;\
log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s: %s\n", #cmd, snd_strerror(rc));\
}\
} while (0)
#define MOD_NAME
Definition alsa.c:67
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59

Definition at line 98 of file alsa.c.

98#define CHECK_OK(cmd) \
99 do {\
100 int rc = cmd;\
101 if (rc < 0) {;\
102 log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s: %s\n", #cmd, snd_strerror(rc));\
103 }\
104 } while (0)

Referenced by audio_play_alsa_reconfigure(), and set_device_buffer().

◆ EXIT_IF_FAILED

#define EXIT_IF_FAILED ( cmd,
name )
Value:
do {\
int rc = cmd;\
if (rc < 0) {;\
log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s: %s\n", name, snd_strerror(rc));\
goto error;\
}\
} while (0)
const char * name
Definition settings.cpp:193

Speex jitter buffer use is currently not stable and not ready for production use. Moreover, it is unclear whether is suitable for our use case.

Definition at line 89 of file alsa.c.

89#define EXIT_IF_FAILED(cmd, name) \
90 do {\
91 int rc = cmd;\
92 if (rc < 0) {;\
93 log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s: %s\n", name, snd_strerror(rc));\
94 goto error;\
95 }\
96 } while (0)

Referenced by audio_play_alsa_reconfigure().

◆ MOD_NAME

#define MOD_NAME   "[ALSA play.] "

Definition at line 74 of file alsa.c.

◆ SCRATCHPAD_SIZE

#define SCRATCHPAD_SIZE   (1024*1024)

Definition at line 75 of file alsa.c.

Referenced by audio_play_alsa_init(), and write_samples().

Enumeration Type Documentation

◆ playback_mode_t

Enumerator
THREAD 
SYNC 
ASYNC 

Definition at line 106 of file alsa.c.

106 {
107 THREAD = 0,
108 SYNC,
109 ASYNC
playback_mode_t
Definition alsa.c:106
@ SYNC
Definition alsa.c:108
@ ASYNC
Definition alsa.c:109
@ THREAD
Definition alsa.c:107

Function Documentation

◆ ADD_TO_PARAM() [1/3]

ADD_TO_PARAM ( "alsa-play-period-size" )

◆ ADD_TO_PARAM() [2/3]

ADD_TO_PARAM ( "alsa-playback-api" )

◆ ADD_TO_PARAM() [3/3]

ADD_TO_PARAM ( "alsa-playback-buffer" ,
"* alsa-playback- buffer = <len>\n" "  Buffer length. Can be used to balance robustness and latency,
in microseconds.\n"  )

◆ alsa_play_async_callback()

static void alsa_play_async_callback ( snd_async_handler_t * handler)
static

Definition at line 396 of file alsa.c.

396 {
397 struct state_alsa_playback *s =
398 snd_async_handler_get_callback_private(handler);
399
400 struct audio_frame f = { .bps = s->desc.bps,
401 .sample_rate = s->desc.sample_rate,
402 .ch_count = s->desc.ch_count };
403
404 size_t len = f.bps * f.ch_count * s->period_size;
405
406 char *data = malloc(len);
407
408 pthread_mutex_lock(&s->lock);
409
410 snd_pcm_sframes_t avail;
411 avail = snd_pcm_avail_update(s->handle);
412 while (avail >= (snd_pcm_sframes_t) s->period_size)
413 {
414 int ret;
415 ret = audio_buffer_read(s->buf, data, len);
416 memset(data + ret, 0, len - ret);
417 f.data = data;
418 f.data_len = len;
420 avail = snd_pcm_avail_update(s->handle);
421 }
422
423 free(data);
424
425 pthread_mutex_unlock(&s->lock);
426}
int void size_t * len
int audio_buffer_read(struct audio_buffer *buf, char *out, int max_len)
static void audio_play_alsa_write_frame(void *state, const struct audio_frame *frame)
Definition alsa.c:984
int sample_rate
Definition types.h:78
int ch_count
Definition types.h:79
int bps
Definition types.h:77
struct used to comunicate with audio capture/playback device
Definition types.h:99
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
pthread_mutex_t lock
Definition alsa.c:135
struct audio_desc desc
Definition alsa.c:114
audio_buffer_t * buf
Definition alsa.c:132
snd_pcm_uframes_t period_size
Definition alsa.c:124
snd_pcm_t * handle
Definition alsa.c:113

References audio_buffer_read(), audio_play_alsa_write_frame(), audio_desc::bps, audio_frame::bps, state_alsa_playback::buf, audio_desc::ch_count, audio_frame::ch_count, audio_frame::data, audio_frame::data_len, state_alsa_playback::desc, state_alsa_playback::handle, len, state_alsa_playback::lock, state_alsa_playback::period_size, and audio_desc::sample_rate.

Referenced by audio_play_alsa_reconfigure().

Here is the call graph for this function:

◆ audio_play_alsa_ctl()

static bool audio_play_alsa_ctl ( void * state,
int request,
void * data,
size_t * len )
static

Definition at line 383 of file alsa.c.

384{
385 struct state_alsa_playback *s = (struct state_alsa_playback *) state;
386
387 switch (request) {
389 return audio_play_alsa_query_format(s, data, len);
390 default:
391 return false;
392 }
393
394}
void ** state
#define AUDIO_PLAYBACK_CTL_QUERY_FORMAT
static bool audio_play_alsa_query_format(struct state_alsa_playback *s, void *data, size_t *len)
Definition alsa.c:261

References audio_play_alsa_query_format(), AUDIO_PLAYBACK_CTL_QUERY_FORMAT, len, and state.

Referenced by audio_play_alsa_init().

Here is the call graph for this function:

◆ audio_play_alsa_done()

static void audio_play_alsa_done ( void * state)
static

Definition at line 1064 of file alsa.c.

1065{
1066 struct state_alsa_playback *s = (struct state_alsa_playback *) state;
1067
1068 if (s->playback_mode == THREAD && s->thread_started) {
1069 pthread_mutex_lock(&s->lock);
1070 s->should_exit_thread = true;
1071 pthread_mutex_unlock(&s->lock);
1072 pthread_join(s->thread_id, NULL);
1073 }
1074
1075 if (s->playback_mode == ASYNC && s->pcm_callback) {
1076 snd_async_del_handler(s->pcm_callback);
1077 }
1078
1079 snd_pcm_drain(s->handle);
1080 snd_pcm_close(s->handle);
1081 if (s->local_config) {
1082 snd_config_delete(s->local_config);
1083 }
1084 if (s->buf != NULL) {
1085#ifdef USE_SPEEX_JITTER_BUFFER
1086 jitter_buffer_destroy(s->buf);
1087#else
1089#endif
1090 }
1091
1092 pthread_mutex_destroy(&s->lock);
1093
1094 free(s->scratchpad);
1095 free(s);
1096}
void audio_buffer_destroy(struct audio_buffer *buf)
bool should_exit_thread
Definition alsa.c:136
playback_mode_t playback_mode
Definition alsa.c:122
snd_async_handler_t * pcm_callback
Definition alsa.c:142
bool thread_started
Definition alsa.c:137
pthread_t thread_id
Definition alsa.c:128
char * scratchpad
Definition alsa.c:146
snd_config_t * local_config
Definition alsa.c:119

References ASYNC, audio_buffer_destroy(), state_alsa_playback::buf, state_alsa_playback::handle, state_alsa_playback::local_config, state_alsa_playback::lock, state_alsa_playback::pcm_callback, state_alsa_playback::playback_mode, state_alsa_playback::scratchpad, state_alsa_playback::should_exit_thread, state, THREAD, state_alsa_playback::thread_id, and state_alsa_playback::thread_started.

Here is the call graph for this function:

◆ audio_play_alsa_help()

static void audio_play_alsa_help ( void )
static

Definition at line 706 of file alsa.c.

706 {
707 printf("Usage\n");
708 color_printf(TERM_BOLD TERM_FG_RED "\t-r alsa" TERM_FG_RESET "[:<device>] --param alsa-playback-api={thread|async|sync}[,alsa-playback-buffer=[<us>-]<us>][,audio-buffer-len=<ablen>]\n" TERM_RESET);
709 color_printf("where\n");
710 color_printf(TERM_BOLD "\talsa-playback-api={thread|async|sync}\n" TERM_RESET);
711 color_printf("\t\tuse selected API ('thread' is default)\n");
712 color_printf(TERM_BOLD "\talsa-playback-buffer=[<us>-]<us>\n" TERM_RESET);
713 color_printf("\t\tset buffer max and optionally max (thread and async API only)\n");
714 color_printf(TERM_BOLD "\taudio-buffer-len=<ablen>\n" TERM_RESET);
715 color_printf("\t\tlength of UG internal ALSA buffer (in milliseconds)\n");
716 printf("\n");
717
718 printf("Available ALSA playback devices:\n");
720}
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
#define TERM_FG_RESET
Definition color_out.h:59

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

Referenced by audio_play_alsa_init().

Here is the call graph for this function:

◆ audio_play_alsa_init()

static void * audio_play_alsa_init ( const char * cfg)
static

Definition at line 837 of file alsa.c.

838{
839 int rc;
840 const char *name;
841
842 struct state_alsa_playback *s = calloc(1, sizeof(struct state_alsa_playback));
843
844 long latency_ns = get_sched_latency_ns();
845 if (latency_ns > 0) {
846 s->sched_latency_ms = latency_ns / 1000 / 1000;
847 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Sched latency: %ld ms.\n", s->sched_latency_ms);
848 } else {
849 s->sched_latency_ms = 24;
850 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Unable to get latency, assuming %ld.\n", s->sched_latency_ms);
851 }
852
853 const char *use_api;
854 use_api = get_commandline_param("alsa-playback-api");
855 if (use_api) {
856 if (strcmp(use_api, "thread") == 0 || strcmp(use_api, "new") == 0) {
858 } else if (strcmp(use_api, "sync") == 0 || strcmp(use_api, "old") == 0) {
859 s->playback_mode = SYNC;
860 } else if (strcmp(use_api, "async") == 0) {
861 s->playback_mode = ASYNC;
862 } else {
863 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unknown API string \"%s\"!\n", use_api);
864 free(s);
865 return NULL;
866 }
867 }
868
869 if (s->playback_mode == THREAD) {
870 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Using new API. In case of problems, you may try to use '--param alsa-playback-api=sync'.\n");
871 }
872
873 if (s->playback_mode == ASYNC) {
874 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Async API is experimental, in case of problems use either \"thread\" or \"sync\" API\n");
875 }
876
877 if (strlen(cfg) > 0) {
878 if(strcmp(cfg, "help") == 0) {
880 free(s);
881 return INIT_NOERR;
882 }
883 name = cfg;
884 } else {
885 if (is_default_pulse()) {
886 name = "pulse";
887 } else {
888 name = "default";
889 }
890 }
891
892 char device[1024] = "pcm.";
893
894 strncat(device, name, sizeof(device) - strlen(device) - 1);
895
896 if (s->playback_mode == SYNC) {
898 }
899
900 if (s->playback_mode == SYNC && s->local_config) {
901 rc = snd_pcm_open_lconf(&s->handle, name,
902 SND_PCM_STREAM_PLAYBACK, 0, s->local_config);
903 } else {
904 rc = snd_pcm_open(&s->handle, name,
905 SND_PCM_STREAM_PLAYBACK, 0);
906 }
907
908 if (rc < 0) {
909 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to open pcm device: %s\n",
910 snd_strerror(rc));
911 goto error;
912 }
914
915 pthread_mutex_init(&s->lock, NULL);
916 if (s->playback_mode == THREAD) {
917#ifdef USE_SPEEX_JITTER_BUFFER
918 s->buf = jitter_buffer_init(1);
919#endif
920 } else if (s->playback_mode == SYNC) {
921 rc = snd_pcm_nonblock(s->handle, 1);
922 if(rc < 0) {
923 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Warning: Unable to set nonblock mode.\n");
924 }
925 }
926
927 /* CC3000e playback needs to be initialized prior to capture
928 * (to arbitrary value) in order to work. This hack ensures
929 * that. */
930 if (snd_pcm_name(s->handle) && strstr(snd_pcm_name(s->handle), "CARD=Speakerph")) {
931 struct audio_desc desc = {2, 48000, 1, AC_PCM};
932 size_t len = sizeof desc;
935 }
936 }
937
938 s->scratchpad = malloc(SCRATCHPAD_SIZE);
939 return s;
940
941error:
942 free(s);
943 return NULL;
944}
struct video_desc desc
static void print_alsa_device_info(snd_pcm_t *handle, const char *module_name)
@ AC_PCM
Definition types.h:53
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
const char * get_commandline_param(const char *key)
Definition host.cpp:894
#define INIT_NOERR
Definition host.h:73
static long get_sched_latency_ns(void)
Definition alsa.c:156
static bool is_default_pulse(void)
Definition alsa.c:722
static bool audio_play_alsa_ctl(void *state, int request, void *data, size_t *len)
Definition alsa.c:383
#define SCRATCHPAD_SIZE
Definition alsa.c:75
static bool audio_play_alsa_reconfigure(void *state, struct audio_desc desc)
Definition alsa.c:496
static void audio_play_alsa_help(void)
Definition alsa.c:706
static snd_config_t * init_local_config_with_workaround(char const *pcm_node_name)
Definition alsa.c:770
long sched_latency_ms
Definition alsa.c:144

References AC_PCM, ASYNC, audio_play_alsa_ctl(), audio_play_alsa_help(), audio_play_alsa_reconfigure(), AUDIO_PLAYBACK_CTL_QUERY_FORMAT, state_alsa_playback::buf, desc, get_commandline_param(), get_sched_latency_ns(), state_alsa_playback::handle, init_local_config_with_workaround(), INIT_NOERR, is_default_pulse(), len, state_alsa_playback::local_config, state_alsa_playback::lock, LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, name, state_alsa_playback::playback_mode, print_alsa_device_info(), state_alsa_playback::sched_latency_ms, state_alsa_playback::scratchpad, SCRATCHPAD_SIZE, SYNC, and THREAD.

Here is the call graph for this function:

◆ audio_play_alsa_probe()

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

Definition at line 697 of file alsa.c.

698{
699 *deleter = free;
700 const char *whitelist[] = {"pulse", "dmix"};
701 audio_alsa_probe(available_devices, count, whitelist, sizeof(whitelist) / sizeof(*whitelist));
702 strcpy((*available_devices)[0].dev, "");
703 strcpy((*available_devices)[0].name, "Default Linux audio output");
704}
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_play_alsa_put_frame()

static void audio_play_alsa_put_frame ( void * state,
const struct audio_frame * frame )
static

Definition at line 1029 of file alsa.c.

1030{
1031 struct state_alsa_playback *s = state;
1032
1033 // first repair asyn stream if needed
1034 if (s->playback_mode == ASYNC && snd_pcm_state(s->handle) != SND_PCM_STATE_RUNNING) {
1035 log_msg(LOG_LEVEL_ERROR, MOD_NAME "%s\n", alsa_get_pcm_state_name(snd_pcm_state(s->handle)));
1036 snd_pcm_prepare(s->handle);
1037 write_fill(s);
1038 int err = snd_pcm_start(s->handle);
1039 if (err < 0) {
1040 printf("Start error: %s\n", snd_strerror(err));
1041 }
1042 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "%s\n", alsa_get_pcm_state_name(snd_pcm_state(s->handle)));
1043 }
1044
1045 if (s->playback_mode == THREAD || s->playback_mode == ASYNC) {
1046 pthread_mutex_lock(&s->lock);
1047#ifdef USE_SPEEX_JITTER_BUFFER
1048 JitterBufferPacket pkt;
1049 pkt.data = frame->data;
1050 pkt.len = frame->data_len;
1051 pkt.timestamp = s->timestamp;
1052 pkt.span = frame->data_len / s->desc.bps / s->desc.ch_count;
1053 s->timestamp += pkt.span;
1054 jitter_buffer_put(s->buf, &pkt);
1055#else
1056 audio_buffer_write(s->buf, frame->data, frame->data_len);
1057#endif
1058 pthread_mutex_unlock(&s->lock);
1059 } else {
1061 }
1062}
struct video_frame * frame
static const char * alsa_get_pcm_state_name(snd_pcm_state_t state) __attribute__((unused))
void audio_buffer_write(struct audio_buffer *buf, const char *in, int len)
static void write_fill(struct state_alsa_playback *s)
Definition alsa.c:428
uint32_t timestamp
Definition alsa.c:138
int64_t timestamp
Definition types.h:331

References alsa_get_pcm_state_name(), ASYNC, audio_buffer_write(), audio_play_alsa_write_frame(), audio_desc::bps, state_alsa_playback::buf, audio_desc::ch_count, state_alsa_playback::desc, frame, state_alsa_playback::handle, state_alsa_playback::lock, LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, log_msg(), MOD_NAME, state_alsa_playback::playback_mode, state, THREAD, state_alsa_playback::timestamp, video_frame::timestamp, and write_fill().

Here is the call graph for this function:

◆ audio_play_alsa_query_format()

static bool audio_play_alsa_query_format ( struct state_alsa_playback * s,
void * data,
size_t * len )
static

Definition at line 261 of file alsa.c.

262{
263 struct audio_desc desc;
264 if (*len < sizeof desc) {
265 return false;
266 } else {
267 memcpy(&desc, data, sizeof desc);
268 }
269
270 int rc;
271 unsigned int val;
272 int dir;
273 snd_pcm_hw_params_t *params;
274 struct audio_desc ret;
275
276 memset(&ret, 0, sizeof ret);
277
278 ret.codec = AC_PCM;
279
280 snd_pcm_hw_params_alloca(&params);
281
282 /* Fill it in with default values. */
283 rc = snd_pcm_hw_params_any(s->handle, params);
284 if (rc < 0) {
285 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to set default parameters: %s\n",
286 snd_strerror(rc));
287 return false;
288 }
289
290 assert (desc.bps > 0 && desc.bps <= 4);
291 int bps = desc.bps;
292 for ( ; ; ) {
293 if (!snd_pcm_hw_params_test_format(s->handle, params, bps_to_snd_fmts[bps])) {
294 break;
295 }
296 // We try to find nearest higher
297 if (bps >= desc.bps) {
298 bps++;
299 } else { // or nearest lower
300 bps--;
301 }
302 if (bps > 4) {
303 bps = desc.bps - 1;
304 }
305 if (bps == 0) {
306 break;
307 }
308 }
309
310 ret.bps = bps;
311
312 rc = snd_pcm_hw_params_set_format(s->handle, params, bps_to_snd_fmts[bps]);
313 if (rc < 0) {
314 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to set capture format: %s\n",
315 snd_strerror(rc));
316 return false;
317 }
318
319 ret.sample_rate = get_rate_near(s->handle, params, desc.sample_rate);
320 if (!ret.sample_rate) {
321 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to find sample rate:\n");
322 return false;
323 }
324
325 /* set sampling rate */
326 val = ret.sample_rate;
327 dir = 0;
328 rc = snd_pcm_hw_params_set_rate_near(s->handle, params,
329 &val, &dir);
330 if (rc < 0) {
331 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to set sampling rate (%s %d): %s\n",
332 dir == 0 ? "=" : (dir == -1 ? "<" : ">"),
333 val, snd_strerror(rc));
334 return false;
335 }
336
337 unsigned int max_channels;
338 rc = snd_pcm_hw_params_get_channels_max(params, &max_channels);
339 if (rc < 0) {
340 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to get max number of channels!\n");
341 return false;
342 }
343 int channels = desc.ch_count;
344 // We try to find nearest higher
345 for ( ; channels <= (int) max_channels; ++channels) {
346 if (!snd_pcm_hw_params_test_channels(s->handle, params, channels)) {
347 break;
348 }
349 }
350 if (channels > (int) max_channels) { // or nearest lower
351 for (channels = desc.ch_count - 1; channels > 0; --channels) {
352 if (!snd_pcm_hw_params_test_channels(s->handle, params, channels)) {
353 break;
354 }
355 }
356 }
357
358 if (channels == 0) {
359 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to get usable channel configuration (max channels: %u)!\n", max_channels);
360 return false;
361 }
362
363 rc = snd_pcm_hw_params_set_channels(s->handle, params, channels);
364 if (rc < 0) {
365 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to set channels!\n");
366 return false;
367 }
368
369 ret.ch_count = channels;
370
371 if (snd_pcm_hw_params_test_access(s->handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)
372 && snd_pcm_hw_params_test_access(s->handle, params, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
373
374 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot find supported access mode!\n");
375 return false;
376 }
377
378 memcpy(data, &ret, sizeof ret);
379 *len = sizeof ret;
380 return true;
381}
int void * val
int int channels
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 struct @19 params[100]

References AC_PCM, audio_desc::bps, bps_to_snd_fmts, audio_desc::ch_count, channels, audio_desc::codec, desc, get_rate_near(), state_alsa_playback::handle, len, LOG_LEVEL_ERROR, log_msg(), MOD_NAME, params, audio_desc::sample_rate, and val.

Referenced by audio_play_alsa_ctl().

Here is the call graph for this function:

◆ audio_play_alsa_reconfigure()

static bool audio_play_alsa_reconfigure ( void * state,
struct audio_desc desc )
static
Todo
Consider using snd_pcm_hw_params_set_buffer_time_first() by default, it works fine with PulseAudio. However, may underrun with different drivers/devices (Juli@?) where the buffer size is significantly lower.

Definition at line 496 of file alsa.c.

497{
498 struct state_alsa_playback *s = (struct state_alsa_playback *) state;
499 snd_pcm_hw_params_t *params;
500 snd_pcm_format_t format;
501 unsigned int val;
502 int dir;
503 int rc;
504
505 if (s->playback_mode == THREAD && s->thread_started) {
506 pthread_mutex_lock(&s->lock);
507 s->should_exit_thread = true;
508 pthread_mutex_unlock(&s->lock);
509 pthread_join(s->thread_id, NULL);
510 s->should_exit_thread = false;
511 s->thread_started = false;
512 }
513 if (s->playback_mode == ASYNC && s->pcm_callback) {
514 snd_async_del_handler(s->pcm_callback);
515 s->pcm_callback = NULL;
516 }
517
518 s->desc.bps = desc.bps;
519 s->desc.ch_count = desc.ch_count;
520 s->desc.sample_rate = desc.sample_rate;
521
522 if (snd_pcm_state(s->handle) == SND_PCM_STATE_RUNNING) {
523 CHECK_OK(snd_pcm_drop(s->handle)); // stop the stream
524 }
525 snd_pcm_state_t current_state = snd_pcm_state(s->handle);
526 if (current_state != SND_PCM_STATE_OPEN &&
527 current_state != SND_PCM_STATE_SETUP) {
528 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Unexpected state: %s\n",
529 alsa_get_pcm_state_name(current_state));
530 }
531
532 /* Allocate a hardware parameters object. */
533 snd_pcm_hw_params_alloca(&params);
534
535 /* Fill it in with default values. */
536 rc = snd_pcm_hw_params_any(s->handle, params);
537 if (rc < 0) {
538 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot obtain default hw parameters: %s\n",
539 snd_strerror(rc));
540 return false;
541 }
542
543 /* Set the desired hardware parameters. */
544
545 /* Interleaved mode */
546 rc = snd_pcm_hw_params_set_access(s->handle, params,
547 SND_PCM_ACCESS_RW_INTERLEAVED);
548 if (rc < 0) {
549 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot set interleaved hw access: %s\n",
550 snd_strerror(rc));
551 rc = snd_pcm_hw_params_set_access(s->handle, params,
552 SND_PCM_ACCESS_RW_NONINTERLEAVED);
553 if (rc < 0) {
554 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot set non-interleaved hw access: %s\n",
555 snd_strerror(rc));
556 return false;
557 }
558 s->non_interleaved = true;
559 } else {
560 s->non_interleaved = false;
561 }
562
563 if (desc.bps > 4 || desc.bps < 1) {
564 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Unsupported BPS for audio (%d).\n",
565 desc.bps * 8);
566 return false;
567
568 }
569 format = bps_to_snd_fmts[desc.bps];
570
571 /* Signed 16-bit little-endian format */
572 rc = snd_pcm_hw_params_set_format(s->handle, params,
573 format);
574 if (rc < 0) {
575 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot set format: %s\n",
576 snd_strerror(rc));
577 return false;
578 }
579
580 /* Two channels (stereo) */
581 rc = snd_pcm_hw_params_set_channels(s->handle, params, desc.ch_count);
582 if (rc < 0) {
583 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot set requested channel count: %s\n",
584 snd_strerror(rc));
585 return false;
586 }
587
588 /* we want to resample if device doesn't support default sample rate */
589 val = 1;
590 rc = snd_pcm_hw_params_set_rate_resample(s->handle,
591 params, val);
592 if(rc < 0) {
593 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Warning: Unable to set resampling: %s\n",
594 snd_strerror(rc));
595 }
596
597
598 /* 44100 bits/second sampling rate (CD quality) */
599 val = desc.sample_rate;
600 dir = 0;
601 rc = snd_pcm_hw_params_set_rate_near(s->handle, params,
602 &val, &dir);
603 if (rc < 0) {
604 log_msg(LOG_LEVEL_ERROR, MOD_NAME "cannot set requested sample rate: %s\n",
605 snd_strerror(rc));
606 return false;
607 }
608
609 /* Set period to its minimal size.
610 *
611 * Do not use snd_pcm_hw_params_set_period_time_near(), since it
612 * allows to set also unsupported value without notifying. Using
613 * snd_pcm_hw_params_set_period_time_first() with Pulseaudio
614 * returns invalid argument.
615 *
616 * See also http://www.alsa-project.org/main/index.php/FramesPeriods */
617 s->period_size = 1;
618 if (get_commandline_param("alsa-play-period-size")) {
619 s->period_size = atoi(get_commandline_param("alsa-play-period-size"));
620 }
621 dir = 1;
622 rc = snd_pcm_hw_params_set_period_size_min(s->handle,
623 params, &s->period_size, &dir);
624 if (rc < 0) {
625 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Warning: cannot set period time: %s\n",
626 snd_strerror(rc));
627 } else {
628 log_msg(LOG_LEVEL_INFO, MOD_NAME "Period size: %lu frames (%lf ms)\n", s->period_size, (double) s->period_size / desc.sample_rate * 1000);
629 }
630
632
633 /* Write the parameters to the driver */
634 rc = snd_pcm_hw_params(s->handle, params);
635 if (rc < 0) {
636 log_msg(LOG_LEVEL_ERROR, MOD_NAME "unable to set hw parameters: %s\n",
637 snd_strerror(rc));
638 return false;
639 }
640
641 snd_pcm_hw_params_current(s->handle, params);
642 snd_pcm_hw_params_get_buffer_size(params, &s->buffer_size);
643
644 if (s->playback_mode == THREAD || s->playback_mode == ASYNC) {
645#ifdef USE_SPEEX_JITTER_BUFFER
646 jitter_buffer_reset(s->buf);
647#else
649 s->audio_buf_len_ms = get_commandline_param("low-latency-audio") ? 5 : s->sched_latency_ms * 2;
650 if (get_commandline_param("audio-buffer-len")) {
651 s->audio_buf_len_ms = atoi(get_commandline_param("audio-buffer-len"));
652 }
653 log_msg(LOG_LEVEL_INFO, "[ALSA play.] Setting audio buffer length: %ld ms\n", s->audio_buf_len_ms);
655#endif
656 }
657
658 if (s->playback_mode == THREAD) {
659 s->timestamp = 0;
660 pthread_create(&s->thread_id, NULL, worker, s);
661 s->thread_started = true;
662 }
663
664 if (s->playback_mode == ASYNC) {
665 snd_pcm_sw_params_t *sw_params;
666 CHECK_OK(snd_pcm_sw_params_malloc(&sw_params));
667 CHECK_OK(snd_pcm_sw_params_current (s->handle, sw_params));
668 CHECK_OK(snd_pcm_sw_params_set_start_threshold(s->handle, sw_params, s->buffer_size - s->period_size));
669 CHECK_OK(snd_pcm_sw_params_set_stop_threshold(s->handle, sw_params, s->buffer_size));
670 CHECK_OK(snd_pcm_sw_params_set_avail_min(s->handle, sw_params, s->period_size));
671 CHECK_OK(snd_pcm_sw_params(s->handle, sw_params));
672 snd_pcm_sw_params_free (sw_params);
673
674 EXIT_IF_FAILED(snd_async_add_pcm_handler(&s->pcm_callback, s->handle, alsa_play_async_callback, s), "Add async handler");
675
676 write_fill(s);
677
678 if (snd_pcm_state(s->handle) == SND_PCM_STATE_PREPARED) {
679 int err = snd_pcm_start(s->handle);
680 if (err < 0) {
681 printf("Start error: %s\n", snd_strerror(err));
682 goto error;
683 }
684 }
685 if (snd_pcm_state(s->handle) != SND_PCM_STATE_RUNNING) {
686 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Device was not running: %s!\n",
687 alsa_get_pcm_state_name(snd_pcm_state(s->handle)));
688 goto error;
689 }
690 }
691
692 return true;
693error:
694 return false;
695}
struct audio_buffer * audio_buffer_init(int sample_rate, int bps, int ch_count, int suggested_latency_ms)
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
static void alsa_play_async_callback(snd_async_handler_t *handler)
Definition alsa.c:396
#define CHECK_OK(cmd)
Definition alsa.c:98
static void set_device_buffer(snd_pcm_t *handle, playback_mode_t playback_mode, snd_pcm_hw_params_t *params)
Definition alsa.c:445
#define EXIT_IF_FAILED(cmd, name)
Definition alsa.c:89
static void * worker(void *args)
Definition alsa.c:190
snd_pcm_uframes_t buffer_size
Definition alsa.c:125
bool non_interleaved
Definition alsa.c:121
long int audio_buf_len_ms
Definition alsa.c:133

References alsa_get_pcm_state_name(), alsa_play_async_callback(), ASYNC, state_alsa_playback::audio_buf_len_ms, audio_buffer_destroy(), audio_buffer_init(), audio_desc::bps, bps_to_snd_fmts, state_alsa_playback::buf, state_alsa_playback::buffer_size, audio_desc::ch_count, CHECK_OK, desc, state_alsa_playback::desc, EXIT_IF_FAILED, get_commandline_param(), state_alsa_playback::handle, state_alsa_playback::lock, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, state_alsa_playback::non_interleaved, params, state_alsa_playback::pcm_callback, state_alsa_playback::period_size, state_alsa_playback::playback_mode, audio_desc::sample_rate, state_alsa_playback::sched_latency_ms, set_device_buffer(), state_alsa_playback::should_exit_thread, state, THREAD, state_alsa_playback::thread_id, state_alsa_playback::thread_started, state_alsa_playback::timestamp, val, worker(), and write_fill().

Referenced by audio_play_alsa_init().

Here is the call graph for this function:

◆ audio_play_alsa_write_frame()

static void audio_play_alsa_write_frame ( void * state,
const struct audio_frame * frame )
static

Definition at line 984 of file alsa.c.

985{
986 struct state_alsa_playback *s = (struct state_alsa_playback *) state;
987 int rc;
988
990 snd_pcm_sframes_t delay = 0;
991 snd_pcm_delay(s->handle, &delay);
992 fprintf(stderr, "Alsa delay: %d samples (%u Hz)\n", (int) delay,
993 (unsigned int) frame->sample_rate);
994 }
995
996 int frames = frame->data_len / (frame->bps * frame->ch_count);
997 rc = write_samples(s->handle, frame->data, frame->bps, frame->ch_count, frames, s->non_interleaved, s->playback_mode, s->scratchpad);
998 if (rc == -EPIPE) {
999 /* EPIPE means underrun */
1000 log_msg(LOG_LEVEL_WARNING, MOD_NAME "underrun occurred\n");
1001 snd_pcm_prepare(s->handle);
1002 /* fill the stream with some sasmples */
1003 for (snd_pcm_uframes_t f = 0; f < s->buffer_size; f += frames) {
1004 int frames_to_write = frames;
1005 if (f + frames > s->buffer_size) {
1006 frames_to_write = s->buffer_size - frames;
1007 }
1008 int rc = write_samples(s->handle, frame->data, frame->bps, frame->ch_count, frames_to_write, s->non_interleaved, s->playback_mode, s->scratchpad);
1009 if(rc < 0) {
1010 log_msg(LOG_LEVEL_WARNING, MOD_NAME "error from writei: %s\n",
1011 snd_strerror(rc));
1012 break;
1013 }
1014 }
1015 } else if (rc < 0) {
1016 log_msg(LOG_LEVEL_WARNING, MOD_NAME "error from writei: %s\n",
1017 snd_strerror(rc));
1018 if (rc == -ENODEV) { // someone pulled the device (eg. videoconferencing
1019 // microphone) out - taking that as a fatal error
1020 log_msg(LOG_LEVEL_FATAL, MOD_NAME "Device removed, exiting!\n");
1022 }
1023 snd_pcm_recover(s->handle, rc, 0);
1024 } else if (rc != (int)frames) {
1025 log_msg(LOG_LEVEL_WARNING, MOD_NAME "short write, written %d frames (overrun)\n", rc);
1026 }
1027}
#define LOG_LEVEL_DEBUG2
Definition debug.h:67
#define LOG_LEVEL_FATAL
errors that prevent UG run
Definition debug.h:58
void exit_uv(int status)
Definition host.cpp:625
#define EXIT_FAIL_AUDIO
Definition host.h:70
volatile int log_level
Definition debug.cpp:65
static int write_samples(snd_pcm_t *handle, const char *data, int bps, int ch_count, int frames, bool noninterleaved, playback_mode_t playback_mode, char *tmp_buffer)
Definition alsa.c:946

References state_alsa_playback::buffer_size, EXIT_FAIL_AUDIO, exit_uv(), frame, state_alsa_playback::handle, log_level, LOG_LEVEL_DEBUG2, LOG_LEVEL_FATAL, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, state_alsa_playback::non_interleaved, state_alsa_playback::playback_mode, state_alsa_playback::scratchpad, state, and write_samples().

Referenced by alsa_play_async_callback(), audio_play_alsa_put_frame(), worker(), and write_fill().

Here is the call graph for this function:

◆ get_sched_latency_ns()

static long get_sched_latency_ns ( void )
static
Todo
the file /proc/sys/kernel/sched_latency_ns is no longer present in current Linuxes - remove the function?

Definition at line 156 of file alsa.c.

157{
158 const char *proc_file = "/proc/sys/kernel/sched_latency_ns";
159
160 int fd = open(proc_file, O_RDONLY);
161 if (fd == -1) {
162 return -1;
163 }
164
165 char buf[11] = ""; // 9 digits + LF + zero
166 unsigned int idx = 0;
167 while (idx < sizeof buf - 1) {
168 ssize_t bytes = read(fd, buf + idx, sizeof buf - 1 - idx);
169 if (bytes < 0) {
170 log_msg(LOG_LEVEL_INFO, "read %s: %s\n", proc_file, strerror(errno));
171 close(fd);
172 return -1;
173 }
174 if (bytes == 0) {
175 break;
176 }
177 idx += bytes;
178 }
179
180 close(fd);
181
182 // more than 9 digits (is unlikely)
183 if (idx == sizeof buf - 1) {
184 return -1;
185 }
186
187 return atol(buf);
188}

References LOG_LEVEL_INFO, and log_msg().

Referenced by audio_play_alsa_init().

Here is the call graph for this function:

◆ init_local_config_with_workaround()

static snd_config_t * init_local_config_with_workaround ( char const * pcm_node_name)
static

Definition at line 770 of file alsa.c.

771{
772 int r;
773 snd_config_t * lconf;
774 snd_config_t * device_node;
775 snd_config_t * type_node;
776 snd_config_t * node;
777 char const * type_string;
778
779 lconf = NULL;
780
781 if (snd_config == NULL) {
782 snd_config_update();
783 }
784
785 r = snd_config_copy(&lconf, snd_config);
786 assert(r >= 0);
787
788 r = snd_config_search(lconf, pcm_node_name, &device_node);
789 if (r != 0) {
790 snd_config_delete(lconf);
791 return NULL;
792 }
793
794 /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
795 r = snd_config_search(device_node, "type", &type_node);
796 if (r != 0) {
797 snd_config_delete(lconf);
798 return NULL;
799 }
800
801 r = snd_config_get_string(type_node, &type_string);
802 if (r != 0) {
803 snd_config_delete(lconf);
804 return NULL;
805 }
806
807 if (strcmp(type_string, "pulse") != 0) {
808 snd_config_delete(lconf);
809 return NULL;
810 }
811
812 /* Don't clobber an explicit existing handle_underrun value, set it only
813 if it doesn't already exist. */
814 r = snd_config_search(device_node, "handle_underrun", &node);
815 if (r != -ENOENT) {
816 snd_config_delete(lconf);
817 return NULL;
818 }
819
820 r = snd_config_imake_integer(&node, "handle_underrun", 0);
821 if (r != 0) {
822 snd_config_delete(lconf);
823 return NULL;
824 }
825
826 r = snd_config_add(device_node, node);
827 if (r != 0) {
828 snd_config_delete(lconf);
829 return NULL;
830 }
831
832 return lconf;
833}

Referenced by audio_play_alsa_init().

◆ is_default_pulse()

static bool is_default_pulse ( void )
static

Definition at line 722 of file alsa.c.

723{
724 void **hints;
725 bool default_pulse = false;
726 bool pulse_present = false;
727
728 snd_device_name_hint(-1, "pcm", &hints);
729 while(*hints != NULL) {
730 char *tmp = strdup(*(char **) hints);
731 assert(tmp != NULL);
732 char *save_ptr = NULL;
733 char *name_part = NULL;
734 char *desc = NULL;
735
736 assert(strlen(tmp) >= 4);
737 name_part = strtok_r(tmp + 4, "|", &save_ptr);
738 if (name_part == NULL) {
739 free(tmp);
740 continue;
741 }
742 desc = strtok_r(NULL, "|", &save_ptr);
743
744 if (strcmp(name_part, "default") == 0) {
745 if (desc && strstr(desc, "PulseAudio")) {
746 default_pulse = true;
747 }
748 }
749
750 if (strcmp(name_part, "pulse") == 0) {
751 pulse_present = true;
752 }
753
754 hints++;
755 free(tmp);
756 }
757
758 return default_pulse && pulse_present;
759}

References desc.

Referenced by audio_play_alsa_init().

◆ REGISTER_MODULE()

REGISTER_MODULE ( alsa ,
& aplay_alsa_info,
LIBRARY_CLASS_AUDIO_PLAYBACK ,
AUDIO_PLAYBACK_ABI_VERSION  )

◆ set_device_buffer()

static void set_device_buffer ( snd_pcm_t * handle,
playback_mode_t playback_mode,
snd_pcm_hw_params_t * params )
static

Definition at line 445 of file alsa.c.

447{
448 enum {
449 REC_MIN_BUF_US = 5000,
450 };
451 unsigned int buf_len_us = 0;
452 int buf_dir = -1;
453 const char *buff_param = get_commandline_param("alsa-playback-buffer");
454
455 if (get_commandline_param("low-latency-audio") != NULL &&
456 buff_param == NULL) {
457 // set minimal value from the configuration space
458 CHECK_OK(snd_pcm_hw_params_set_buffer_time_first(
459 handle, params, &buf_len_us, &buf_dir));
460 MSG(INFO, "ALSA driver buffer len set to: %lf ms\n",
461 US_TO_MS((double) buf_len_us));
462 if (buf_len_us <= REC_MIN_BUF_US) {
463 MSG(WARNING,
464 "ALSA driver buffer len less than %d usec seem to "
465 "be too loow, consider using alsa-playback-buffer "
466 "instead of low-latency-audio.",
467 REC_MIN_BUF_US);
468 }
469 return;
470 }
471
472 buf_len_us = buff_param != NULL ? atoi(buff_param)
473 : MS_TO_US(playback_mode == SYNC
476
477 const int rc = snd_pcm_hw_params_set_buffer_time_near(
478 handle, params, &buf_len_us, &buf_dir);
479 if (rc < 0) {
480 MSG(WARNING,
481 "Warning - unable to set buffer to its size %u us: %s\n",
482 buf_len_us, snd_strerror(rc));
483 }
484 MSG(INFO, "ALSA driver buffer len set to: %lf ms\n",
485 US_TO_MS((double) buf_len_us));
486}
#define MSG(l, fmt,...)
Definition debug.h:96
#define BUF_LEN_DEFAULT_MS
Definition alsa.c:72
#define BUF_LEN_DEFAULT_SYNC_MS
Definition alsa.c:73
#define MS_TO_US(val_ms)
Definition tv.h:97
#define US_TO_MS(val_us)
Definition tv.h:96

References BUF_LEN_DEFAULT_MS, BUF_LEN_DEFAULT_SYNC_MS, CHECK_OK, get_commandline_param(), MS_TO_US, MSG, SYNC, and US_TO_MS.

Referenced by audio_play_alsa_reconfigure().

Here is the call graph for this function:

◆ worker()

static void * worker ( void * args)
static

Definition at line 190 of file alsa.c.

190 {
191 struct state_alsa_playback *s = args;
192
193 struct audio_frame f = { .bps = s->desc.bps,
194 .sample_rate = s->desc.sample_rate,
195 .ch_count = s->desc.ch_count };
196
197 size_t len = f.bps * f.ch_count * (f.sample_rate * s->sched_latency_ms / 1000);
198 char *data = alloca(len);
199
200#ifdef USE_SPEEX_JITTER_BUFFER
201 const int pkt_max_len = s->desc.sample_rate / 10;
202 JitterBufferPacket pkt;
203 pkt.len = pkt_max_len;
204 pkt.data = alloca(pkt_max_len);
205#endif
206 while (1) {
207 pthread_mutex_lock(&s->lock);
208 if (s->should_exit_thread) {
209 pthread_mutex_unlock(&s->lock);
210 return NULL;
211 }
212
213#ifdef USE_SPEEX_JITTER_BUFFER
214 int start_offset;
215 int err = jitter_buffer_get(s->buf, &pkt, len, &start_offset);
216 jitter_buffer_tick(s->buf);
217#else
218 int ret = audio_buffer_read(s->buf, data, len);
219#endif
220 pthread_mutex_unlock(&s->lock);
221
222#ifdef USE_SPEEX_JITTER_BUFFER
223 if (err == JITTER_BUFFER_OK) {
224 f.data_len = pkt.len;
225 f.data = pkt.data;
226 } else {
227 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "empty buffer\n");
228 if (err < 0) {
229 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Jitter buffer: %s\n",
230 JITTER_BUFFER_INTERNAL_ERROR ? "internal error" :
231 "invalid argument\n");
232 }
233 memset(data, 0, len);
234 f.data_len = len;
235 f.data = data;
236 }
237 pkt.len = pkt_max_len;
238#else
239 struct timeval now;
240 gettimeofday(&now, NULL);
241
242 if (ret > 0) {
243 f.data_len = ret;
244 f.data = data;
245 s->last_audio_read = now;
246 } else {
247 memset(data, 0, len);
248 f.data_len = len;
249 f.data = data;
250
251 if (tv_diff(now, s->last_audio_read) < 2.0) {
252 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "empty buffer\n");
253 }
254 }
255#endif
256
258 }
259}
int sample_rate
Definition types.h:101
struct timeval last_audio_read
Definition alsa.c:139
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97

References audio_buffer_read(), audio_play_alsa_write_frame(), audio_desc::bps, audio_frame::bps, state_alsa_playback::buf, audio_desc::ch_count, audio_frame::ch_count, audio_frame::data, audio_frame::data_len, state_alsa_playback::desc, state_alsa_playback::last_audio_read, len, state_alsa_playback::lock, LOG_LEVEL_VERBOSE, LOG_LEVEL_WARNING, log_msg(), MOD_NAME, audio_desc::sample_rate, audio_frame::sample_rate, state_alsa_playback::sched_latency_ms, state_alsa_playback::should_exit_thread, and tv_diff().

Referenced by audio_play_alsa_reconfigure(), display_conference_run(), extract_port(), move_port_to_worker(), recompress_done(), recompress_get_num_active_ports(), recompress_process_async(), stop_grab_workers(), wait_for_frames(), and worker_get_num_active_ports().

Here is the call graph for this function:

◆ write_fill()

static void write_fill ( struct state_alsa_playback * s)
static

Definition at line 428 of file alsa.c.

428 {
429 struct audio_frame f = { .bps = s->desc.bps,
430 .sample_rate = s->desc.sample_rate,
431 .ch_count = s->desc.ch_count };
432
433 size_t len = f.bps * f.ch_count * s->buffer_size;
434 f.data = calloc(1, len);
435 f.data_len = len;
436
438
439 free(f.data);
440}

References audio_play_alsa_write_frame(), audio_desc::bps, audio_frame::bps, state_alsa_playback::buffer_size, audio_desc::ch_count, audio_frame::ch_count, audio_frame::data, audio_frame::data_len, state_alsa_playback::desc, len, and audio_desc::sample_rate.

Referenced by audio_play_alsa_put_frame(), and audio_play_alsa_reconfigure().

Here is the call graph for this function:

◆ write_samples()

static int write_samples ( snd_pcm_t * handle,
const char * data,
int bps,
int ch_count,
int frames,
bool noninterleaved,
playback_mode_t playback_mode,
char * tmp_buffer )
static

Definition at line 946 of file alsa.c.

946 {
947 char *write_ptr[ch_count]; // for non-interleaved
948 long data_len = bps * ch_count * frames;
949 if (noninterleaved) {
950 assert(frames * bps * ch_count < SCRATCHPAD_SIZE);
951 interleaved2noninterleaved(tmp_buffer, data, bps, data_len, ch_count);
952 data = tmp_buffer;
953 }
954 if (bps == 1) { // convert to unsigned
955 assert(frames * bps * ch_count < SCRATCHPAD_SIZE);
956 signed2unsigned(tmp_buffer, data, data_len); // data may already equal tmp_buffer if noninterleaved, but np, s2u can work in situ
957 data = tmp_buffer;
958 }
959
960 int written = 0;
961 while (written < frames) {
962 int rc;
963 if (noninterleaved) {
964 for (int i = 0; i < ch_count; ++i) {
965 write_ptr[i] = tmp_buffer + frames * bps * i + written * bps;
966 }
967 rc = snd_pcm_writen(handle, (void **) &write_ptr, frames);
968 } else {
969 rc = snd_pcm_writei(handle, data, frames - written);
970 }
971
972 if (rc < 0) {
973 return rc;
974 }
975 if (playback_mode == SYNC) { // overrun (in non-blocking mode) or signal
976 return rc;
977 }
978 written += rc;
979 data += written * bps * ch_count;
980 }
981 return written;
982}
void signed2unsigned(char *out, const char *in, int in_len)
Definition utils.cpp:515
void interleaved2noninterleaved(char *out, const char *in, int bps, int in_len, int channel_count)
Definition utils.cpp:610

References audio_desc::bps, audio_desc::ch_count, interleaved2noninterleaved(), SCRATCHPAD_SIZE, signed2unsigned(), and SYNC.

Referenced by audio_play_alsa_write_frame().

Here is the call graph for this function:

Variable Documentation

◆ aplay_alsa_info

const struct audio_playback_info aplay_alsa_info
static
Initial value:
= {
}
static void audio_play_alsa_done(void *state)
Definition alsa.c:1064
static void * audio_play_alsa_init(const char *cfg)
Definition alsa.c:837
static void audio_play_alsa_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition alsa.c:697
static void audio_play_alsa_put_frame(void *state, const struct audio_frame *frame)
Definition alsa.c:1029

Definition at line 1098 of file alsa.c.