diff options
-rw-r--r-- | src/audio_out/audio_sun_out.c | 237 |
1 files changed, 213 insertions, 24 deletions
diff --git a/src/audio_out/audio_sun_out.c b/src/audio_out/audio_sun_out.c index 797f91fa8..b481ced56 100644 --- a/src/audio_out/audio_sun_out.c +++ b/src/audio_out/audio_sun_out.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_sun_out.c,v 1.23 2002/09/11 18:19:53 jkeil Exp $ + * $Id: audio_sun_out.c,v 1.24 2002/10/07 14:14:30 jkeil Exp $ */ #ifdef HAVE_CONFIG_H @@ -32,6 +32,9 @@ #include <math.h> #include <unistd.h> #include <sys/audioio.h> +#if HAVE_SYS_MIXER_H +#include <sys/mixer.h> +#endif #include <sys/uio.h> #include <sys/ioctl.h> #include <inttypes.h> @@ -234,6 +237,149 @@ error: } +/* + * match the requested sample rate |sample_rate| against the + * sample rates supported by the audio device |dev|. Return + * a supported sample rate, it that sample rate is close to + * (< 1% difference) the requested rate; return 0 otherwise. + */ +static int +find_close_samplerate_match(int dev, int sample_rate) +{ +#if HAVE_SYS_MIXER_H + am_sample_rates_t *sr; + int i, num, err, best_err, best_rate; + + for (num = 16; num < 1024; num *= 2) { + sr = malloc(AUDIO_MIXER_SAMP_RATES_STRUCT_SIZE(num)); + if (!sr) + return 0; + sr->type = AUDIO_PLAY; + sr->flags = 0; + sr->num_samp_rates = num; + if (ioctl(dev, AUDIO_MIXER_GET_SAMPLE_RATES, sr)) { + free(sr); + return 0; + } + if (sr->num_samp_rates <= num) + break; + free(sr); + } + + if (sr->flags & MIXER_SR_LIMITS) { + /* + * HW can playback any rate between + * sr->samp_rates[0] .. sr->samp_rates[1] + */ + free(sr); + return 0; + } else { + /* HW supports fixed sample rates only */ + + best_err = 65535; + best_rate = 0; + + for (i = 0; i < sr->num_samp_rates; i++) { + err = abs(sr->samp_rates[i] - sample_rate); + if (err == 0) { + /* + * exact supported sample rate match, no need to + * retry something else + */ + best_rate = 0; + break; + } + if (err < best_err) { + best_err = err; + best_rate = sr->samp_rates[i]; + } + } + + free(sr); + + if (best_rate > 0 && 100*best_err < sample_rate) { + /* found a supported sample rate with <1% error? */ + return best_rate; + } + return 0; + } + +#else + int i, err; + int audiocs_rates[] = { + 5510, 6620, 8000, 9600, 11025, 16000, 18900, 22050, + 27420, 32000, 33075, 37800, 44100, 48000, 0 + }; + + for (i = 0; audiocs_rates[i]; i++) { + err = abs(audiocs_rates[i] - sample_rate); + if (err == 0) { + /* + * exact supported sample rate match, no need to + * retry something elise + */ + return 0; + } + if (100*err < audiocs_rates[i]) { + /* <1% error? */ + return audiocs_rates[i]; + } + } + + return 0; +#endif +} + + +/* + * return the highest sample rate supported by audio device |dev|. + */ +static int +find_highest_samplerate(int dev) +{ +#if HAVE_SYS_MIXER_H + am_sample_rates_t *sr; + int i, num, max_rate; + + for (num = 16; num < 1024; num *= 2) { + sr = malloc(AUDIO_MIXER_SAMP_RATES_STRUCT_SIZE(num)); + if (!sr) + return 0; + sr->type = AUDIO_PLAY; + sr->flags = 0; + sr->num_samp_rates = num; + if (ioctl(dev, AUDIO_MIXER_GET_SAMPLE_RATES, sr)) { + free(sr); + return 0; + } + if (sr->num_samp_rates <= num) + break; + free(sr); + } + + if (sr->flags & MIXER_SR_LIMITS) { + /* + * HW can playback any rate between + * sr->samp_rates[0] .. sr->samp_rates[1] + */ + max_rate = sr->samp_rates[1]; + } else { + /* HW supports fixed sample rates only */ + max_rate = 0; + for (i = 0; i < sr->num_samp_rates; i++) { + if (sr->samp_rates[i] > max_rate) + max_rate = sr->samp_rates[i]; + } + } + free(sr); + return max_rate; + +#else + return 44100; /* should be supported even on old ISA SB cards */ +#endif +} + + /* * open the audio device for writing to * @@ -251,6 +397,7 @@ static int ao_sun_open(xine_ao_driver_t *this_gen, { sun_driver_t *this = (sun_driver_t *) this_gen; audio_info_t info; + int pass; int ok; printf ("audio_sun_out: ao_sun_open rate=%d, mode=%d\n", rate, mode); @@ -278,7 +425,7 @@ static int ao_sun_open(xine_ao_driver_t *this_gen, this->audio_fd=open(this->audio_dev,O_WRONLY|O_NONBLOCK); if(this->audio_fd < 0) { - printf("audio_sun_out: Opening audio device %s: %s\n", + printf("audio_sun_out: Opening audio device %s failed: %s\n", this->audio_dev, strerror(errno)); return 0; } @@ -289,36 +436,75 @@ static int ao_sun_open(xine_ao_driver_t *this_gen, /* * configure audio device */ + for (ok = pass = 0; pass <= 5; pass++) { + + AUDIO_INITINFO(&info); + info.play.channels = (mode & AO_CAP_MODE_STEREO) + ? AUDIO_CHANNELS_STEREO + : AUDIO_CHANNELS_MONO; + info.play.precision = bits; + info.play.encoding = bits == 8 + ? AUDIO_ENCODING_LINEAR8 + : AUDIO_ENCODING_LINEAR; + info.play.sample_rate = this->input_sample_rate; + info.play.eof = 0; + info.play.samples = 0; + + this->convert_u8_s8 = 0; + + if (pass & 1) { + /* + * on some sun audio drivers, 8-bit unsigned LINEAR8 encoding is + * not supported, but 8-bit signed encoding is. + * + * Try S8, and if it works, use our own U8->S8 conversion before + * sending the samples to the sound driver. + */ + if (info.play.encoding != AUDIO_ENCODING_LINEAR8) + continue; + info.play.encoding = AUDIO_ENCODING_LINEAR; + this->convert_u8_s8 = 1; + } - AUDIO_INITINFO(&info); - info.play.channels = (mode & AO_CAP_MODE_STEREO) - ? AUDIO_CHANNELS_STEREO - : AUDIO_CHANNELS_MONO; - info.play.precision = bits; - info.play.encoding = bits == 8 - ? AUDIO_ENCODING_LINEAR8 - : AUDIO_ENCODING_LINEAR; - info.play.sample_rate = this->input_sample_rate; - info.play.eof = 0; - info.play.samples = 0; + if (pass & 2) { + /* + * on some sun audio drivers, only certain fixed sample rates are + * supported. + * + * In case the requested sample rate is very close to one of the + * supported rates, use the fixed supported rate instead. + * + * XXX: assuming the fixed supported rate works, should we + * lie with our return value and report the requested input + * sample rate, to avoid the software resample code? + */ + if (!(info.play.sample_rate = + find_close_samplerate_match(this->audio_fd, + this->input_sample_rate))) + continue; + } + + if (pass & 4) { + /* like "pass & 2", but use the highest supported sample rate */ + if (!(info.play.sample_rate = find_highest_samplerate(this->audio_fd))) + continue; + } + + if ((ok = ioctl(this->audio_fd, AUDIO_SETINFO, &info) >= 0)) { + /* audio format accepted by audio driver */ + break; + } - this->convert_u8_s8 = 0; - ok = ioctl(this->audio_fd, AUDIO_SETINFO, &info) >= 0; - if (!ok && info.play.encoding == AUDIO_ENCODING_LINEAR8) { /* - * Unsigned AUDIO_ENCODING_LINEAR8 not supported. - * Maybe signed AUDIO_ENCODING_LINEAR works? + * format not supported? + * retry with different encoding and/or sample rate */ - info.play.encoding = AUDIO_ENCODING_LINEAR; - ok = ioctl(this->audio_fd, AUDIO_SETINFO, &info) >= 0; - if (ok) this->convert_u8_s8 = 1; } if (!ok) { printf("audio_sun_out: Cannot configure audio device for " - "%dhz, %d channel, %d bits\n", - info.play.sample_rate, info.play.channels, - info.play.precision); + "%dhz, %d channel, %d bits: %s\n", + rate, info.play.channels, bits, strerror(errno)); close(this->audio_fd); this->audio_fd = -1; return 0; @@ -715,6 +901,9 @@ static void *init_audio_out_plugin (xine_t *xine, void *data) { this->capabilities |= AO_CAP_MODE_STEREO; printf ("stereo "); + this->capabilities |= AO_CAP_8BITS; + printf ("8bit "); + this->capabilities |= AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL; printf ("\n"); |