diff --git a/audio_sndio.c b/audio_sndio.c index a369bfb58..9fa1ea986 100644 --- a/audio_sndio.c +++ b/audio_sndio.c @@ -4,7 +4,7 @@ * Copyright (c) 2017 Tobias Kortkamp * * Modifications for audio synchronisation - * and related work, copyright (c) Mike Brady 2014 -- 2022 + * and related work, copyright (c) Mike Brady 2014 -- 2024 * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any @@ -30,6 +30,7 @@ static pthread_mutex_t sndio_mutex = PTHREAD_MUTEX_INITIALIZER; static struct sio_hdl *hdl; +static int is_running; static int framesize; static size_t played; static size_t written; @@ -58,7 +59,9 @@ static struct sndio_formats formats[] = {{"S8", SPS_FORMAT_S8, 44100, 8, 1, 1, S {"S24_3BE", SPS_FORMAT_S24_3BE, 44100, 24, 3, 1, 0}, {"S32", SPS_FORMAT_S32, 44100, 32, 4, 1, SIO_LE_NATIVE}}; -static void help() { printf(" -d output-device set the output device [default*|...]\n"); } +static void help() { + printf(" -d output-device set the output device [default|rsnd/0|rsnd/1...]\n"); +} void onmove_cb(__attribute__((unused)) void *arg, int delta) { time_of_last_onmove_cb = get_absolute_time_in_ns(); @@ -155,6 +158,7 @@ static int init(int argc, char **argv) { debug(1, "sndio: rate: %u.", par.rate); debug(1, "sndio: bits: %u.", par.bits); + is_running = 0; hdl = sio_open(devname, SIO_PLAY, 0); if (!hdl) die("sndio: cannot open audio device"); @@ -206,26 +210,15 @@ static int init(int argc, char **argv) { } static void deinit() { - // pthread_mutex_lock(&sndio_mutex); - pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - sio_close(hdl); - // pthread_mutex_unlock(&sndio_mutex); - pthread_cleanup_pop(1); // unlock the mutex -} - -static void start(__attribute__((unused)) int sample_rate, - __attribute__((unused)) int sample_format) { - // pthread_mutex_lock(&sndio_mutex); pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - at_least_one_onmove_cb_seen = 0; - // any previously-reported frame count - - if (!sio_start(hdl)) - die("sndio: unable to start"); - written = played = 0; - time_of_last_onmove_cb = 0; - at_least_one_onmove_cb_seen = 0; - // pthread_mutex_unlock(&sndio_mutex); + if (hdl != NULL) { + if (is_running != 0) { + sio_flush(hdl); + is_running = 0; + } + sio_close(hdl); + hdl = NULL; + } pthread_cleanup_pop(1); // unlock the mutex } @@ -233,10 +226,20 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type, __attribute__((unused)) uint32_t timestamp, __attribute__((unused)) uint64_t playtime) { if (frames > 0) { - // pthread_mutex_lock(&sndio_mutex); pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); + if (is_running == 0) { + if (hdl != NULL) { + if (sio_start(hdl) != 1) + debug(1, "sndio: unable to start"); + is_running = 1; + written = played = 0; + time_of_last_onmove_cb = 0; + at_least_one_onmove_cb_seen = 0; + } else { + debug(1, "sndio: output device is not open for play!"); + } + } written += sio_write(hdl, buf, frames * framesize); - // pthread_mutex_unlock(&sndio_mutex); pthread_cleanup_pop(1); // unlock the mutex } return 0; @@ -244,10 +247,17 @@ static int play(void *buf, int frames, __attribute__((unused)) int sample_type, static void stop() { pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - - if (!sio_stop(hdl)) - die("sndio: unable to stop"); - written = played = 0; + if (hdl != NULL) { + if (is_running != 0) { + if (sio_flush(hdl) != 1) + debug(1, "sndio: unable to stop"); + written = played = is_running = 0; + } else { + debug(1, "sndio: stop: not running."); + } + } else { + debug(1, "sndio: output device is not open for stop!"); + } pthread_cleanup_pop(1); // unlock the mutex } @@ -276,18 +286,34 @@ int get_delay(long *delay) { static int delay(long *delay) { int result = 0; pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - result = get_delay(delay); + if (hdl != NULL) { + if (is_running != 0) { + result = get_delay(delay); + } else { + debug(1, "sndio: output device is not open for delay!"); + if (delay != NULL) + *delay = 0; + } + } else { + debug(1, "sndio: output device is not open for delay!"); + } pthread_cleanup_pop(1); // unlock the mutex return result; } static void flush() { - // pthread_mutex_lock(&sndio_mutex); pthread_cleanup_debug_mutex_lock(&sndio_mutex, 1000, 1); - if (!sio_stop(hdl) || !sio_start(hdl)) - die("sndio: unable to flush"); - written = played = 0; - // pthread_mutex_unlock(&sndio_mutex); + if (hdl != NULL) { + if (is_running != 0) { + if (sio_flush(hdl) != 1) + debug(1, "sndio: unable to flush"); + written = played = is_running = 0; + } else { + debug(1, "sndio: flush: not running."); + } + } else { + debug(1, "sndio: output device is not open for flush!"); + } pthread_cleanup_pop(1); // unlock the mutex } @@ -296,7 +322,7 @@ audio_output audio_sndio = {.name = "sndio", .init = &init, .deinit = &deinit, .prepare = NULL, - .start = &start, + .start = NULL, .stop = &stop, .is_running = NULL, .flush = &flush, diff --git a/player.c b/player.c index 46ef100f6..11b0c5385 100644 --- a/player.c +++ b/player.c @@ -2141,9 +2141,10 @@ void *player_thread_func(void *arg) { if ((config.output->parameters == NULL) || (conn->input_bit_depth > output_bit_depth) || (config.playback_mode == ST_mono)) conn->enable_dither = 1; - - // remember, the output device may never have been initialised prior to this call - config.output->start(config.output_rate, config.output_format); // will need a corresponding stop + + // call the backend's start() function if it exists. + if (config.output->start != NULL) + config.output->start(config.output_rate, config.output_format); // we need an intermediate "transition" buffer diff --git a/scripts/shairport-sync.conf b/scripts/shairport-sync.conf index 78a79c7e1..517a75605 100644 --- a/scripts/shairport-sync.conf +++ b/scripts/shairport-sync.conf @@ -157,7 +157,7 @@ pw = // --with-sndio sndio = { -// device = "snd/0"; // optional setting to set the name of the output device. Default is the sndio system default. +// device = "default"; // optional setting to set the name of the output device, e.g. "rsnd/0", "rsnd/1", etc. // rate = 44100; // optional setting which can be 44100, 88200, 176400 or 352800, but the device must have the capability. Default is 44100. // format = "S16"; // optional setting which can be "U8", "S8", "S16", "S24", "S24_3LE", "S24_3BE" or "S32", but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor. // round = ; // advanced optional setting to set the period size near to this value