This is update audacious to "recent" sndio changes, and few other tweaks:
- stop audio during pause to consume less cpu (accounted as sndio and interrupt cpu usage). - use sio_onvol() callback so get the volume, this way audacious can see volume changes from other programs. - remove checks for negative values passed to sio_onmove() callback. The api was simplified so this isn't needed anymore - make sndio backend the default pluggin on openbsd - add 24-bit encondings Above bring new problems though. Audacious is multi-threaded with respect to audio, write_audio() is called in the "audio thread", while pause(), flush() and set_volume() are called from another thread. Sndio requires calls to all sio_xxx functions to be serialized. AFAICS the easier is to postpone execution of pause(), flush() and set_volume() to the end of write_audio(), unless the later in not running. Comments? Ok? -- Alexandre Index: Makefile =================================================================== RCS file: /cvs/ports/audio/audacious-plugins/Makefile,v retrieving revision 1.39 diff -u -p -r1.39 Makefile --- Makefile 17 Oct 2011 10:38:41 -0000 1.39 +++ Makefile 31 Jan 2012 08:59:27 -0000 @@ -8,7 +8,7 @@ COMMENT-jack = jack plugin for audaciou V = 2.4.5 DISTNAME = audacious-plugins-$V PKGNAME-main = ${DISTNAME} -REVISION-main = 3 +REVISION-main = 4 PKGNAME-jack = audacious-jack-$V REVISION-jack = 2 Index: files/sndio.c =================================================================== RCS file: /cvs/ports/audio/audacious-plugins/files/sndio.c,v retrieving revision 1.2 diff -u -p -r1.2 sndio.c --- files/sndio.c 19 Dec 2010 18:19:56 -0000 1.2 +++ files/sndio.c 31 Jan 2012 08:59:27 -0000 @@ -1,5 +1,6 @@ /* - * Copyright (c) 2008,2009 Thomas Pfaff <tpf...@tp76.info> + * Copyright (c) 2008, 2009 Thomas Pfaff <tpf...@tp76.info> + * Copyright (c) 2012 Alexandre Ratchov <a...@caoua.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,22 +15,24 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sndio.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - +#include <pthread.h> #include <gtk/gtk.h> #include <libmcs/mcs.h> -#include <sndio.h> +#include <libaudgui/libaudgui.h> +#include <libaudgui/libaudgui-gtk.h> +#include <audacious/audconfig.h> #include <audacious/configdb.h> #include <audacious/i18n.h> #include <audacious/plugin.h> -#include <libaudgui/libaudgui.h> -#include <libaudgui/libaudgui-gtk.h> #include "config.h" OutputPluginInitStatus sndio_init(void); +void sndio_cleanup(void); void sndio_about(void); void sndio_configure(void); void sndio_get_volume(gint *, gint *); @@ -39,12 +42,12 @@ void sndio_write(gpointer, gint); void sndio_close(void); void sndio_flush(gint); void sndio_pause(gshort); -gint sndio_free(void); -gint sndio_playing(void); gint sndio_output_time(void); gint sndio_written_time(void); +void sndio_drain(void); void onmove_cb(void *, int); +void onvol_cb(void *, unsigned); void configure_win_ok_cb(GtkWidget *, gpointer); @@ -52,9 +55,11 @@ static struct sio_par par; static struct sio_hdl *hdl; static long long rdpos; static long long wrpos; -static int paused; -static int volume; -static long bytes_per_sec; +static int paused, flushed, volume; +static int flush_time, pause_flag, volume_target; +static int writing, pause_pending, flush_pending, volume_pending; +static int bytes_per_sec; +static pthread_mutex_t mtx; static GtkWidget *configure_win; static GtkWidget *adevice_entry; @@ -63,9 +68,10 @@ static gchar *audiodev; OutputPlugin sndio_op = { .description = "Sndio Output Plugin", .init = sndio_init, - .cleanup = NULL, + .cleanup = sndio_cleanup, .about = sndio_about, .configure = sndio_configure, + .probe_priority = 2, .get_volume = sndio_get_volume, .set_volume = sndio_set_volume, .open_audio = sndio_open, @@ -73,16 +79,74 @@ OutputPlugin sndio_op = { .close_audio = sndio_close, .flush = sndio_flush, .pause = sndio_pause, - .buffer_free = sndio_free, - .buffer_playing = sndio_playing, .output_time = sndio_output_time, - .written_time = sndio_written_time + .written_time = sndio_written_time, + .drain = sndio_drain }; OutputPlugin *sndio_oplist[] = { &sndio_op, NULL }; SIMPLE_OUTPUT_PLUGIN(sndio, sndio_oplist); +static struct fmt_to_par { + int fmt, bits, sig, le; +} fmt_to_par[] = { + {FMT_S8, 8, 1, 0}, {FMT_U8, 8, 1, 0}, + {FMT_S16_LE, 16, 1, 1}, {FMT_S16_BE, 16, 1, 0}, + {FMT_U16_LE, 16, 0, 1}, {FMT_U16_BE, 16, 0, 0}, + {FMT_S24_LE, 24, 1, 1}, {FMT_S24_BE, 24, 1, 0}, + {FMT_U24_LE, 24, 0, 1}, {FMT_U24_BE, 24, 0, 0}, + {FMT_S32_LE, 32, 1, 1}, {FMT_S32_BE, 32, 1, 0}, + {FMT_U32_LE, 32, 0, 1}, {FMT_U32_BE, 32, 0, 0} +}; + +static void +volume_do(int v) +{ + if (writing) { + volume_target = v; + volume_pending = 1; + } else { + if (hdl) + sio_setvol(hdl, v * SIO_MAXVOL / 100); + volume_pending = 0; + } +} + +static void +pause_do(int flag) +{ + if (writing) { + pause_flag = flag; + pause_pending = 1; + } else { + if (flag && !paused && !flushed) { + sio_stop(hdl); + sio_start(hdl); + rdpos = wrpos; + } + paused = flag; + pause_pending = 0; + } +} + +static void +flush_do(int time) +{ + if (writing) { + flush_time = time; + flush_pending = 1; + } else { + if (!paused && !flushed) { + sio_stop(hdl); + sio_start(hdl); + } + rdpos = wrpos = (long long)time * bytes_per_sec / 1000; + flush_pending = 0; + flushed = 1; + } +} + void sndio_about(void) { @@ -99,144 +163,151 @@ sndio_init(void) { mcs_handle_t *cfgfile; - cfgfile = aud_cfg_db_open(); - aud_cfg_db_get_int(cfgfile, "sndio", "volume", &volume); - aud_cfg_db_get_string(cfgfile, "sndio", "audiodev", &audiodev); - aud_cfg_db_close(cfgfile); + pthread_mutex_init(&mtx, NULL); - if (!volume) + cfgfile = aud_cfg_db_open(); + if (!aud_cfg_db_get_int(cfgfile, "sndio", "volume", &volume)) volume = 100; - if (!audiodev) + if (!aud_cfg_db_get_string(cfgfile, "sndio", "audiodev", &audiodev)) audiodev = g_strdup(""); + aud_cfg_db_close(cfgfile); return (OUTPUT_PLUGIN_INIT_FOUND_DEVICES); } void +sndio_cleanup(void) +{ + mcs_handle_t *cfgfile; + + cfgfile = aud_cfg_db_open(); + aud_cfg_db_set_int(cfgfile, "sndio", "volume", volume); + aud_cfg_db_set_string(cfgfile, "sndio", "audiodev", audiodev); + aud_cfg_db_close(cfgfile); + pthread_mutex_destroy(&mtx); +} + +void sndio_get_volume(gint *l, gint *r) { + pthread_mutex_lock(&mtx); *l = *r = volume; + pthread_mutex_unlock(&mtx); } void sndio_set_volume(gint l, gint r) { /* Ignore balance control, so use unattenuated channel. */ + pthread_mutex_lock(&mtx); volume = l > r ? l : r; - if (hdl) - sio_setvol(hdl, volume * SIO_MAXVOL / 100); + volume_do(volume); + pthread_mutex_unlock(&mtx); } gint sndio_open(gint fmt, gint rate, gint nch) { + int i; struct sio_par askpar; + GtkWidget *dialog = NULL; hdl = sio_open(strlen(audiodev) > 0 ? audiodev : NULL, SIO_PLAY, 0); if (!hdl) { g_warning("failed to open audio device %s", audiodev); return (0); } - - sio_initpar(&par); - switch (fmt) { - case FMT_U8: - par.bits = 8; - par.sig = 0; - break; - case FMT_S8: - par.bits = 8; - par.sig = 1; - break; - case FMT_U16_LE: - par.bits = 16; - par.sig = 0; - par.le = 1; - break; - case FMT_U16_BE: - par.bits = 16; - par.sig = 0; - par.le = 0; - break; - case FMT_S16_LE: - par.bits = 16; - par.sig = 1; - par.le = 1; - break; - case FMT_S16_BE: - par.bits = 16; - par.sig = 1; - par.le = 0; - default: - g_warning("unknown format %d requested", fmt); - sndio_close(); - return (0); + sio_initpar(&askpar); + for (i = 0; ; i++) { + if (i == sizeof(fmt_to_par) / sizeof(struct fmt_to_par)) { + g_warning("unknown format %d requested", fmt); + sndio_close(); + return 0; + } + if (fmt_to_par[i].fmt == fmt) + break; } - par.pchan = nch; - par.rate = rate; - - /* 250 ms buffer */ - par.appbufsz = par.rate / 4; - - askpar = par; - if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) { + askpar.bits = fmt_to_par[i].bits; + askpar.bps = SIO_BPS(askpar.bits); + askpar.sig = fmt_to_par[i].sig; + if (askpar.bits > 8) + askpar.le = fmt_to_par[i].le; + askpar.pchan = nch; + askpar.rate = rate; + askpar.appbufsz = aud_cfg->output_buffer_size; + if (!sio_setpar(hdl, &askpar) || !sio_getpar(hdl, &par)) { g_warning("failed to set parameters"); sndio_close(); return (0); } - - if ((par.bits == 16 && par.le != askpar.le) || - par.bits != askpar.bits || + if ((par.bps > 1 && par.le != askpar.le) || + (par.bits < par.bps * 8 && !par.msb) || + par.bps != askpar.bps || par.sig != askpar.sig || par.pchan != askpar.pchan || par.rate != askpar.rate) { - GtkWidget *dialog = NULL; - g_warning("parameters not supported"); + g_warning("parameters not supported by the audio device"); audgui_simple_message(&dialog, GTK_MESSAGE_INFO, _("Unsupported format"), _("A format not supported by the audio device " "was requested.\n\n" - "Please try again with the aucat(1) server running.")); + "Please try again with the sndiod(1) server running.")); sndio_close(); return (0); } - rdpos = 0; wrpos = 0; sio_onmove(hdl, onmove_cb, NULL); - - paused = 0; + sio_onvol(hdl, onvol_cb, NULL); + volume_do(volume); if (!sio_start(hdl)) { g_warning("failed to start audio device"); sndio_close(); return (0); } - + pause_pending = flush_pending = volume_pending = 0; bytes_per_sec = par.bps * par.pchan * par.rate; - - sndio_set_volume(volume, volume); + flushed = 1; + paused = 0; return (1); } void sndio_write(gpointer ptr, gint length) { - if (!paused) - wrpos += sio_write(hdl, ptr, length); + unsigned n; + + pthread_mutex_lock(&mtx); + flushed = 0; + if (!paused) { + writing = 1; + pthread_mutex_unlock(&mtx); + n = sio_write(hdl, ptr, length); + pthread_mutex_lock(&mtx); + writing = 0; + wrpos += n; + } + for (;;) { + if (volume_pending) + volume_do(volume); + if (flush_pending) + flush_do(flush_time); + if (pause_pending) + pause_do(pause_flag); + if (flushed || !paused) + break; + pthread_mutex_unlock(&mtx); + usleep(10000); + pthread_mutex_lock(&mtx); + } + pthread_mutex_unlock(&mtx); } void sndio_close(void) { - mcs_handle_t *cfgfile; - if (!hdl) return; - - cfgfile = aud_cfg_db_open(); - aud_cfg_db_set_int(cfgfile, "sndio", "volume", volume); - aud_cfg_db_close(cfgfile); - sio_close(hdl); hdl = NULL; } @@ -244,45 +315,63 @@ sndio_close(void) void sndio_flush(gint time) { - int bufused = (rdpos < 0) ? wrpos : wrpos - rdpos; - rdpos = time / 1000 * bytes_per_sec; - wrpos = rdpos + bufused; + pthread_mutex_lock(&mtx); + flush_do(time); + pthread_mutex_unlock(&mtx); } void sndio_pause(gshort flag) -{ - paused = flag; -} - -gint -sndio_free(void) -{ - return (paused ? 0 : 1000000); +{ + pthread_mutex_lock(&mtx); + pause_do(flag); + pthread_mutex_unlock(&mtx); } -gint -sndio_playing(void) +void +sndio_drain(void) { - return (paused ? TRUE : FALSE); + /* sndio always drains */ } gint sndio_output_time(void) { - return (hdl ? rdpos * 1000 / bytes_per_sec : 0); + int time; + + pthread_mutex_lock(&mtx); + time = rdpos * 1000 / bytes_per_sec; + pthread_mutex_unlock(&mtx); + return time; } gint sndio_written_time(void) { - return (hdl ? wrpos * 1000 / bytes_per_sec : 0); + int time; + + pthread_mutex_lock(&mtx); + time = wrpos * 1000 / bytes_per_sec; + pthread_mutex_unlock(&mtx); + return time; } void onmove_cb(void *addr, int delta) { - rdpos += delta *(int)(par.bps * par.pchan); + pthread_mutex_lock(&mtx); + rdpos += delta * (int)(par.bps * par.pchan); + pthread_mutex_unlock(&mtx); +} + +void +onvol_cb(void *addr, unsigned ctl) +{ + /* Update volume only if it actually changed */ + pthread_mutex_lock(&mtx); + if (ctl != volume * SIO_MAXVOL / 100) + volume = ctl * 100 / SIO_MAXVOL; + pthread_mutex_unlock(&mtx); } void