mirror of https://github.com/mpv-player/mpv
cVS: ---------------------------------------------------------------------
added get/set_sound controls stripped down initializing changed play() again outsourced xrun-handling added some cases to get_space() as xrun-handling added nonblock-mode added mmap-mode added subopts for mmap and noblock called mmap and noblock could be accessed as -ao alsa9:noblock:mmap git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@6634 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
parent
87de548153
commit
a678a1018a
|
@ -12,8 +12,8 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
//#include <unistd.h>
|
#include <math.h>
|
||||||
//#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
|
@ -48,24 +48,27 @@ static snd_pcm_hw_params_t *alsa_hwparams;
|
||||||
static snd_pcm_sw_params_t *alsa_swparams;
|
static snd_pcm_sw_params_t *alsa_swparams;
|
||||||
static char *alsa_device;
|
static char *alsa_device;
|
||||||
|
|
||||||
static int alsa_fragsize = OUTBURST; /* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h but now its not used cause chunksize is allocated dynamically. */
|
/* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h */
|
||||||
|
static int alsa_fragsize = 4096; //OUTBURST
|
||||||
static int alsa_fragcount = 8;
|
static int alsa_fragcount = 8;
|
||||||
|
|
||||||
static int chunk_size = -1;
|
static int chunk_size = -1;
|
||||||
static int buffer_size = 0;
|
|
||||||
static int start_delay = 0;
|
|
||||||
static int stop_delay = 0;
|
|
||||||
static size_t bits_per_sample, bits_per_frame;
|
static size_t bits_per_sample, bits_per_frame;
|
||||||
static size_t chunk_bytes;
|
static size_t chunk_bytes;
|
||||||
|
|
||||||
|
int ao_mmap = 0;
|
||||||
|
int ao_noblock = 0;
|
||||||
|
|
||||||
|
static int open_mode;
|
||||||
|
static int set_block_mode;
|
||||||
|
|
||||||
#define ALSA_DEVICE_SIZE 48
|
#define ALSA_DEVICE_SIZE 48
|
||||||
|
|
||||||
#define BUFFERTIME /* last undef */
|
#undef BUFFERTIME
|
||||||
#undef SET_PERIOD /* only function now is to set chunksize staticaly, last defined */
|
#define SET_CHUNKSIZE
|
||||||
#define SW_PARAMS /* last undef */
|
|
||||||
|
|
||||||
|
|
||||||
snd_pcm_t *
|
snd_pcm_t *
|
||||||
|
|
||||||
spdif_init(int acard, int adevice)
|
spdif_init(int acard, int adevice)
|
||||||
{
|
{
|
||||||
//char *pcm_name = "hw:0,2"; /* first card second device */
|
//char *pcm_name = "hw:0,2"; /* first card second device */
|
||||||
|
@ -149,8 +152,8 @@ spdif_init(int acard, int adevice)
|
||||||
fprintf(stderr, "Broken configuration for this PCM: no configurations available");
|
fprintf(stderr, "Broken configuration for this PCM: no configurations available");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
err = snd_pcm_hw_params_set_access(handler, params,
|
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
err = snd_pcm_hw_params_set_access(handler, params,SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
fprintf(stderr, "Access tyep not available");
|
fprintf(stderr, "Access tyep not available");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -186,6 +189,104 @@ spdif_init(int acard, int adevice)
|
||||||
/* to set/get/query special features/parameters */
|
/* to set/get/query special features/parameters */
|
||||||
static int control(int cmd, int arg)
|
static int control(int cmd, int arg)
|
||||||
{
|
{
|
||||||
|
switch(cmd) {
|
||||||
|
case AOCONTROL_QUERY_FORMAT:
|
||||||
|
return CONTROL_TRUE;
|
||||||
|
case AOCONTROL_GET_VOLUME:
|
||||||
|
case AOCONTROL_SET_VOLUME:
|
||||||
|
{
|
||||||
|
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
snd_mixer_t *handle;
|
||||||
|
snd_mixer_elem_t *elem;
|
||||||
|
snd_mixer_selem_id_t *sid;
|
||||||
|
|
||||||
|
const char *mix_name = "PCM";
|
||||||
|
char *card = "default";
|
||||||
|
|
||||||
|
long pmin, pmax;
|
||||||
|
long get_vol, set_vol;
|
||||||
|
float calc_vol, diff, f_multi;
|
||||||
|
|
||||||
|
if(ao_data.format == AFMT_AC3)
|
||||||
|
return CONTROL_TRUE;
|
||||||
|
|
||||||
|
//allocate simple id
|
||||||
|
snd_mixer_selem_id_alloca(&sid);
|
||||||
|
|
||||||
|
//sets simple-mixer index and name
|
||||||
|
snd_mixer_selem_id_set_index(sid, 0);
|
||||||
|
snd_mixer_selem_id_set_name(sid, mix_name);
|
||||||
|
|
||||||
|
if ((err = snd_mixer_open(&handle, 0)) < 0) {
|
||||||
|
printf("alsa-control: mixer open error: %s\n", snd_strerror(err));
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_mixer_attach(handle, card)) < 0) {
|
||||||
|
printf("alsa-control: mixer attach %s error: %s", card, snd_strerror(err));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
|
||||||
|
printf("alsa-control: mixer register error: %s", snd_strerror(err));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
err = snd_mixer_load(handle);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("alsa-control: mixer load error: %s", snd_strerror(err));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = snd_mixer_find_selem(handle, sid);
|
||||||
|
if (!elem) {
|
||||||
|
printf("alsa-control: unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
|
||||||
|
f_multi = (100 / (float)pmax);
|
||||||
|
|
||||||
|
if (cmd == AOCONTROL_SET_VOLUME) {
|
||||||
|
|
||||||
|
diff = (vol->left+vol->right) / 2;
|
||||||
|
set_vol = rint(diff / f_multi);
|
||||||
|
|
||||||
|
if (set_vol < 0)
|
||||||
|
set_vol = 0;
|
||||||
|
else if (set_vol > pmax)
|
||||||
|
set_vol = pmax;
|
||||||
|
|
||||||
|
//setting channels
|
||||||
|
if ((err = snd_mixer_selem_set_playback_volume(elem, 0, set_vol)) < 0) {
|
||||||
|
printf("alsa-control: error setting left channel, %s",snd_strerror(err));
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
if ((err = snd_mixer_selem_set_playback_volume(elem, 1, set_vol)) < 0) {
|
||||||
|
printf("alsa-control: error setting right channel, %s",snd_strerror(err));
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("diff=%f, set_vol=%i, pmax=%i, mult=%f\n", diff, set_vol, pmax, f_multi);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snd_mixer_selem_get_playback_volume(elem, 0, &get_vol);
|
||||||
|
calc_vol = get_vol;
|
||||||
|
calc_vol = rintf(calc_vol * f_multi);
|
||||||
|
|
||||||
|
vol->left = vol->right = (int)calc_vol;
|
||||||
|
|
||||||
|
//printf("get_vol = %i, calc=%i\n",get_vol, calc_vol);
|
||||||
|
}
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
return(CONTROL_UNKNOWN);
|
return(CONTROL_UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,10 +299,9 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
int cards = -1;
|
int cards = -1;
|
||||||
|
int period_val;
|
||||||
snd_pcm_info_t *alsa_info;
|
snd_pcm_info_t *alsa_info;
|
||||||
|
char *str_block_mode;
|
||||||
size_t xfer_align; //new
|
|
||||||
snd_pcm_uframes_t start_threshold, stop_threshold; //new
|
|
||||||
|
|
||||||
printf("alsa-init: testing and bugreports are welcome.\n");
|
printf("alsa-init: testing and bugreports are welcome.\n");
|
||||||
printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz,
|
printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz,
|
||||||
|
@ -223,7 +323,7 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
ao_data.format = format;
|
ao_data.format = format;
|
||||||
ao_data.channels = channels;
|
ao_data.channels = channels;
|
||||||
ao_data.outburst = OUTBURST;
|
ao_data.outburst = OUTBURST;
|
||||||
ao_data.buffersize = 16384;
|
ao_data.buffersize = MAX_OUTBURST; // was 16384
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
|
@ -270,8 +370,46 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ao_subdevice != NULL) // ?? makes no sense
|
if (ao_subdevice) {
|
||||||
alsa_device = ao_subdevice;
|
//start parsing ao_subdevice, ugly and not thread safe!
|
||||||
|
//maybe there's a better way?
|
||||||
|
int i2 = 1;
|
||||||
|
int i3 = 0;
|
||||||
|
char *sub_str;
|
||||||
|
|
||||||
|
char *token_str[3];
|
||||||
|
char* test_str = strdup(ao_subdevice);
|
||||||
|
|
||||||
|
//printf("subd=%s, test=%s\n", ao_subdevice,test_str);
|
||||||
|
|
||||||
|
if ((strcspn(ao_subdevice, ":")) > 0) {
|
||||||
|
|
||||||
|
sub_str = strtok(test_str, ":");
|
||||||
|
*(token_str) = sub_str;
|
||||||
|
|
||||||
|
while (((sub_str = strtok(NULL, ":")) != NULL) && (i2 <= 3)) {
|
||||||
|
*(token_str+i2) = sub_str;
|
||||||
|
//printf("token %i: %s\n", i2, *(token_str+i2));
|
||||||
|
i2 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i3=0; i3 <= i2-1; i3++) {
|
||||||
|
//printf("test %i, %s\n", i3, *(token_str+i3));
|
||||||
|
if (strcmp(*(token_str + i3), "mmap") == 0) {
|
||||||
|
ao_mmap = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(*(token_str+i3), "noblock") == 0) {
|
||||||
|
ao_noblock = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(*(token_str+i3), "hw") == 0) {
|
||||||
|
alsa_device = *(token_str+i3);
|
||||||
|
}
|
||||||
|
else if (!alsa_device || !ao_mmap || !ao_noblock) {
|
||||||
|
alsa_device = *(token_str+i3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //end parsing ao_subdevice
|
||||||
|
|
||||||
if (alsa_device == NULL)
|
if (alsa_device == NULL)
|
||||||
{
|
{
|
||||||
|
@ -320,13 +458,45 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
alsa_handler = spdif_init(0, 2);
|
alsa_handler = spdif_init(0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//setting modes for block or nonblock-mode
|
||||||
|
if (ao_noblock) {
|
||||||
|
open_mode = SND_PCM_NONBLOCK;
|
||||||
|
set_block_mode = 1;
|
||||||
|
str_block_mode = "nonblock-mode";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
open_mode = 0;
|
||||||
|
set_block_mode = 0;
|
||||||
|
str_block_mode = "block-mode";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
|
||||||
if (!alsa_handler) {
|
if (!alsa_handler) {
|
||||||
if ((err = snd_pcm_open(&alsa_handler,alsa_device,SND_PCM_STREAM_PLAYBACK,0)) < 0)
|
if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, open_mode)) < 0)
|
||||||
{
|
{
|
||||||
|
if (ao_noblock) {
|
||||||
|
printf("alsa-init: open in nonblock-mode failed, trying to open in block-mode\n");
|
||||||
|
if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
||||||
|
printf("alsa-init: playback open error: %s\n", snd_strerror(err));
|
||||||
|
return(0);
|
||||||
|
} else {
|
||||||
|
set_block_mode = 0;
|
||||||
|
str_block_mode = "block-mode";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
printf("alsa-init: playback open error: %s\n", snd_strerror(err));
|
printf("alsa-init: playback open error: %s\n", snd_strerror(err));
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_nonblock(alsa_handler, set_block_mode)) < 0) {
|
||||||
|
printf("alsa-init: error set block-mode %s\n", snd_strerror(err));
|
||||||
|
}
|
||||||
|
else if (verbose) {
|
||||||
|
printf("alsa-init: pcm opend in %s\n", str_block_mode);
|
||||||
|
}
|
||||||
|
|
||||||
snd_pcm_hw_params_alloca(&alsa_hwparams);
|
snd_pcm_hw_params_alloca(&alsa_hwparams);
|
||||||
snd_pcm_sw_params_alloca(&alsa_swparams);
|
snd_pcm_sw_params_alloca(&alsa_swparams);
|
||||||
|
@ -339,12 +509,21 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,
|
if (ao_mmap) {
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof());
|
||||||
{
|
snd_pcm_access_mask_none(mask);
|
||||||
printf("alsa-init: unable to set access type: %s\n",
|
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
||||||
snd_strerror(err));
|
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
||||||
return(0);
|
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
|
||||||
|
err = snd_pcm_hw_params_set_access_mask(alsa_handler, alsa_hwparams, mask);
|
||||||
|
printf("alsa-init: mmap set\n");
|
||||||
|
} else {
|
||||||
|
err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
|
printf("alsa-init: interleave set\n");
|
||||||
|
}
|
||||||
|
if (err < 0) {
|
||||||
|
printf("alsa-init: unable to set access type: %s\n", snd_strerror(err));
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
|
if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
|
||||||
|
@ -367,21 +546,9 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to set samplerate-2: %s\n",
|
printf("alsa-init: unable to set samplerate-2: %s\n",
|
||||||
snd_strerror(err));
|
snd_strerror(err));
|
||||||
//snd_pcm_hw_params_dump(alsa_hwparams, errlog); jp
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef SET_PERIOD
|
|
||||||
{
|
|
||||||
if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, alsa_fragsize, 0)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BUFFERTIME
|
#ifdef BUFFERTIME
|
||||||
{
|
{
|
||||||
int alsa_buffer_time = 500000; /* original 60 */
|
int alsa_buffer_time = 500000; /* original 60 */
|
||||||
|
@ -406,36 +573,33 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* get chunk-size */
|
#ifdef SET_CHUNKSIZE
|
||||||
if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, 0)) < 0)
|
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to get chunk-size in hw-params: %s\n", snd_strerror(err));
|
//set chunksize
|
||||||
return(0);
|
chunk_size = alsa_fragsize / 4;
|
||||||
} else
|
|
||||||
{
|
|
||||||
chunk_size = err;
|
|
||||||
if (verbose) {printf("alsa-init: got chunksize %i\n", chunk_size);}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get buffer size */
|
if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, chunk_size, 0)) < 0)
|
||||||
if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0)
|
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to get buffersize in hw-params: %s\n", snd_strerror(err));
|
printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err));
|
||||||
return(0);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
ao_data.buffersize = err;
|
|
||||||
if (verbose) {printf("alsa-init: got buffersize %i\n", ao_data.buffersize);}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MAX_OUTBURST > ao_data.buffersize) { //warning if MAX_OUTBURST is bigger than buffersize
|
|
||||||
printf("alsa-init: WARNING! MAX_OUTBURST exceeds your available buffersize.\nalsa-init: MAX_OUTBURST=%i, buffersize=%i\n",MAX_OUTBURST,ao_data.buffersize);}
|
|
||||||
|
|
||||||
if (chunk_size == ao_data.buffersize)
|
|
||||||
{
|
|
||||||
printf("alsa-init: Can't use period equal to buffer size (%u == %lu)", chunk_size, (long)buffer_size);
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
else if (verbose) {
|
||||||
|
printf("alsa-init: chunksize set to %i\n", chunk_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set period_count
|
||||||
|
if ((period_val = snd_pcm_hw_params_get_periods_max(alsa_hwparams, 0)) < alsa_fragcount) {
|
||||||
|
alsa_fragcount = period_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
printf("alsa-init: current val=%i, fragcount=%i\n", period_val, alsa_fragcount);
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_periods(alsa_handler, alsa_hwparams, alsa_fragcount, 0)) < 0) {
|
||||||
|
printf("alsa-init: unable to set periods: %s\n", snd_strerror(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* finally install hardware parameters */
|
/* finally install hardware parameters */
|
||||||
if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
|
if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
|
||||||
|
@ -446,34 +610,22 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
}
|
}
|
||||||
// end setting hw-params
|
// end setting hw-params
|
||||||
|
|
||||||
#ifdef SW_PARAMS
|
|
||||||
|
// gets buffersize for control
|
||||||
|
if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0)
|
||||||
{
|
{
|
||||||
size_t n;
|
printf("alsa-init: unable to get buffersize: %s\n", snd_strerror(err));
|
||||||
xfer_align = snd_pcm_sw_params_get_xfer_align(alsa_swparams);
|
return(0);
|
||||||
if (xfer_align == 0)
|
|
||||||
xfer_align = 4;
|
|
||||||
n = (ao_data.buffersize / xfer_align) * xfer_align;
|
|
||||||
|
|
||||||
if (start_delay <= 0) {
|
|
||||||
start_threshold = n + (double) ao_data.samplerate * start_delay / 1000000;
|
|
||||||
} else {
|
|
||||||
start_threshold = (double) ao_data.samplerate * start_delay / 1000000;
|
|
||||||
}
|
}
|
||||||
if (start_threshold < 1)
|
else {
|
||||||
start_threshold = 1;
|
ao_data.buffersize = err;
|
||||||
if (start_threshold > n)
|
if (verbose)
|
||||||
start_threshold = n;
|
printf("alsa-init: got buffersize=%i\n", ao_data.buffersize);
|
||||||
|
|
||||||
if (stop_delay <= 0) {
|
|
||||||
stop_threshold = ao_data.buffersize + (double) ao_data.samplerate * stop_delay / 1000000;
|
|
||||||
} else {
|
|
||||||
stop_threshold = (double) ao_data.samplerate * stop_delay / 1000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
// setting sw-params (only avail-min) if noblocking mode was choosed
|
||||||
printf("alsa-init: start_threshold=%lu, stop_threshold=%lu\n",start_threshold,stop_threshold);
|
if (ao_noblock)
|
||||||
printf("alsa-init: n=%i\n", n);
|
{
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0)
|
if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -482,31 +634,13 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
//set min available frames to consider pcm ready (4)
|
//set min available frames to consider pcm ready (4)
|
||||||
if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, 4)) < 0)
|
//increased for nonblock-mode should be set dynamically later
|
||||||
|
if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, chunk_size)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err));
|
printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err));
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, start_threshold)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set start_threshold %s\n",snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, stop_threshold)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set stop_threshold %s\n",snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//transfers stream aligned to 4 in nonblocking-mode it would be 1
|
|
||||||
if ((err = snd_pcm_sw_params_set_xfer_align(alsa_handler, alsa_swparams, 4)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set xfer_align: %s\n",snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0)
|
if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to install sw-params\n");
|
printf("alsa-init: unable to install sw-params\n");
|
||||||
|
@ -519,9 +653,8 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
printf("alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);}
|
printf("alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);}
|
||||||
}
|
|
||||||
|
|
||||||
#endif //end swparams
|
}//end swparams
|
||||||
|
|
||||||
if ((err = snd_pcm_prepare(alsa_handler)) < 0)
|
if ((err = snd_pcm_prepare(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -539,18 +672,17 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
/* close audio device */
|
/* close audio device */
|
||||||
static void uninit()
|
static void uninit()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (alsa_handler) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
//if (alsa_device != NULL)
|
if (!ao_noblock) {
|
||||||
//free(alsa_device);
|
|
||||||
|
|
||||||
//snd_pcm_hw_params_free(alsa_hwparams);
|
|
||||||
|
|
||||||
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err));
|
printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_close(alsa_handler)) < 0)
|
if ((err = snd_pcm_close(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -562,25 +694,30 @@ static void uninit()
|
||||||
alsa_device = NULL;
|
alsa_device = NULL;
|
||||||
printf("alsa-uninit: pcm closed\n");
|
printf("alsa-uninit: pcm closed\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("alsa-uninit: no handler defined!\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_pause()
|
static void audio_pause()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (!ao_noblock) {
|
||||||
|
//drain causes error in nonblock-mode!
|
||||||
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err));
|
printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (verbose)
|
||||||
|
printf("alsa-pause: paused nonblock\n");
|
||||||
|
|
||||||
#ifdef reset
|
|
||||||
if ((err = snd_pcm_reset(alsa_handler)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-pause: pcm reset error: %s\n", snd_strerror(err));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_resume()
|
static void audio_resume()
|
||||||
|
@ -599,9 +736,11 @@ static void reset()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (!ao_noblock) {
|
||||||
|
//drain causes error in nonblock-mode!
|
||||||
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-reset: pcm drain error: %s\n", snd_strerror(err));
|
printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +749,11 @@ static void reset()
|
||||||
printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err));
|
printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (verbose)
|
||||||
|
printf("alsa-reset: reset nonblocked");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef timersub
|
#ifndef timersub
|
||||||
|
@ -625,61 +768,96 @@ do { \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* I/O error handler */
|
||||||
|
static int xrun(u_char *str_mode)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
snd_pcm_status_t *status;
|
||||||
|
|
||||||
|
snd_pcm_status_alloca(&status);
|
||||||
|
|
||||||
|
if ((err = snd_pcm_status(alsa_handler, status))<0) {
|
||||||
|
printf("status error: %s", snd_strerror(err));
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
|
||||||
|
struct timeval now, diff, tstamp;
|
||||||
|
gettimeofday(&now, 0);
|
||||||
|
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
|
||||||
|
timersub(&now, &tstamp, &diff);
|
||||||
|
printf("alsa-%s: xrun of at least %.3f msecs. resetting stream\n",
|
||||||
|
str_mode,
|
||||||
|
diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_prepare(alsa_handler))<0) {
|
||||||
|
printf("xrun: prepare error: %s", snd_strerror(err));
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(1); /* ok, data should be accepted again */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
plays 'len' bytes of 'data'
|
plays 'len' bytes of 'data'
|
||||||
returns: number of bytes played
|
returns: number of bytes played
|
||||||
modified last at 26.06.02 by jp
|
modified last at 29.06.02 by jp
|
||||||
|
thanxs for marius <marius@rospot.com> for giving us the light ;)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int play(void* data, int len, int flags)
|
static int play(void* data, int len, int flags)
|
||||||
{
|
{
|
||||||
|
|
||||||
snd_pcm_status_t *status;
|
//ao_data.bps is always 4 cause its set to channels * 2 by alsa_format??
|
||||||
|
int num_frames = len / ao_data.bps;
|
||||||
int num_frames=len/ao_data.bps;
|
|
||||||
signed short *output_samples=data;
|
signed short *output_samples=data;
|
||||||
snd_pcm_sframes_t res = 0;
|
snd_pcm_sframes_t res = 0;
|
||||||
|
|
||||||
|
//printf("alsa-play: frames=%i, len=%i",num_frames,len);
|
||||||
|
|
||||||
if (!alsa_handler) {
|
if (!alsa_handler) {
|
||||||
printf("alsa-play: device configuration error");
|
printf("alsa-play: device configuration error");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
while (num_frames > 0) {
|
||||||
if (res == -EPIPE) { /* underrun */
|
|
||||||
snd_pcm_status_alloca(&status);
|
if (ao_mmap) {
|
||||||
if ((res = snd_pcm_status(alsa_handler, status))<0) {
|
res = snd_pcm_mmap_writei(alsa_handler, (void *)output_samples, num_frames);
|
||||||
printf("alsa-play: buffer underrun. can't determine length");
|
|
||||||
} else {
|
} else {
|
||||||
if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
|
res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
|
||||||
struct timeval now, diff, tstamp;
|
|
||||||
gettimeofday(&now, 0);
|
|
||||||
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
|
|
||||||
timersub(&now, &tstamp, &diff);
|
|
||||||
printf("alsa-play: xrun of at least %.3f msecs. resetting stream",
|
|
||||||
diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
|
|
||||||
} else
|
|
||||||
printf("alsa-play: xrun. can't determine length");
|
|
||||||
}
|
|
||||||
res = snd_pcm_prepare(alsa_handler);
|
|
||||||
}
|
|
||||||
else if (res == -ESTRPIPE) { /* suspend */
|
|
||||||
printf("alsa-play: pcm in suspend mode. trying to resume");
|
|
||||||
while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
|
|
||||||
sleep(1);
|
|
||||||
if (res < 0)
|
|
||||||
res = snd_pcm_prepare(alsa_handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res >= 0)
|
if (res == -EAGAIN) {
|
||||||
res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
|
snd_pcm_wait(alsa_handler, 1000);
|
||||||
|
}
|
||||||
|
else if (res == -EPIPE) { /* underrun */
|
||||||
|
if (xrun("play") <= 0) {
|
||||||
|
printf("alsa-play: xrun reset error");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (res == -ESTRPIPE) { /* suspend */
|
||||||
|
printf("alsa-play: pcm in suspend mode. trying to resume\n");
|
||||||
|
while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
else if (res < 0) {
|
||||||
|
printf("alsa-play: unknown status, trying to reset soundcard\n");
|
||||||
|
if ((res = snd_pcm_prepare(alsa_handler)) < 0) {
|
||||||
|
printf("alsa-play: snd prepare error");
|
||||||
|
return(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (res > 0) {
|
if (res > 0) {
|
||||||
output_samples += ao_data.channels * res;
|
output_samples += ao_data.channels * res;
|
||||||
num_frames -= res;
|
num_frames -= res;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (res == -EPIPE || num_frames > 0);
|
} //end while
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
printf("alsa-play: write error %s", snd_strerror(res));
|
printf("alsa-play: write error %s", snd_strerror(res));
|
||||||
|
@ -688,12 +866,14 @@ static int play(void* data, int len, int flags)
|
||||||
return res < 0 ? (int)res : len;
|
return res < 0 ? (int)res : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* how many byes are free in the buffer */
|
/* how many byes are free in the buffer */
|
||||||
static int get_space()
|
static int get_space()
|
||||||
{
|
{
|
||||||
snd_pcm_status_t *status;
|
snd_pcm_status_t *status;
|
||||||
int ret;
|
int ret;
|
||||||
|
char *str_status;
|
||||||
|
|
||||||
|
//snd_pcm_sframes_t avail_frames = 0;
|
||||||
|
|
||||||
if ((ret = snd_pcm_status_malloc(&status)) < 0)
|
if ((ret = snd_pcm_status_malloc(&status)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -710,25 +890,49 @@ static int get_space()
|
||||||
switch(snd_pcm_status_get_state(status))
|
switch(snd_pcm_status_get_state(status))
|
||||||
{
|
{
|
||||||
case SND_PCM_STATE_OPEN:
|
case SND_PCM_STATE_OPEN:
|
||||||
|
str_status = "open";
|
||||||
case SND_PCM_STATE_PREPARED:
|
case SND_PCM_STATE_PREPARED:
|
||||||
|
if (str_status != "open")
|
||||||
|
str_status = "prepared";
|
||||||
case SND_PCM_STATE_RUNNING:
|
case SND_PCM_STATE_RUNNING:
|
||||||
ret = snd_pcm_status_get_avail(status) * ao_data.bps;
|
ret = snd_pcm_status_get_avail(status) * ao_data.bps;
|
||||||
|
//avail_frames = snd_pcm_avail_update(alsa_handler) * ao_data.bps;
|
||||||
|
if (str_status != "open" && str_status != "prepared")
|
||||||
|
str_status = "running";
|
||||||
|
break;
|
||||||
|
case SND_PCM_STATE_PAUSED:
|
||||||
|
if (verbose) printf("alsa-space: paused");
|
||||||
|
str_status = "paused";
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case SND_PCM_STATE_XRUN:
|
||||||
|
xrun("space");
|
||||||
|
str_status = "xrun";
|
||||||
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
str_status = "undefined";
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verbose && str_status != "running")
|
||||||
|
printf("alsa-space: free space = %i, status=%i, %s --\n", ret, status, str_status);
|
||||||
snd_pcm_status_free(status);
|
snd_pcm_status_free(status);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
|
printf("negative value!!\n");
|
||||||
ret = 0;
|
ret = 0;
|
||||||
//printf("alsa-space: free space = %i",ret);
|
}
|
||||||
|
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* delay in seconds between first and last sample in buffer */
|
/* delay in seconds between first and last sample in buffer */
|
||||||
static float get_delay()
|
static float get_delay()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (alsa_handler) {
|
||||||
|
|
||||||
snd_pcm_status_t *status;
|
snd_pcm_status_t *status;
|
||||||
float ret;
|
float ret;
|
||||||
|
|
||||||
|
@ -760,4 +964,8 @@ static float get_delay()
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
return(ret);
|
return(ret);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
//#include <unistd.h>
|
#include <math.h>
|
||||||
//#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../config.h"
|
#include "../config.h"
|
||||||
|
|
||||||
|
@ -48,24 +48,27 @@ static snd_pcm_hw_params_t *alsa_hwparams;
|
||||||
static snd_pcm_sw_params_t *alsa_swparams;
|
static snd_pcm_sw_params_t *alsa_swparams;
|
||||||
static char *alsa_device;
|
static char *alsa_device;
|
||||||
|
|
||||||
static int alsa_fragsize = OUTBURST; /* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h but now its not used cause chunksize is allocated dynamically. */
|
/* possible 4096, original 8192, OUTBURST is set statically to 512 in config.h */
|
||||||
|
static int alsa_fragsize = 4096; //OUTBURST
|
||||||
static int alsa_fragcount = 8;
|
static int alsa_fragcount = 8;
|
||||||
|
|
||||||
static int chunk_size = -1;
|
static int chunk_size = -1;
|
||||||
static int buffer_size = 0;
|
|
||||||
static int start_delay = 0;
|
|
||||||
static int stop_delay = 0;
|
|
||||||
static size_t bits_per_sample, bits_per_frame;
|
static size_t bits_per_sample, bits_per_frame;
|
||||||
static size_t chunk_bytes;
|
static size_t chunk_bytes;
|
||||||
|
|
||||||
|
int ao_mmap = 0;
|
||||||
|
int ao_noblock = 0;
|
||||||
|
|
||||||
|
static int open_mode;
|
||||||
|
static int set_block_mode;
|
||||||
|
|
||||||
#define ALSA_DEVICE_SIZE 48
|
#define ALSA_DEVICE_SIZE 48
|
||||||
|
|
||||||
#define BUFFERTIME /* last undef */
|
#undef BUFFERTIME
|
||||||
#undef SET_PERIOD /* only function now is to set chunksize staticaly, last defined */
|
#define SET_CHUNKSIZE
|
||||||
#define SW_PARAMS /* last undef */
|
|
||||||
|
|
||||||
|
|
||||||
snd_pcm_t *
|
snd_pcm_t *
|
||||||
|
|
||||||
spdif_init(int acard, int adevice)
|
spdif_init(int acard, int adevice)
|
||||||
{
|
{
|
||||||
//char *pcm_name = "hw:0,2"; /* first card second device */
|
//char *pcm_name = "hw:0,2"; /* first card second device */
|
||||||
|
@ -149,8 +152,8 @@ spdif_init(int acard, int adevice)
|
||||||
fprintf(stderr, "Broken configuration for this PCM: no configurations available");
|
fprintf(stderr, "Broken configuration for this PCM: no configurations available");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
err = snd_pcm_hw_params_set_access(handler, params,
|
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED);
|
err = snd_pcm_hw_params_set_access(handler, params,SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
fprintf(stderr, "Access tyep not available");
|
fprintf(stderr, "Access tyep not available");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -186,6 +189,104 @@ spdif_init(int acard, int adevice)
|
||||||
/* to set/get/query special features/parameters */
|
/* to set/get/query special features/parameters */
|
||||||
static int control(int cmd, int arg)
|
static int control(int cmd, int arg)
|
||||||
{
|
{
|
||||||
|
switch(cmd) {
|
||||||
|
case AOCONTROL_QUERY_FORMAT:
|
||||||
|
return CONTROL_TRUE;
|
||||||
|
case AOCONTROL_GET_VOLUME:
|
||||||
|
case AOCONTROL_SET_VOLUME:
|
||||||
|
{
|
||||||
|
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
|
||||||
|
|
||||||
|
int err;
|
||||||
|
snd_mixer_t *handle;
|
||||||
|
snd_mixer_elem_t *elem;
|
||||||
|
snd_mixer_selem_id_t *sid;
|
||||||
|
|
||||||
|
const char *mix_name = "PCM";
|
||||||
|
char *card = "default";
|
||||||
|
|
||||||
|
long pmin, pmax;
|
||||||
|
long get_vol, set_vol;
|
||||||
|
float calc_vol, diff, f_multi;
|
||||||
|
|
||||||
|
if(ao_data.format == AFMT_AC3)
|
||||||
|
return CONTROL_TRUE;
|
||||||
|
|
||||||
|
//allocate simple id
|
||||||
|
snd_mixer_selem_id_alloca(&sid);
|
||||||
|
|
||||||
|
//sets simple-mixer index and name
|
||||||
|
snd_mixer_selem_id_set_index(sid, 0);
|
||||||
|
snd_mixer_selem_id_set_name(sid, mix_name);
|
||||||
|
|
||||||
|
if ((err = snd_mixer_open(&handle, 0)) < 0) {
|
||||||
|
printf("alsa-control: mixer open error: %s\n", snd_strerror(err));
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_mixer_attach(handle, card)) < 0) {
|
||||||
|
printf("alsa-control: mixer attach %s error: %s", card, snd_strerror(err));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_mixer_selem_register(handle, NULL, NULL)) < 0) {
|
||||||
|
printf("alsa-control: mixer register error: %s", snd_strerror(err));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
err = snd_mixer_load(handle);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("alsa-control: mixer load error: %s", snd_strerror(err));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
elem = snd_mixer_find_selem(handle, sid);
|
||||||
|
if (!elem) {
|
||||||
|
printf("alsa-control: unable to find simple control '%s',%i\n", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax);
|
||||||
|
f_multi = (100 / (float)pmax);
|
||||||
|
|
||||||
|
if (cmd == AOCONTROL_SET_VOLUME) {
|
||||||
|
|
||||||
|
diff = (vol->left+vol->right) / 2;
|
||||||
|
set_vol = rint(diff / f_multi);
|
||||||
|
|
||||||
|
if (set_vol < 0)
|
||||||
|
set_vol = 0;
|
||||||
|
else if (set_vol > pmax)
|
||||||
|
set_vol = pmax;
|
||||||
|
|
||||||
|
//setting channels
|
||||||
|
if ((err = snd_mixer_selem_set_playback_volume(elem, 0, set_vol)) < 0) {
|
||||||
|
printf("alsa-control: error setting left channel, %s",snd_strerror(err));
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
if ((err = snd_mixer_selem_set_playback_volume(elem, 1, set_vol)) < 0) {
|
||||||
|
printf("alsa-control: error setting right channel, %s",snd_strerror(err));
|
||||||
|
return CONTROL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("diff=%f, set_vol=%i, pmax=%i, mult=%f\n", diff, set_vol, pmax, f_multi);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snd_mixer_selem_get_playback_volume(elem, 0, &get_vol);
|
||||||
|
calc_vol = get_vol;
|
||||||
|
calc_vol = rintf(calc_vol * f_multi);
|
||||||
|
|
||||||
|
vol->left = vol->right = (int)calc_vol;
|
||||||
|
|
||||||
|
//printf("get_vol = %i, calc=%i\n",get_vol, calc_vol);
|
||||||
|
}
|
||||||
|
snd_mixer_close(handle);
|
||||||
|
return CONTROL_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
return(CONTROL_UNKNOWN);
|
return(CONTROL_UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,10 +299,9 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
int cards = -1;
|
int cards = -1;
|
||||||
|
int period_val;
|
||||||
snd_pcm_info_t *alsa_info;
|
snd_pcm_info_t *alsa_info;
|
||||||
|
char *str_block_mode;
|
||||||
size_t xfer_align; //new
|
|
||||||
snd_pcm_uframes_t start_threshold, stop_threshold; //new
|
|
||||||
|
|
||||||
printf("alsa-init: testing and bugreports are welcome.\n");
|
printf("alsa-init: testing and bugreports are welcome.\n");
|
||||||
printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz,
|
printf("alsa-init: requested format: %d Hz, %d channels, %s\n", rate_hz,
|
||||||
|
@ -223,7 +323,7 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
ao_data.format = format;
|
ao_data.format = format;
|
||||||
ao_data.channels = channels;
|
ao_data.channels = channels;
|
||||||
ao_data.outburst = OUTBURST;
|
ao_data.outburst = OUTBURST;
|
||||||
ao_data.buffersize = 16384;
|
ao_data.buffersize = MAX_OUTBURST; // was 16384
|
||||||
|
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
|
@ -270,8 +370,46 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ao_subdevice != NULL) // ?? makes no sense
|
if (ao_subdevice) {
|
||||||
alsa_device = ao_subdevice;
|
//start parsing ao_subdevice, ugly and not thread safe!
|
||||||
|
//maybe there's a better way?
|
||||||
|
int i2 = 1;
|
||||||
|
int i3 = 0;
|
||||||
|
char *sub_str;
|
||||||
|
|
||||||
|
char *token_str[3];
|
||||||
|
char* test_str = strdup(ao_subdevice);
|
||||||
|
|
||||||
|
//printf("subd=%s, test=%s\n", ao_subdevice,test_str);
|
||||||
|
|
||||||
|
if ((strcspn(ao_subdevice, ":")) > 0) {
|
||||||
|
|
||||||
|
sub_str = strtok(test_str, ":");
|
||||||
|
*(token_str) = sub_str;
|
||||||
|
|
||||||
|
while (((sub_str = strtok(NULL, ":")) != NULL) && (i2 <= 3)) {
|
||||||
|
*(token_str+i2) = sub_str;
|
||||||
|
//printf("token %i: %s\n", i2, *(token_str+i2));
|
||||||
|
i2 += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i3=0; i3 <= i2-1; i3++) {
|
||||||
|
//printf("test %i, %s\n", i3, *(token_str+i3));
|
||||||
|
if (strcmp(*(token_str + i3), "mmap") == 0) {
|
||||||
|
ao_mmap = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(*(token_str+i3), "noblock") == 0) {
|
||||||
|
ao_noblock = 1;
|
||||||
|
}
|
||||||
|
else if (strcmp(*(token_str+i3), "hw") == 0) {
|
||||||
|
alsa_device = *(token_str+i3);
|
||||||
|
}
|
||||||
|
else if (!alsa_device || !ao_mmap || !ao_noblock) {
|
||||||
|
alsa_device = *(token_str+i3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //end parsing ao_subdevice
|
||||||
|
|
||||||
if (alsa_device == NULL)
|
if (alsa_device == NULL)
|
||||||
{
|
{
|
||||||
|
@ -320,13 +458,45 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
alsa_handler = spdif_init(0, 2);
|
alsa_handler = spdif_init(0, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//setting modes for block or nonblock-mode
|
||||||
|
if (ao_noblock) {
|
||||||
|
open_mode = SND_PCM_NONBLOCK;
|
||||||
|
set_block_mode = 1;
|
||||||
|
str_block_mode = "nonblock-mode";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
open_mode = 0;
|
||||||
|
set_block_mode = 0;
|
||||||
|
str_block_mode = "block-mode";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//modes = 0, SND_PCM_NONBLOCK, SND_PCM_ASYNC
|
||||||
if (!alsa_handler) {
|
if (!alsa_handler) {
|
||||||
if ((err = snd_pcm_open(&alsa_handler,alsa_device,SND_PCM_STREAM_PLAYBACK,0)) < 0)
|
if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, open_mode)) < 0)
|
||||||
{
|
{
|
||||||
|
if (ao_noblock) {
|
||||||
|
printf("alsa-init: open in nonblock-mode failed, trying to open in block-mode\n");
|
||||||
|
if ((err = snd_pcm_open(&alsa_handler, alsa_device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
||||||
|
printf("alsa-init: playback open error: %s\n", snd_strerror(err));
|
||||||
|
return(0);
|
||||||
|
} else {
|
||||||
|
set_block_mode = 0;
|
||||||
|
str_block_mode = "block-mode";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
printf("alsa-init: playback open error: %s\n", snd_strerror(err));
|
printf("alsa-init: playback open error: %s\n", snd_strerror(err));
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_nonblock(alsa_handler, set_block_mode)) < 0) {
|
||||||
|
printf("alsa-init: error set block-mode %s\n", snd_strerror(err));
|
||||||
|
}
|
||||||
|
else if (verbose) {
|
||||||
|
printf("alsa-init: pcm opend in %s\n", str_block_mode);
|
||||||
|
}
|
||||||
|
|
||||||
snd_pcm_hw_params_alloca(&alsa_hwparams);
|
snd_pcm_hw_params_alloca(&alsa_hwparams);
|
||||||
snd_pcm_sw_params_alloca(&alsa_swparams);
|
snd_pcm_sw_params_alloca(&alsa_swparams);
|
||||||
|
@ -339,12 +509,21 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,
|
if (ao_mmap) {
|
||||||
SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
snd_pcm_access_mask_t *mask = alloca(snd_pcm_access_mask_sizeof());
|
||||||
{
|
snd_pcm_access_mask_none(mask);
|
||||||
printf("alsa-init: unable to set access type: %s\n",
|
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
||||||
snd_strerror(err));
|
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
||||||
return(0);
|
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
|
||||||
|
err = snd_pcm_hw_params_set_access_mask(alsa_handler, alsa_hwparams, mask);
|
||||||
|
printf("alsa-init: mmap set\n");
|
||||||
|
} else {
|
||||||
|
err = snd_pcm_hw_params_set_access(alsa_handler, alsa_hwparams,SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||||
|
printf("alsa-init: interleave set\n");
|
||||||
|
}
|
||||||
|
if (err < 0) {
|
||||||
|
printf("alsa-init: unable to set access type: %s\n", snd_strerror(err));
|
||||||
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
|
if ((err = snd_pcm_hw_params_set_format(alsa_handler, alsa_hwparams,
|
||||||
|
@ -367,21 +546,9 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to set samplerate-2: %s\n",
|
printf("alsa-init: unable to set samplerate-2: %s\n",
|
||||||
snd_strerror(err));
|
snd_strerror(err));
|
||||||
//snd_pcm_hw_params_dump(alsa_hwparams, errlog); jp
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef SET_PERIOD
|
|
||||||
{
|
|
||||||
if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, alsa_fragsize, 0)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BUFFERTIME
|
#ifdef BUFFERTIME
|
||||||
{
|
{
|
||||||
int alsa_buffer_time = 500000; /* original 60 */
|
int alsa_buffer_time = 500000; /* original 60 */
|
||||||
|
@ -406,36 +573,33 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* get chunk-size */
|
#ifdef SET_CHUNKSIZE
|
||||||
if ((err = snd_pcm_hw_params_get_period_size(alsa_hwparams, 0)) < 0)
|
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to get chunk-size in hw-params: %s\n", snd_strerror(err));
|
//set chunksize
|
||||||
return(0);
|
chunk_size = alsa_fragsize / 4;
|
||||||
} else
|
|
||||||
{
|
|
||||||
chunk_size = err;
|
|
||||||
if (verbose) {printf("alsa-init: got chunksize %i\n", chunk_size);}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get buffer size */
|
if ((err = snd_pcm_hw_params_set_period_size(alsa_handler, alsa_hwparams, chunk_size, 0)) < 0)
|
||||||
if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0)
|
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to get buffersize in hw-params: %s\n", snd_strerror(err));
|
printf("alsa-init: unable to set periodsize: %s\n", snd_strerror(err));
|
||||||
return(0);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
ao_data.buffersize = err;
|
|
||||||
if (verbose) {printf("alsa-init: got buffersize %i\n", ao_data.buffersize);}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MAX_OUTBURST > ao_data.buffersize) { //warning if MAX_OUTBURST is bigger than buffersize
|
|
||||||
printf("alsa-init: WARNING! MAX_OUTBURST exceeds your available buffersize.\nalsa-init: MAX_OUTBURST=%i, buffersize=%i\n",MAX_OUTBURST,ao_data.buffersize);}
|
|
||||||
|
|
||||||
if (chunk_size == ao_data.buffersize)
|
|
||||||
{
|
|
||||||
printf("alsa-init: Can't use period equal to buffer size (%u == %lu)", chunk_size, (long)buffer_size);
|
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
else if (verbose) {
|
||||||
|
printf("alsa-init: chunksize set to %i\n", chunk_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set period_count
|
||||||
|
if ((period_val = snd_pcm_hw_params_get_periods_max(alsa_hwparams, 0)) < alsa_fragcount) {
|
||||||
|
alsa_fragcount = period_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
printf("alsa-init: current val=%i, fragcount=%i\n", period_val, alsa_fragcount);
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_set_periods(alsa_handler, alsa_hwparams, alsa_fragcount, 0)) < 0) {
|
||||||
|
printf("alsa-init: unable to set periods: %s\n", snd_strerror(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* finally install hardware parameters */
|
/* finally install hardware parameters */
|
||||||
if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
|
if ((err = snd_pcm_hw_params(alsa_handler, alsa_hwparams)) < 0)
|
||||||
|
@ -446,34 +610,22 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
}
|
}
|
||||||
// end setting hw-params
|
// end setting hw-params
|
||||||
|
|
||||||
#ifdef SW_PARAMS
|
|
||||||
|
// gets buffersize for control
|
||||||
|
if ((err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams)) < 0)
|
||||||
{
|
{
|
||||||
size_t n;
|
printf("alsa-init: unable to get buffersize: %s\n", snd_strerror(err));
|
||||||
xfer_align = snd_pcm_sw_params_get_xfer_align(alsa_swparams);
|
return(0);
|
||||||
if (xfer_align == 0)
|
|
||||||
xfer_align = 4;
|
|
||||||
n = (ao_data.buffersize / xfer_align) * xfer_align;
|
|
||||||
|
|
||||||
if (start_delay <= 0) {
|
|
||||||
start_threshold = n + (double) ao_data.samplerate * start_delay / 1000000;
|
|
||||||
} else {
|
|
||||||
start_threshold = (double) ao_data.samplerate * start_delay / 1000000;
|
|
||||||
}
|
}
|
||||||
if (start_threshold < 1)
|
else {
|
||||||
start_threshold = 1;
|
ao_data.buffersize = err;
|
||||||
if (start_threshold > n)
|
if (verbose)
|
||||||
start_threshold = n;
|
printf("alsa-init: got buffersize=%i\n", ao_data.buffersize);
|
||||||
|
|
||||||
if (stop_delay <= 0) {
|
|
||||||
stop_threshold = ao_data.buffersize + (double) ao_data.samplerate * stop_delay / 1000000;
|
|
||||||
} else {
|
|
||||||
stop_threshold = (double) ao_data.samplerate * stop_delay / 1000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
// setting sw-params (only avail-min) if noblocking mode was choosed
|
||||||
printf("alsa-init: start_threshold=%lu, stop_threshold=%lu\n",start_threshold,stop_threshold);
|
if (ao_noblock)
|
||||||
printf("alsa-init: n=%i\n", n);
|
{
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0)
|
if ((err = snd_pcm_sw_params_current(alsa_handler, alsa_swparams)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -482,31 +634,13 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
//set min available frames to consider pcm ready (4)
|
//set min available frames to consider pcm ready (4)
|
||||||
if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, 4)) < 0)
|
//increased for nonblock-mode should be set dynamically later
|
||||||
|
if ((err = snd_pcm_sw_params_set_avail_min(alsa_handler, alsa_swparams, chunk_size)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err));
|
printf("alsa-init: unable to set avail_min %s\n",snd_strerror(err));
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params_set_start_threshold(alsa_handler, alsa_swparams, start_threshold)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set start_threshold %s\n",snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params_set_stop_threshold(alsa_handler, alsa_swparams, stop_threshold)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set stop_threshold %s\n",snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//transfers stream aligned to 4 in nonblocking-mode it would be 1
|
|
||||||
if ((err = snd_pcm_sw_params_set_xfer_align(alsa_handler, alsa_swparams, 4)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-init: unable to set xfer_align: %s\n",snd_strerror(err));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0)
|
if ((err = snd_pcm_sw_params(alsa_handler, alsa_swparams)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-init: unable to install sw-params\n");
|
printf("alsa-init: unable to install sw-params\n");
|
||||||
|
@ -519,9 +653,8 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
printf("alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);}
|
printf("alsa-init: bits per sample (bps)=%i, bits per frame (bpf)=%i, chunk_bytes=%i\n",bits_per_sample,bits_per_frame,chunk_bytes);}
|
||||||
}
|
|
||||||
|
|
||||||
#endif //end swparams
|
}//end swparams
|
||||||
|
|
||||||
if ((err = snd_pcm_prepare(alsa_handler)) < 0)
|
if ((err = snd_pcm_prepare(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -539,18 +672,17 @@ static int init(int rate_hz, int channels, int format, int flags)
|
||||||
/* close audio device */
|
/* close audio device */
|
||||||
static void uninit()
|
static void uninit()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (alsa_handler) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
//if (alsa_device != NULL)
|
if (!ao_noblock) {
|
||||||
//free(alsa_device);
|
|
||||||
|
|
||||||
//snd_pcm_hw_params_free(alsa_hwparams);
|
|
||||||
|
|
||||||
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err));
|
printf("alsa-uninit: pcm drain error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_close(alsa_handler)) < 0)
|
if ((err = snd_pcm_close(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -562,25 +694,30 @@ static void uninit()
|
||||||
alsa_device = NULL;
|
alsa_device = NULL;
|
||||||
printf("alsa-uninit: pcm closed\n");
|
printf("alsa-uninit: pcm closed\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("alsa-uninit: no handler defined!\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_pause()
|
static void audio_pause()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (!ao_noblock) {
|
||||||
|
//drain causes error in nonblock-mode!
|
||||||
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err));
|
printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (verbose)
|
||||||
|
printf("alsa-pause: paused nonblock\n");
|
||||||
|
|
||||||
#ifdef reset
|
|
||||||
if ((err = snd_pcm_reset(alsa_handler)) < 0)
|
|
||||||
{
|
|
||||||
printf("alsa-pause: pcm reset error: %s\n", snd_strerror(err));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void audio_resume()
|
static void audio_resume()
|
||||||
|
@ -599,9 +736,11 @@ static void reset()
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (!ao_noblock) {
|
||||||
|
//drain causes error in nonblock-mode!
|
||||||
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
if ((err = snd_pcm_drain(alsa_handler)) < 0)
|
||||||
{
|
{
|
||||||
printf("alsa-reset: pcm drain error: %s\n", snd_strerror(err));
|
printf("alsa-pause: pcm drain error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +749,11 @@ static void reset()
|
||||||
printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err));
|
printf("alsa-reset: pcm prepare error: %s\n", snd_strerror(err));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (verbose)
|
||||||
|
printf("alsa-reset: reset nonblocked");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef timersub
|
#ifndef timersub
|
||||||
|
@ -625,61 +768,96 @@ do { \
|
||||||
} while (0)
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* I/O error handler */
|
||||||
|
static int xrun(u_char *str_mode)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
snd_pcm_status_t *status;
|
||||||
|
|
||||||
|
snd_pcm_status_alloca(&status);
|
||||||
|
|
||||||
|
if ((err = snd_pcm_status(alsa_handler, status))<0) {
|
||||||
|
printf("status error: %s", snd_strerror(err));
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
|
||||||
|
struct timeval now, diff, tstamp;
|
||||||
|
gettimeofday(&now, 0);
|
||||||
|
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
|
||||||
|
timersub(&now, &tstamp, &diff);
|
||||||
|
printf("alsa-%s: xrun of at least %.3f msecs. resetting stream\n",
|
||||||
|
str_mode,
|
||||||
|
diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_prepare(alsa_handler))<0) {
|
||||||
|
printf("xrun: prepare error: %s", snd_strerror(err));
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return(1); /* ok, data should be accepted again */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
plays 'len' bytes of 'data'
|
plays 'len' bytes of 'data'
|
||||||
returns: number of bytes played
|
returns: number of bytes played
|
||||||
modified last at 26.06.02 by jp
|
modified last at 29.06.02 by jp
|
||||||
|
thanxs for marius <marius@rospot.com> for giving us the light ;)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int play(void* data, int len, int flags)
|
static int play(void* data, int len, int flags)
|
||||||
{
|
{
|
||||||
|
|
||||||
snd_pcm_status_t *status;
|
//ao_data.bps is always 4 cause its set to channels * 2 by alsa_format??
|
||||||
|
int num_frames = len / ao_data.bps;
|
||||||
int num_frames=len/ao_data.bps;
|
|
||||||
signed short *output_samples=data;
|
signed short *output_samples=data;
|
||||||
snd_pcm_sframes_t res = 0;
|
snd_pcm_sframes_t res = 0;
|
||||||
|
|
||||||
|
//printf("alsa-play: frames=%i, len=%i",num_frames,len);
|
||||||
|
|
||||||
if (!alsa_handler) {
|
if (!alsa_handler) {
|
||||||
printf("alsa-play: device configuration error");
|
printf("alsa-play: device configuration error");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
while (num_frames > 0) {
|
||||||
if (res == -EPIPE) { /* underrun */
|
|
||||||
snd_pcm_status_alloca(&status);
|
if (ao_mmap) {
|
||||||
if ((res = snd_pcm_status(alsa_handler, status))<0) {
|
res = snd_pcm_mmap_writei(alsa_handler, (void *)output_samples, num_frames);
|
||||||
printf("alsa-play: buffer underrun. can't determine length");
|
|
||||||
} else {
|
} else {
|
||||||
if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {
|
res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
|
||||||
struct timeval now, diff, tstamp;
|
|
||||||
gettimeofday(&now, 0);
|
|
||||||
snd_pcm_status_get_trigger_tstamp(status, &tstamp);
|
|
||||||
timersub(&now, &tstamp, &diff);
|
|
||||||
printf("alsa-play: xrun of at least %.3f msecs. resetting stream",
|
|
||||||
diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
|
|
||||||
} else
|
|
||||||
printf("alsa-play: xrun. can't determine length");
|
|
||||||
}
|
|
||||||
res = snd_pcm_prepare(alsa_handler);
|
|
||||||
}
|
|
||||||
else if (res == -ESTRPIPE) { /* suspend */
|
|
||||||
printf("alsa-play: pcm in suspend mode. trying to resume");
|
|
||||||
while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
|
|
||||||
sleep(1);
|
|
||||||
if (res < 0)
|
|
||||||
res = snd_pcm_prepare(alsa_handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res >= 0)
|
if (res == -EAGAIN) {
|
||||||
res = snd_pcm_writei(alsa_handler, (void *)output_samples, num_frames);
|
snd_pcm_wait(alsa_handler, 1000);
|
||||||
|
}
|
||||||
|
else if (res == -EPIPE) { /* underrun */
|
||||||
|
if (xrun("play") <= 0) {
|
||||||
|
printf("alsa-play: xrun reset error");
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (res == -ESTRPIPE) { /* suspend */
|
||||||
|
printf("alsa-play: pcm in suspend mode. trying to resume\n");
|
||||||
|
while ((res = snd_pcm_resume(alsa_handler)) == -EAGAIN)
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
else if (res < 0) {
|
||||||
|
printf("alsa-play: unknown status, trying to reset soundcard\n");
|
||||||
|
if ((res = snd_pcm_prepare(alsa_handler)) < 0) {
|
||||||
|
printf("alsa-play: snd prepare error");
|
||||||
|
return(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (res > 0) {
|
if (res > 0) {
|
||||||
output_samples += ao_data.channels * res;
|
output_samples += ao_data.channels * res;
|
||||||
num_frames -= res;
|
num_frames -= res;
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (res == -EPIPE || num_frames > 0);
|
} //end while
|
||||||
|
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
printf("alsa-play: write error %s", snd_strerror(res));
|
printf("alsa-play: write error %s", snd_strerror(res));
|
||||||
|
@ -688,12 +866,14 @@ static int play(void* data, int len, int flags)
|
||||||
return res < 0 ? (int)res : len;
|
return res < 0 ? (int)res : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* how many byes are free in the buffer */
|
/* how many byes are free in the buffer */
|
||||||
static int get_space()
|
static int get_space()
|
||||||
{
|
{
|
||||||
snd_pcm_status_t *status;
|
snd_pcm_status_t *status;
|
||||||
int ret;
|
int ret;
|
||||||
|
char *str_status;
|
||||||
|
|
||||||
|
//snd_pcm_sframes_t avail_frames = 0;
|
||||||
|
|
||||||
if ((ret = snd_pcm_status_malloc(&status)) < 0)
|
if ((ret = snd_pcm_status_malloc(&status)) < 0)
|
||||||
{
|
{
|
||||||
|
@ -710,25 +890,49 @@ static int get_space()
|
||||||
switch(snd_pcm_status_get_state(status))
|
switch(snd_pcm_status_get_state(status))
|
||||||
{
|
{
|
||||||
case SND_PCM_STATE_OPEN:
|
case SND_PCM_STATE_OPEN:
|
||||||
|
str_status = "open";
|
||||||
case SND_PCM_STATE_PREPARED:
|
case SND_PCM_STATE_PREPARED:
|
||||||
|
if (str_status != "open")
|
||||||
|
str_status = "prepared";
|
||||||
case SND_PCM_STATE_RUNNING:
|
case SND_PCM_STATE_RUNNING:
|
||||||
ret = snd_pcm_status_get_avail(status) * ao_data.bps;
|
ret = snd_pcm_status_get_avail(status) * ao_data.bps;
|
||||||
|
//avail_frames = snd_pcm_avail_update(alsa_handler) * ao_data.bps;
|
||||||
|
if (str_status != "open" && str_status != "prepared")
|
||||||
|
str_status = "running";
|
||||||
|
break;
|
||||||
|
case SND_PCM_STATE_PAUSED:
|
||||||
|
if (verbose) printf("alsa-space: paused");
|
||||||
|
str_status = "paused";
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case SND_PCM_STATE_XRUN:
|
||||||
|
xrun("space");
|
||||||
|
str_status = "xrun";
|
||||||
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
str_status = "undefined";
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verbose && str_status != "running")
|
||||||
|
printf("alsa-space: free space = %i, status=%i, %s --\n", ret, status, str_status);
|
||||||
snd_pcm_status_free(status);
|
snd_pcm_status_free(status);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
|
printf("negative value!!\n");
|
||||||
ret = 0;
|
ret = 0;
|
||||||
//printf("alsa-space: free space = %i",ret);
|
}
|
||||||
|
|
||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* delay in seconds between first and last sample in buffer */
|
/* delay in seconds between first and last sample in buffer */
|
||||||
static float get_delay()
|
static float get_delay()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (alsa_handler) {
|
||||||
|
|
||||||
snd_pcm_status_t *status;
|
snd_pcm_status_t *status;
|
||||||
float ret;
|
float ret;
|
||||||
|
|
||||||
|
@ -760,4 +964,8 @@ static float get_delay()
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
return(ret);
|
return(ret);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue