On Mon, 1 Sep 2003, James Courtier-Dutton wrote:
I have found two problems with using the dmix alsa device name. 1) snd_pcm_hw_params_can_pause (params) causes alsa-lib to assert!
2) Sound is broken up. a) It can only function with 2 periods, why is that? Having 8 periods might be better, although "front" works fine with 2 periods, but "dmix" with 2 periods fails.
See attachment dmix-fail.c compile with gcc -g -DDEBUG -lasound a.c
Shows problem (1) and (2)
I don't have a small compilable example for this yet. The problem application is the latest xine cvs. (xine.sf.net)b) Of the 2 periods, it sounds like sound is only being played from one of the periods, with silence for the other period.
Summary: - If I use device name "front", there are no problems with sound output. If I use device name "dmix", there are the above problems.
Show me your code, please (compilable example).
Jaroslav
Cheers James
/* * Copyright (C) 2000-2002 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * * Credits go * - for the SPDIF A/52 sync part * - frame size calculation added (16-08-2001) * (c) 2001 Andy Lo A Foe <[EMAIL PROTECTED]> * for initial ALSA 0.9.x support. * adding MONO/STEREO/4CHANNEL/5CHANNEL/5.1CHANNEL analogue support. * (c) 2001 James Courtier-Dutton <[EMAIL PROTECTED]> * * * $Id: audio_alsa_out.c,v 1.106 2003/09/01 04:08:41 jcdutton Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <math.h>
#include <alloca.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#include <sys/ioctl.h>
#include <inttypes.h>
#include <pthread.h>
/*
#define ALSA_LOG
*/
/*
#define LOG_DEBUG
*/
#define AO_OUT_ALSA_IFACE_VERSION 7
#define BUFFER_TIME 1000*1000
#define PERIOD_TIME 100*1000
#define GAP_TOLERANCE 5000
#define MIXER_MASK_LEFT (1 << 0)
#define MIXER_MASK_RIGHT (1 << 1)
#define MIXER_MASK_STEREO (MIXER_MASK_LEFT|MIXER_MASK_RIGHT)
typedef struct alsa_driver_s {
snd_pcm_t *audio_fd;
int capabilities;
int open_mode;
int has_pause_resume;
int32_t output_sample_rate, input_sample_rate;
double sample_rate_factor;
uint32_t num_channels;
uint32_t bits_per_sample;
uint32_t bytes_per_frame;
uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware
*/
snd_pcm_uframes_t buffer_size;
int32_t mmap;
} alsa_driver_t;
static snd_output_t *jcd_out;
/*
* open the audio device for writing to
*/
int main (int argc, char **argv) {
alsa_driver_t variables;
alsa_driver_t *this = &variables;
char *pcm_device;
snd_pcm_stream_t direction = SND_PCM_STREAM_PLAYBACK;
snd_pcm_hw_params_t *params;
snd_pcm_sw_params_t *swparams;
snd_pcm_access_mask_t *mask;
snd_pcm_uframes_t period_size;
uint32_t periods;
uint32_t buffer_time=BUFFER_TIME;
int err, dir;
int open_mode=1; /* NONBLOCK */
/* int open_mode=0; BLOCK */
int rate = 48000;
int bits = 16;
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_sw_params_alloca(&swparams);
err = snd_output_stdio_attach(&jcd_out, stdout, 0);
pcm_device = "dmix";
#ifdef ALSA_LOG
printf("audio_alsa_out: Audio Device name = %s\n",pcm_device);
printf("audio_alsa_out: Number of channels = %d\n",this->num_channels);
#endif
this->audio_fd = NULL;
this->open_mode = open_mode;
this->input_sample_rate = rate;
this->bits_per_sample = bits;
this->num_channels = 2;
this->bytes_in_buffer = 0;
/*
* open audio device
*/
err=snd_pcm_open(&this->audio_fd, pcm_device, direction, open_mode);
if(err <0 ) {
printf ("audio_alsa_out: snd_pcm_open() of %s failed: %s\n", pcm_device,
snd_strerror(err));
printf ("audio_alsa_out: >>> check if another program don't already use PCM
<<<\n");
return 0;
}
/* printf ("audio_alsa_out: snd_pcm_open() opened %s\n", pcm_device); */
/* We wanted non blocking open but now put it back to normal */
//snd_pcm_nonblock(this->audio_fd, 0);
snd_pcm_nonblock(this->audio_fd, 1);
/*
* configure audio device
*/
err = snd_pcm_hw_params_any(this->audio_fd, params);
if (err < 0) {
printf ("audio_alsa_out: broken configuration for this PCM: no configurations
available\n");
goto __close;
}
/* set interleaved access */
if (this->mmap != 0) {
mask = alloca(snd_pcm_access_mask_sizeof());
snd_pcm_access_mask_none(mask);
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
snd_pcm_access_mask_set(mask, SND_PCM_ACCESS_MMAP_COMPLEX);
err = snd_pcm_hw_params_set_access_mask(this->audio_fd, params, mask);
if (err < 0) {
printf ("audio_alsa_out: mmap not availiable, falling back to compatiblity
mode\n");
this->mmap=0;
err = snd_pcm_hw_params_set_access(this->audio_fd, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
}
} else {
err = snd_pcm_hw_params_set_access(this->audio_fd, params,
SND_PCM_ACCESS_RW_INTERLEAVED);
}
if (err < 0) {
printf ("audio_alsa_out: access type not available\n");
goto __close;
}
/* set the sample format ([SU]{8,16{LE,BE}})*/
err = snd_pcm_hw_params_set_format(this->audio_fd, params, (bits == 16) ?
#ifdef WORDS_BIGENDIAN
SND_PCM_FORMAT_S16_BE
#else
SND_PCM_FORMAT_S16_LE
#endif
: SND_PCM_FORMAT_U8);
if (err < 0) {
printf ("audio_alsa_out: sample format non available\n");
goto __close;
}
/* set the number of channels */
err = snd_pcm_hw_params_set_channels(this->audio_fd, params, this->num_channels);
if (err < 0) {
printf ("audio_alsa_out: Cannot set number of channels to %d (err=%d)\n",
this->num_channels, err);
goto __close;
}
/* set the stream rate [Hz] */
dir=0;
err = snd_pcm_hw_params_set_rate_near(this->audio_fd, params, &rate, &dir);
if (err < 0) {
printf ("audio_alsa_out: rate not available\n");
goto __close;
}
this->output_sample_rate = (uint32_t)rate;
if (this->input_sample_rate != this->output_sample_rate) {
printf ("audio_alsa_out: audio rate : %d requested, %d provided by device/sec\n",
this->input_sample_rate, this->output_sample_rate);
}
/* Set period to buffer size ratios at 8 periods to 1 buffer */
dir=-1;
periods=8;
err = snd_pcm_hw_params_set_periods_near(this->audio_fd, params, &periods ,&dir);
if (err < 0) {
printf ("audio_alsa_out: unable to set any periods\n");
goto __close;
}
printf ("audio_alsa_out: Requested 8 periods, got %d\n", periods);
/* set the ring-buffer time [us] (large enough for x us|y samples ...) */
dir=0;
err = snd_pcm_hw_params_set_buffer_time_near(this->audio_fd, params, &buffer_time,
&dir);
if (err < 0) {
printf ("audio_alsa_out: buffer time not available\n");
goto __close;
}
err = snd_pcm_hw_params_get_buffer_size(params, &(this->buffer_size));
#if 0
/* set the period time [us] (interrupt every x us|y samples ...) */
dir=0;
period_size=this->buffer_size/8;
err = snd_pcm_hw_params_set_period_size_near(this->audio_fd, params, &period_size,
&dir);
if (err < 0) {
printf ("audio_alsa_out: period time not available");
goto __close;
}
#endif
dir=0;
err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir);
if (2*period_size > this->buffer_size) {
printf ("audio_alsa_out: buffer to small, could not use\n");
goto __close;
}
/* write the parameters to device */
err = snd_pcm_hw_params(this->audio_fd, params);
if (err < 0) {
printf ("audio_alsa_out: pcm hw_params failed: %s\n", snd_strerror(err));
goto __close;
}
/* Check for pause/resume support */
this->has_pause_resume = ( snd_pcm_hw_params_can_pause (params)
&& snd_pcm_hw_params_can_resume (params) );
this->sample_rate_factor = (double) this->output_sample_rate / (double)
this->input_sample_rate;
this->bytes_per_frame = snd_pcm_frames_to_bytes (this->audio_fd, 1);
/*
* audio buffer size handling
*/
/* Copy current parameters into swparams */
err = snd_pcm_sw_params_current(this->audio_fd, swparams);
if (err < 0) {
printf ("audio_alsa_out: Unable to determine current swparams: %s\n",
snd_strerror(err));
goto __close;
}
/* align all transfers to 1 sample */
err = snd_pcm_sw_params_set_xfer_align(this->audio_fd, swparams, 1);
if (err < 0) {
printf ("audio_alsa_out: Unable to set transfer alignment: %s\n",
snd_strerror(err));
goto __close;
}
/* allow the transfer when at least period_size samples can be processed */
err = snd_pcm_sw_params_set_avail_min(this->audio_fd, swparams, period_size);
if (err < 0) {
printf ("audio_alsa_out: Unable to set available min: %s\n", snd_strerror(err));
goto __close;
}
/* start the transfer when the buffer contains at least period_size samples */
err = snd_pcm_sw_params_set_start_threshold(this->audio_fd, swparams, period_size);
if (err < 0) {
printf ("audio_alsa_out: Unable to set start threshold: %s\n", snd_strerror(err));
goto __close;
}
/* never stop the transfer, even on xruns */
err = snd_pcm_sw_params_set_stop_threshold(this->audio_fd, swparams,
this->buffer_size);
if (err < 0) {
printf ("audio_alsa_out: Unable to set stop threshold: %s\n", snd_strerror(err));
goto __close;
}
/* Install swparams into current parameters */
err = snd_pcm_sw_params(this->audio_fd, swparams);
if (err < 0) {
printf ("audio_alsa_out: Unable to set swparams: %s\n", snd_strerror(err));
goto __close;
}
#ifdef ALSA_LOG
snd_pcm_dump_setup(this->audio_fd, jcd_out);
snd_pcm_sw_params_dump(swparams, jcd_out);
#endif
// return this->output_sample_rate;
__close:
snd_pcm_close (this->audio_fd);
this->audio_fd=NULL;
return 0;
}
