/* * sndio: sndio audio output * * Copyright (c) 2008 Christian Weisgerber , * Alexandre Ratchov * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "out123_int.h" #include #include "debug.h" static int sndio_to_mpg123_enc(int sign, int bits) { switch(bits) { case 8: return sign ? MPG123_ENC_SIGNED_8 : MPG123_ENC_UNSIGNED_8; case 16: return sign ? MPG123_ENC_SIGNED_16 : MPG123_ENC_UNSIGNED_16; case 24: return sign ? MPG123_ENC_SIGNED_24 : MPG123_ENC_UNSIGNED_24; case 32: return sign ? MPG123_ENC_SIGNED_32 : MPG123_ENC_UNSIGNED_32; } return -1; } static double rate_diff(long a, unsigned int b) { double ar = (double)a; double br = (double)b; return ( a>b ? (ar-br)/ar : ( a <= 0 ? 1. : (br-ar)/ar ) ); } static int mpg123_to_sndio_enc(int enc, unsigned int *sig, unsigned int *bits) { switch(enc) { case -1: // Query default format only. break; case MPG123_ENC_SIGNED_32: *sig = 1; *bits = 32; break; case MPG123_ENC_UNSIGNED_32: *sig = 0; *bits = 32; break; case MPG123_ENC_SIGNED_24: *sig = 1; *bits = 24; break; case MPG123_ENC_UNSIGNED_24: *sig = 0; *bits = 24; break; case MPG123_ENC_SIGNED_16: *sig = 1; *bits = 16; break; case MPG123_ENC_UNSIGNED_16: *sig = 0; *bits = 16; break; case MPG123_ENC_UNSIGNED_8: *sig = 0; *bits = 8; break; case MPG123_ENC_SIGNED_8: *sig = 1; *bits = 8; break; default: return -1; } return 0; } // Just check if there seems some support for stereo audio, // take maximum channel count otherwise. static unsigned int guess_channels(struct sio_hdl *hdl) { struct sio_cap cap; unsigned int maxchan = 0; unsigned int stereo_mask = 0; unsigned int all_conf = 0; if(!sio_getcap(hdl, &cap)) return 0; // Zero is as good as nothing. // There is no specification of order in pchan[], So no guessing // about which index for stereo. for(int ci=0; ci maxchan) maxchan = cap.pchan[ci]; } mdebug("maximum device channels: %u\n", maxchan); return all_conf & stereo_mask ? 2 : maxchan; } static int open_sndio(out123_handle *ao) { struct sio_hdl *hdl; struct sio_par par; hdl = sio_open(ao->device /* NULL is fine */, SIO_PLAY, 0); if (hdl == NULL) { error("Got nothing from sio_open(). "); return -1; } sio_initpar(&par); par.le = SIO_LE_NATIVE; if(ao->format != -1) { mdebug("Actually opening with %d channels, rate %ld.", ao->channels, ao->rate); par.rate = ao->rate; par.pchan = ao->channels; } else { // Hack around buggy sndio versions up to 1.8.0 that fail to // neuter the default value before handing to OSS driver. // Also need to re-open, as sndio likes errors to be fatal. if(!sio_setpar(hdl, &par)) { sio_close(hdl); hdl = sio_open(ao->device, SIO_PLAY, 0); if(hdl == NULL) { error("Re-opening of device for channel guessing failed."); return -1; } par.pchan = guess_channels(hdl); } } if(mpg123_to_sndio_enc(ao->format, &par.sig, &par.bits)) { if (!AOQUIET) error1("invalid sample format %d", ao->format); sio_close(hdl); return -1; } if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par) || par.le != SIO_LE_NATIVE ) { if(!AOQUIET) error("parameter setup failure"); sio_close(hdl); return -1; } if(ao->format == -1) // Store default format. { ao->format = sndio_to_mpg123_enc(par.sig, par.bits); ao->rate = par.rate; ao->channels = par.pchan; } else { if( ao->format != sndio_to_mpg123_enc(par.sig, par.bits) || ao->channels != par.pchan || rate_diff(ao->rate, par.rate) > 0.005 ) { if(!AOQUIET) error("format not accepted as given"); sio_close(hdl); return -1; } if(!sio_start(hdl)) { if(!AOQUIET) error("cannot start"); sio_close(hdl); return -1; } } ao->userptr = hdl; return 0; } static int get_formats_sndio(out123_handle *ao) { struct sio_hdl *hdl = (struct sio_hdl *)ao->userptr; struct sio_cap cap; int fmt = 0; // Direct querying with sio_setpar()/sio_getpar is too slow, so let's // learn about the funky bitmask indexing of the capability stuff. if(!sio_getcap(hdl, &cap)) { if(!AOQUIET) error("failure getting caps"); return 0; } unsigned int rmask = 0; for(int ri=0; rirate == cap.rate[ri]) { rmask |= 1 << ri; break; } } if(!rmask) { for(int ri=0; rirate, cap.rate[ri]) <= 0.005) { rmask |= 1 << ri; break; } } } unsigned int cmask = 0; for(int ci=0; cichannels == cap.pchan[ci]) { cmask |= 1 << ci; break; } } if(!rmask || !cmask) { // no rate match, do the elaborate check. debug("cap table does not help, doing elaborate format check"); static int fmts[] = { MPG123_ENC_SIGNED_8, MPG123_ENC_UNSIGNED_8 , MPG123_ENC_SIGNED_16, MPG123_ENC_UNSIGNED_16 , MPG123_ENC_SIGNED_24, MPG123_ENC_UNSIGNED_24 , MPG123_ENC_SIGNED_32, MPG123_ENC_UNSIGNED_32 }; for(int i=0;irate; par.pchan = ao->channels; if(sio_setpar(hdl, &par) && sio_getpar(hdl, &par)) { if( par.le == SIO_LE_NATIVE && fmts[i] == sndio_to_mpg123_enc(par.sig, par.bits) && ao->channels == par.pchan && rate_diff(ao->rate, par.rate) <= 0.005 ) fmt |= fmts[i]; } } } else { int menc[SIO_NENC]; for(int ei=0; eiuserptr; int count; count = (int)sio_write(hdl, buf, len); if (count == 0 && sio_eof(hdl)) return -1; return count; } static void flush_sndio(out123_handle *ao) { return; } static int close_sndio(out123_handle *ao) { struct sio_hdl *hdl = (struct sio_hdl *)ao->userptr; if(hdl) sio_close(hdl); return 0; } static int init_sndio(out123_handle* ao) { if (ao == NULL) return -1; /* Set callbacks */ ao->open = open_sndio; ao->flush = flush_sndio; /* required */ ao->write = write_sndio; ao->get_formats = get_formats_sndio; ao->close = close_sndio; /* Success */ return 0; } /* Module information data structure */ mpg123_module_t mpg123_output_module_info = { /* api_version */ MPG123_MODULE_API_VERSION, /* name */ "sndio", /* description */ "Output audio using sndio library", /* revision */ "$Rev:$", /* handle */ NULL, /* init_output */ init_sndio, };