diff options
author | James Courtier-Dutton <jcdutton@users.sourceforge.net> | 2001-08-26 17:52:53 +0000 |
---|---|---|
committer | James Courtier-Dutton <jcdutton@users.sourceforge.net> | 2001-08-26 17:52:53 +0000 |
commit | 8618c4ddd4a24e8b9f55be99b745a6cf82ceddc3 (patch) | |
tree | 103932d2ada806f4851517b88452ff1d81343d0f | |
parent | 3ac33f119ce7751c00fac4e4df0e5aba87cb1d15 (diff) | |
download | xine-lib-8618c4ddd4a24e8b9f55be99b745a6cf82ceddc3.tar.gz xine-lib-8618c4ddd4a24e8b9f55be99b745a6cf82ceddc3.tar.bz2 |
Surround sound audio actually works now.
CVS patchset: 504
CVS date: 2001/08/26 17:52:53
-rw-r--r-- | src/audio_out/audio_alsa_out.c | 405 |
1 files changed, 200 insertions, 205 deletions
diff --git a/src/audio_out/audio_alsa_out.c b/src/audio_out/audio_alsa_out.c index e20a636fe..64ecac600 100644 --- a/src/audio_out/audio_alsa_out.c +++ b/src/audio_out/audio_alsa_out.c @@ -22,11 +22,11 @@ * - frame size calculation added (16-08-2001) * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org> * for initial ALSA 0.9.x support. - * adding MONO support. + * adding MONO/STEREO/4CHANNEL/5CHANNEL/5.1CHANNEL analogue support. * (c) 2001 James Courtier-Dutton <James@superbug.demon.co.uk> * * - * $Id: audio_alsa_out.c,v 1.22 2001/08/26 15:24:24 jcdutton Exp $ + * $Id: audio_alsa_out.c,v 1.23 2001/08/26 17:52:53 jcdutton Exp $ */ #ifdef HAVE_CONFIG_H @@ -62,7 +62,6 @@ #include "xine_internal.h" #include "monitor.h" #include "audio_out.h" -#include "metronom.h" #include "utils.h" #ifndef AFMT_S16_NE @@ -77,23 +76,26 @@ #define AO_OUT_ALSA_IFACE_VERSION 2 #define GAP_TOLERANCE 5000 -#define MAX_MASTER_CLOCK_DIV 5000 -#define MAX_GAP 90000 typedef struct alsa_driver_s { - ao_driver_t ao_driver; - char audio_dev[20]; - snd_pcm_t * audio_fd; - int capabilities; - int open_mode; - - 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 */ + ao_driver_t ao_driver; + char audio_default_device[20]; + char audio_front_device[20]; + char audio_surround40_device[20]; + char audio_surround50_device[20]; + char audio_surround51_device[20]; + char audio_ac3_device[128]; + snd_pcm_t *audio_fd; + int capabilities; + int open_mode; + + 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 */ } alsa_driver_t; @@ -103,24 +105,23 @@ typedef struct alsa_driver_s { */ static int ao_alsa_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode) { - alsa_driver_t *this = (alsa_driver_t *) this_gen; - snd_pcm_stream_t direction = SND_PCM_STREAM_PLAYBACK; - snd_pcm_hw_params_t *params; - snd_pcm_sw_params_t *swparams; - snd_pcm_sframes_t buffer_time; - snd_pcm_sframes_t period_time,tmp; - snd_aes_iec958_t spdif; + alsa_driver_t *this = (alsa_driver_t *) this_gen; + 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_sframes_t buffer_time; + snd_pcm_sframes_t period_time,tmp; + snd_aes_iec958_t spdif; snd_ctl_elem_value_t *ctl; snd_ctl_t *ctl_handle; - snd_pcm_info_t *info; - - char ctl_name[12]; - int ctl_card; - - - int err, step; + snd_pcm_info_t *info; + char ctl_name[12]; + int ctl_card; + int err, step; // int open_mode=1; //NONBLOCK - int open_mode=0; //BLOCK + int open_mode=0; //BLOCK + snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); err = snd_output_stdio_attach(&jcd_out, stderr, 0); @@ -128,28 +129,37 @@ static int ao_alsa_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int switch (mode) { case AO_CAP_MODE_MONO: this->num_channels = 1; + pcm_device = this->audio_default_device; break; case AO_CAP_MODE_STEREO: this->num_channels = 2; - break; - case AO_CAP_MODE_AC3: - this->num_channels = 2; + pcm_device = this->audio_front_device; break; case AO_CAP_MODE_4CHANNEL: this->num_channels = 4; + pcm_device = this->audio_surround40_device; break; case AO_CAP_MODE_5CHANNEL: this->num_channels = 5; + pcm_device = this->audio_surround50_device; break; case AO_CAP_MODE_5_1CHANNEL: this->num_channels = 6; + pcm_device = this->audio_surround51_device; + break; + case AO_CAP_MODE_AC3: + this->num_channels = 2; + pcm_device = this->audio_ac3_device; break; default: - error ("ALSA Driver only supports AC3/stereo/mono output modes at the moment. Requested mode=%d",mode); + error ("ALSA Driver does not support the requested mode: 0x%X",mode); return 0; } -if (this->audio_fd != NULL) { + printf("audio_alsa_out: Audio Device name = %s\n",pcm_device); + printf("audio_alsa_out: Number of channels = %d\n",this->num_channels); + + if (this->audio_fd != NULL) { error ("Already open...WHY!"); snd_pcm_close (this->audio_fd); } @@ -164,130 +174,119 @@ if (this->audio_fd != NULL) { * open audio device */ - err=snd_pcm_open(&this->audio_fd, this->audio_dev, direction, open_mode); + err=snd_pcm_open(&this->audio_fd, pcm_device, direction, open_mode); if(err <0 ) { error("snd_pcm_open() failed: %s", snd_strerror(err)); error(">>> Check if another program don't already use PCM <<<"); return 0; } - if (mode & AO_CAP_MODE_AC3) { - - snd_pcm_info_alloca(&info); - - if ((err = snd_pcm_info(this->audio_fd, info)) < 0) { - fprintf(stderr, "info: %s\n", snd_strerror(err)); - goto __close; - } - printf("device: %d, subdevice: %d\n", snd_pcm_info_get_device(info), - snd_pcm_info_get_subdevice(info)); - - - spdif.status[0] = IEC958_AES0_NONAUDIO | - IEC958_AES0_CON_EMPHASIS_NONE; - spdif.status[1] = IEC958_AES1_CON_ORIGINAL | - IEC958_AES1_CON_PCM_CODER; - spdif.status[2] = 0; - spdif.status[3] = IEC958_AES3_CON_FS_48000; - - snd_ctl_elem_value_alloca(&ctl); - snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM); - snd_ctl_elem_value_set_device(ctl,snd_pcm_info_get_device(info)); - snd_ctl_elem_value_set_subdevice(ctl, snd_pcm_info_get_subdevice(info)); - snd_ctl_elem_value_set_name(ctl, SND_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM)); - snd_ctl_elem_value_set_iec958(ctl, &spdif); - ctl_card = snd_pcm_info_get_card(info); - if (ctl_card < 0) { - fprintf(stderr, "Unable to setup the IEC958 (S/PDIF) interface - PCM has no assigned card"); - goto __close; - - } - sprintf(ctl_name, "hw:%d", ctl_card); - printf("hw:%d\n", ctl_card); - if ((err = snd_ctl_open(&ctl_handle, ctl_name, 0)) < 0) { - fprintf(stderr, "Unable to open the control interface '%s': - %s", ctl_name, snd_strerror(err)); - goto __close; - } - if ((err = snd_ctl_elem_write(ctl_handle, ctl)) < 0) { - fprintf(stderr, "Unable to update the IEC958 control: %s", snd_strerror(err)); - - goto __close; - } - snd_ctl_close(ctl_handle); - } - + if (mode & AO_CAP_MODE_AC3) { + snd_pcm_info_alloca(&info); + if ((err = snd_pcm_info(this->audio_fd, info)) < 0) { + fprintf(stderr, "info: %s\n", snd_strerror(err)); + goto __close; + } + printf("device: %d, subdevice: %d\n", snd_pcm_info_get_device(info), + snd_pcm_info_get_subdevice(info)); + + spdif.status[0] = IEC958_AES0_NONAUDIO | + IEC958_AES0_CON_EMPHASIS_NONE; + spdif.status[1] = IEC958_AES1_CON_ORIGINAL | + IEC958_AES1_CON_PCM_CODER; + spdif.status[2] = 0; + spdif.status[3] = IEC958_AES3_CON_FS_48000; + + snd_ctl_elem_value_alloca(&ctl); + snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM); + snd_ctl_elem_value_set_device(ctl,snd_pcm_info_get_device(info)); + snd_ctl_elem_value_set_subdevice(ctl, snd_pcm_info_get_subdevice(info)); + snd_ctl_elem_value_set_name(ctl, SND_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM)); + snd_ctl_elem_value_set_iec958(ctl, &spdif); + ctl_card = snd_pcm_info_get_card(info); + if (ctl_card < 0) { + fprintf(stderr, "Unable to setup the IEC958 (S/PDIF) interface - PCM has no assigned card"); + goto __close; + } + sprintf(ctl_name, "hw:%d", ctl_card); + printf("hw:%d\n", ctl_card); + if ((err = snd_ctl_open(&ctl_handle, ctl_name, 0)) < 0) { + fprintf(stderr, "Unable to open the control interface '%s': + %s", ctl_name, snd_strerror(err)); + goto __close; + } + if ((err = snd_ctl_elem_write(ctl_handle, ctl)) < 0) { + fprintf(stderr, "Unable to update the IEC958 control: %s", snd_strerror(err)); + goto __close; + } + snd_ctl_close(ctl_handle); + } /* We wanted non blocking open but now put it back to normal */ snd_pcm_nonblock(this->audio_fd, 0); /* * configure audio device */ - err = snd_pcm_hw_params_any(this->audio_fd, params); - if (err < 0) { - error("Broken configuration for this PCM: no configurations available"); - goto __close; - } - /* set interleaved access */ - err = snd_pcm_hw_params_set_access(this->audio_fd, params, - SND_PCM_ACCESS_RW_INTERLEAVED); - if (err < 0) { - error("Access type not available"); - goto __close; - } - err = snd_pcm_hw_params_set_format(this->audio_fd, params, bits == 16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8); - if (err < 0) { - error("Sample format non available"); - goto __close; - } - err = snd_pcm_hw_params_set_channels(this->audio_fd, params, this->num_channels); - printf("audio_alsa_out: Number to channels=%d\n",this->num_channels); - if (err < 0) { - error("Channels count non available"); - goto __close; - } - err = snd_pcm_hw_params_set_rate_near(this->audio_fd, params, rate, 0); - if (err < 0) { - error("Rate not available"); - goto __close; - } - buffer_time = snd_pcm_hw_params_set_buffer_time_near(this->audio_fd, params, - 500000, 0); - if (buffer_time < 0) { - error("Buffer time not available"); - goto __close; - } - step = 2; + err = snd_pcm_hw_params_any(this->audio_fd, params); + if (err < 0) { + error("Broken configuration for this PCM: no configurations available"); + goto __close; + } + /* set interleaved access */ + err = snd_pcm_hw_params_set_access(this->audio_fd, params, + SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) { + error("Access type not available"); + goto __close; + } + err = snd_pcm_hw_params_set_format(this->audio_fd, params, bits == 16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8); + if (err < 0) { + error("Sample format non available"); + goto __close; + } + err = snd_pcm_hw_params_set_channels(this->audio_fd, params, this->num_channels); + if (err < 0) { + error("Channels count non available"); + goto __close; + } + err = snd_pcm_hw_params_set_rate_near(this->audio_fd, params, rate, 0); + if (err < 0) { + error("Rate not available"); + goto __close; + } + buffer_time = snd_pcm_hw_params_set_buffer_time_near(this->audio_fd, params, + 500000, 0); + if (buffer_time < 0) { + error("Buffer time not available"); + goto __close; + } + step = 2; + period_time = 10000 * 2; + do { + period_time /= 2; + tmp = snd_pcm_hw_params_set_period_time_near(this->audio_fd, params, + period_time, 0); + if (tmp == period_time) { + period_time /= 3; + tmp = snd_pcm_hw_params_set_period_time_near(this->audio_fd, params, + period_time, 0); + if (tmp == period_time) period_time = 10000 * 2; - do { - period_time /= 2; - tmp = snd_pcm_hw_params_set_period_time_near(this->audio_fd, params, - period_time, 0); - if (tmp == period_time) { - period_time /= 3; - tmp = snd_pcm_hw_params_set_period_time_near(this->audio_fd, params, - period_time, 0); - if (tmp == period_time) - period_time = 10000 * 2; - } - if (period_time < 0) { - fprintf(stderr, "Period time not available"); - goto __close; - } - } while (buffer_time == period_time && period_time > 10000); - if (buffer_time == period_time) { - error("Buffer time and period time match, could not use"); - goto __close; - } - if ((err = snd_pcm_hw_params(this->audio_fd, params)) < 0) { - error("PCM hw_params failed: %s", snd_strerror(err)); - goto __close; - } - - - - + } + if (period_time < 0) { + fprintf(stderr, "Period time not available"); + goto __close; + } + } while (buffer_time == period_time && period_time > 10000); + if (buffer_time == period_time) { + error("Buffer time and period time match, could not use"); + goto __close; + } + if ((err = snd_pcm_hw_params(this->audio_fd, params)) < 0) { + error("PCM hw_params failed: %s", snd_strerror(err)); + goto __close; + } this->output_sample_rate = this->input_sample_rate; this->sample_rate_factor = (double) this->output_sample_rate / (double) this->input_sample_rate; /* @@ -301,14 +300,8 @@ if (this->audio_fd != NULL) { /* Install swparams into current parameters */ snd_pcm_sw_params(this->audio_fd, swparams); - - snd_pcm_dump_setup(this->audio_fd, jcd_out); - snd_pcm_sw_params_dump(swparams, jcd_out); - - - // write_pause_burst(this,0); - - + snd_pcm_dump_setup(this->audio_fd, jcd_out); + snd_pcm_sw_params_dump(swparams, jcd_out); return this->output_sample_rate; __close: snd_pcm_close (this->audio_fd); @@ -318,14 +311,14 @@ __close: static int ao_alsa_num_channels(ao_driver_t *this_gen) { - alsa_driver_t *this = (alsa_driver_t *) this_gen; - return this->num_channels; + alsa_driver_t *this = (alsa_driver_t *) this_gen; + return this->num_channels; } static int ao_alsa_bytes_per_frame(ao_driver_t *this_gen) { - alsa_driver_t *this = (alsa_driver_t *) this_gen; - return this->bytes_per_frame; + alsa_driver_t *this = (alsa_driver_t *) this_gen; + return this->bytes_per_frame; } static int ao_alsa_get_gap_tolerance (ao_driver_t *this_gen) @@ -337,6 +330,7 @@ static int ao_alsa_delay (ao_driver_t *this_gen) { snd_pcm_status_t *pcm_stat; snd_pcm_sframes_t delay; + alsa_driver_t *this = (alsa_driver_t *) this_gen; snd_pcm_status_alloca(&pcm_stat); snd_pcm_status(this->audio_fd, pcm_stat); @@ -348,27 +342,26 @@ static int ao_alsa_delay (ao_driver_t *this_gen) void xrun(alsa_driver_t *this) { - snd_pcm_status_t *status; - int res; - - snd_pcm_status_alloca(&status); - if ((res = snd_pcm_status(this->audio_fd, status))<0) { - printf("status error: %s", snd_strerror(res)); - return; - } - 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); - fprintf(stderr, "xrun!!! (at least %.3f ms long)\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0); - - if ((res = snd_pcm_prepare(this->audio_fd))<0) { - printf("xrun: prepare error: %s", snd_strerror(res)); - return; - } - return; /* ok, data should be accepted again */ - } + snd_pcm_status_t *status; + int res; + + snd_pcm_status_alloca(&status); + if ((res = snd_pcm_status(this->audio_fd, status))<0) { + printf("status error: %s", snd_strerror(res)); + return; + } + 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); + fprintf(stderr, "xrun!!! (at least %.3f ms long)\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0); + if ((res = snd_pcm_prepare(this->audio_fd))<0) { + printf("xrun: prepare error: %s", snd_strerror(res)); + return; + } + return; /* ok, data should be accepted again */ + } } static int ao_alsa_write(ao_driver_t *this_gen,int16_t *data, uint32_t count) @@ -377,7 +370,7 @@ static int ao_alsa_write(ao_driver_t *this_gen,int16_t *data, uint32_t count) alsa_driver_t *this = (alsa_driver_t *) this_gen; while( count > 0) { - r = snd_pcm_writei(this->audio_fd, data, count); + r = snd_pcm_writei(this->audio_fd, data, count); if (r == -EAGAIN || (r >=0 && r < count)) { snd_pcm_wait(this->audio_fd, 1000); } else if (r == -EPIPE) { @@ -389,7 +382,7 @@ static int ao_alsa_write(ao_driver_t *this_gen,int16_t *data, uint32_t count) data += r * 2 * this->num_channels; } } - /* FIXME: What should this really be? */ + /* FIXME: What should this really be? */ return 1; } @@ -412,9 +405,6 @@ static void ao_alsa_exit(ao_driver_t *this_gen) free (this); } -/* - * - */ static int ao_alsa_get_property (ao_driver_t *this, int property) { /* FIXME: implement some properties @@ -454,18 +444,24 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { alsa_driver_t *this; int err; char *pcm_device; - char *ac3_device; snd_pcm_hw_params_t *params; this = (alsa_driver_t *) malloc (sizeof (alsa_driver_t)); snd_pcm_hw_params_alloca(¶ms); - pcm_device = config->lookup_str(config,"alsa_pcm_device", "default"); - ac3_device = config->lookup_str(config,"alsa_ac3_device", "hw:0,2"); - + pcm_device = config->lookup_str(config,"alsa_default_device", "default"); + strcpy(this->audio_default_device,pcm_device); + pcm_device = config->lookup_str(config,"alsa_front_device", "front"); + strcpy(this->audio_front_device,pcm_device); + pcm_device = config->lookup_str(config,"alsa_surround40_device", "surround40"); + strcpy(this->audio_surround40_device,pcm_device); + pcm_device = config->lookup_str(config,"alsa_surround50_device", "surround51"); + strcpy(this->audio_surround50_device,pcm_device); + pcm_device = config->lookup_str(config,"alsa_surround51_device", "surround51"); + strcpy(this->audio_surround51_device,pcm_device); + pcm_device = config->lookup_str(config,"alsa_ac3_device", "hw:0,2"); + strcpy(this->audio_ac3_device,pcm_device); - strcpy(this->audio_dev,pcm_device); - printf("audio_alsa_out: devicename=%s\n",pcm_device); /* * find best device driver/channel */ @@ -473,11 +469,11 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { * open that device */ - err=snd_pcm_open(&this->audio_fd, this->audio_dev, SND_PCM_STREAM_PLAYBACK, 0); - if(err <0 ) { - error("snd_pcm_open() failed: %d", err); - error(">>> Check if another program don't already use PCM <<<"); - return NULL; + err=snd_pcm_open(&this->audio_fd, this->audio_default_device, SND_PCM_STREAM_PLAYBACK, 0); + if(err <0 ) { + error("snd_pcm_open() failed: %d", err); + error(">>> Check if another program don't already use PCM <<<"); + return NULL; } /* @@ -489,7 +485,7 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { return NULL; } err = snd_pcm_hw_params_set_access(this->audio_fd, params, - SND_PCM_ACCESS_RW_INTERLEAVED); + SND_PCM_ACCESS_RW_INTERLEAVED); if (err < 0) { error("Access type not available"); return NULL; @@ -500,22 +496,20 @@ ao_driver_t *init_audio_out_plugin (config_values_t *config) { if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 2))) this->capabilities |= AO_CAP_MODE_STEREO; if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 4)) && - config->lookup_int (config, "four_channel", 0) ) - this->capabilities |= AO_CAP_MODE_4CHANNEL; + config->lookup_int (config, "four_channel", 0) ) + this->capabilities |= AO_CAP_MODE_4CHANNEL; if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 5)) && - config->lookup_int (config, "five_channel", 0) ) - this->capabilities |= AO_CAP_MODE_5CHANNEL; + config->lookup_int (config, "five_channel", 0) ) + this->capabilities |= AO_CAP_MODE_5CHANNEL; if (!(snd_pcm_hw_params_test_channels(this->audio_fd, params, 6)) && - config->lookup_int (config, "five_lfe_channel", 0) ) - this->capabilities |= AO_CAP_MODE_5_1CHANNEL; + config->lookup_int (config, "five_lfe_channel", 0) ) + this->capabilities |= AO_CAP_MODE_5_1CHANNEL; snd_pcm_close (this->audio_fd); this->audio_fd=NULL; this->output_sample_rate = 0; if (config->lookup_int (config, "ac3_pass_through", 0)) { this->capabilities |= AO_CAP_MODE_AC3; - strcpy(this->audio_dev,ac3_device); - printf("AC3 pass through activated\n"); } printf("audio_alsa_out: Capabilities 0x%X\n",this->capabilities); @@ -544,3 +538,4 @@ static ao_info_t ao_info_alsa9 = { ao_info_t *get_audio_out_plugin_info() { return &ao_info_alsa9; } + |