/* oss: audio output via Open Sound System copyright ?-2006 by the mpg123 project - free software under the terms of the LGPL 2.1 see COPYING and AUTHORS files in distribution or http://mpg123.org initially written by Michael Hipp */ #include "out123_int.h" #include #include #ifdef HAVE_LINUX_SOUNDCARD_H #include #endif #ifdef HAVE_SYS_SOUNDCARD_H #include #endif #ifdef HAVE_MACHINE_SOUNDCARD_H #include #endif #ifndef AFMT_S16_NE # ifdef OSS_BIG_ENDIAN # define AFMT_S16_NE AFMT_S16_BE # else # define AFMT_S16_NE AFMT_S16_LE # endif #endif #ifndef AFMT_U16_NE # ifdef OSS_BIG_ENDIAN # define AFMT_U16_NE AFMT_U16_BE # else # define AFMT_U16_NE AFMT_U16_LE # endif #endif #include "debug.h" struct oss_stuff { int fragment; /* size of one fragment */ int nfrag; /* number of fragments */ }; static int rate_best_match_oss(out123_handle *ao) { int ret,dsp_rate; if(!ao || ao->fn < 0 || ao->rate < 0) return -1; dsp_rate = ao->rate; ret = ioctl(ao->fn, SNDCTL_DSP_SPEED,&dsp_rate); if(ret < 0) return ret; ao->rate = dsp_rate; return 0; } static int set_rate_oss(out123_handle *ao) { int dsp_rate; int ret = 0; if(ao->rate >= 0) { dsp_rate = ao->rate; ret = ioctl(ao->fn, SNDCTL_DSP_SPEED,&dsp_rate); } return ret; } static int set_channels_oss(out123_handle *ao) { int chan = ao->channels - 1; int ret; if(ao->channels < 0) return 0; ret = ioctl(ao->fn, SNDCTL_DSP_STEREO, &chan); if(chan != (ao->channels-1)) return -1; return ret; } static int set_format_oss(out123_handle *ao) { int fmts; int sf,ret; if(ao->format == -1) return 0; switch(ao->format) { case MPG123_ENC_SIGNED_16: default: fmts = AFMT_S16_NE; break; case MPG123_ENC_UNSIGNED_8: fmts = AFMT_U8; break; case MPG123_ENC_SIGNED_8: fmts = AFMT_S8; break; case MPG123_ENC_ULAW_8: fmts = AFMT_MU_LAW; break; case MPG123_ENC_ALAW_8: fmts = AFMT_A_LAW; break; case MPG123_ENC_UNSIGNED_16: fmts = AFMT_U16_NE; break; } sf = fmts; ret = ioctl(ao->fn, SNDCTL_DSP_SETFMT, &fmts); if(sf != fmts) return -1; return ret; } static int reset_parameters_oss(out123_handle *ao) { int ret; ret = ioctl(ao->fn, SNDCTL_DSP_RESET, NULL); if(ret < 0 && !AOQUIET) error("Can't reset audio!"); ret = set_format_oss(ao); if (ret == -1) goto err; ret = set_channels_oss(ao); if (ret == -1) goto err; ret = set_rate_oss(ao); if (ret == -1) goto err; /* Careful here. As per OSS v1.1, the next ioctl() commits the format * set above, so we must issue SNDCTL_DSP_RESET before we're allowed to * change it again. [dk] */ /* FIXME: this needs re-enabled (but not using global variables this time): if (ioctl(ao->fn, SNDCTL_DSP_GETBLKSIZE, &outburst) == -1 || outburst > MAXOUTBURST) outburst = MAXOUTBURST; */ err: return ret; } static int open_oss(out123_handle *ao) { char usingdefdev = 0; const char *dev; if(!ao) return -1; dev = ao->device; if(!dev) { dev = "/dev/dsp"; usingdefdev = 1; } ao->fn = open(dev,O_WRONLY); if(ao->fn < 0) { if(usingdefdev) { dev = "/dev/sound/dsp"; ao->fn = open(dev,O_WRONLY); if(ao->fn < 0) { if(!AOQUIET) error("Can't open default sound device!"); return -1; } } else { if(!AOQUIET) error1("Can't open %s!",dev); return -1; } } if(reset_parameters_oss(ao) < 0) { close(ao->fn); return -1; } if(ao->gain >= 0) { int e,mask; e = ioctl(ao->fn , SOUND_MIXER_READ_DEVMASK ,&mask); if(e < 0) { if(!AOQUIET) error("audio/gain: Can't get audio device features list."); } else if(mask & SOUND_MASK_PCM) { int gain = (ao->gain<<8)|(ao->gain); e = ioctl(ao->fn, SOUND_MIXER_WRITE_PCM , &gain); } else if(!(mask & SOUND_MASK_VOLUME)) { if(!AOQUIET) error1("audio/gain: setable Volume/PCM-Level not supported by your audio device: %#04x",mask); } else { int gain = (ao->gain<<8)|(ao->gain); e = ioctl(ao->fn, SOUND_MIXER_WRITE_VOLUME , &gain); } } return ao->fn; } /* * get formats for specific channel/rate parameters */ static int get_formats_oss(out123_handle *ao) { int fmt = 0; int r = ao->rate; int c = ao->channels; int i; static int fmts[] = { MPG123_ENC_ULAW_8 , MPG123_ENC_SIGNED_16 , MPG123_ENC_UNSIGNED_8 , MPG123_ENC_SIGNED_8 , MPG123_ENC_UNSIGNED_16 , MPG123_ENC_ALAW_8 }; /* Reset is required before we're allowed to set the new formats. [dk] */ ioctl(ao->fn, SNDCTL_DSP_RESET, NULL); for(i=0;i<6;i++) { ao->format = fmts[i]; if(set_format_oss(ao) < 0) { continue; } ao->channels = c; if(set_channels_oss(ao) < 0) { continue; } ao->rate = r; if(rate_best_match_oss(ao) < 0) { continue; } if( (ao->rate*100 > r*(100-AUDIO_RATE_TOLERANCE)) && (ao->rate*100 < r*(100+AUDIO_RATE_TOLERANCE)) ) { fmt |= fmts[i]; } } #if 0 if(ioctl(ao->fn,SNDCTL_DSP_GETFMTS,&fmts) < 0) { if(!AOQUIET) error("Failed to get SNDCTL_DSP_GETFMTS"); return -1; } if(fmts & AFMT_MU_LAW) ret |= MPG123_ENC_ULAW_8; if(fmts & AFMT_S16_NE) ret |= MPG123_ENC_SIGNED_16; if(fmts & AFMT_U8) ret |= MPG123_ENC_UNSIGNED_8; if(fmts & AFMT_S8) ret |= MPG123_ENC_SIGNED_8; if(fmts & AFMT_U16_NE) ret |= MPG123_ENC_UNSIGNED_16; if(fmts & AFMT_A_LAW) ret |= MPG123_ENC_ALAW_8; #endif return fmt; } static int write_oss(out123_handle *ao,unsigned char *buf,int len) { return write(ao->fn,buf,len); } static int close_oss(out123_handle *ao) { close(ao->fn); return 0; } static void flush_oss(out123_handle *ao) { } static int init_oss(out123_handle* ao) { if (ao==NULL) return -1; /* Set callbacks */ ao->open = open_oss; ao->flush = flush_oss; ao->write = write_oss; ao->get_formats = get_formats_oss; ao->close = close_oss; /* Success */ return 0; } /* Module information data structure */ mpg123_module_t mpg123_output_module_info = { /* api_version */ MPG123_MODULE_API_VERSION, /* name */ "oss", /* description */ "Output audio using OSS", /* revision */ "$Rev: 4021 $", /* handle */ NULL, /* init_output */ init_oss, };