diff --git a/audio_alsa.c b/audio_alsa.c index 9939bc74..471b3475 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -181,7 +181,7 @@ static void deinit(void) { } } -void open_alsa_device(void) { +int open_alsa_device(void) { int ret, dir = 0; unsigned int my_sample_rate = desired_sample_rate; @@ -189,7 +189,8 @@ void open_alsa_device(void) { snd_pcm_uframes_t buffer_size = frames*4; ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0); if (ret < 0) - die("Alsa initialization failed: unable to open pcm device: %s.", snd_strerror(ret)); + return(ret); + // die("Alsa initialization failed: unable to open pcm device: %s.", snd_strerror(ret)); snd_pcm_hw_params_alloca(&alsa_params); snd_pcm_hw_params_any(alsa_handle, alsa_params); @@ -206,6 +207,7 @@ void open_alsa_device(void) { if (my_sample_rate!=desired_sample_rate) { die("Can't set the D/A converter to %d -- set to %d instead./n",desired_sample_rate,my_sample_rate); } + return(0); } void close_alsa_device(void) { @@ -220,7 +222,6 @@ static void start(int sample_rate) { if (sample_rate != 44100) die("Unexpected sample rate %d -- only 44,100 supported!",sample_rate); desired_sample_rate = sample_rate; // must be a variable - // open_alsa_device(); } static uint32_t delay() { @@ -259,25 +260,28 @@ static uint32_t delay() { } static void play(short buf[], int samples) { + int ret = 0; if (alsa_handle==NULL) - open_alsa_device(); - pthread_mutex_lock(&alsa_mutex); - snd_pcm_sframes_t current_delay = 0; - int err,ignore; - if ((snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) || (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING)) { - err = snd_pcm_writei(alsa_handle, (char*)buf, samples); - if (err < 0) { - ignore = snd_pcm_recover(alsa_handle, err, 0); - debug(1,"Error %d writing %d samples in play() %s.",err,samples, snd_strerror(err)); - } - } else { - debug(1,"Error -- ALSA device in incorrect state (%d) for play.",snd_pcm_state(alsa_handle)); - if (err = snd_pcm_prepare(alsa_handle)) { - ignore = snd_pcm_recover(alsa_handle, err, 0); - debug(1,"Error preparing after play error: %s.", snd_strerror(err)); + ret = open_alsa_device(); + if (ret==0) { + pthread_mutex_lock(&alsa_mutex); + snd_pcm_sframes_t current_delay = 0; + int err,ignore; + if ((snd_pcm_state(alsa_handle)==SND_PCM_STATE_PREPARED) || (snd_pcm_state(alsa_handle)==SND_PCM_STATE_RUNNING)) { + err = snd_pcm_writei(alsa_handle, (char*)buf, samples); + if (err < 0) { + ignore = snd_pcm_recover(alsa_handle, err, 0); + debug(1,"Error %d writing %d samples in play() %s.",err,samples, snd_strerror(err)); + } + } else { + debug(1,"Error -- ALSA device in incorrect state (%d) for play.",snd_pcm_state(alsa_handle)); + if (err = snd_pcm_prepare(alsa_handle)) { + ignore = snd_pcm_recover(alsa_handle, err, 0); + debug(1,"Error preparing after play error: %s.", snd_strerror(err)); + } } + pthread_mutex_unlock(&alsa_mutex); } - pthread_mutex_unlock(&alsa_mutex); } static void flush(void) { diff --git a/common.c b/common.c index b1344834..761a6379 100644 --- a/common.c +++ b/common.c @@ -69,11 +69,23 @@ #include "common.h" #include +//true if Shairport Sync is supposed to be sending output to the output device, false otherwise + +static volatile int requested_connection_state_to_output = 1; shairport_cfg config; int debuglev = 0; +int get_requested_connection_state_to_output() { + return requested_connection_state_to_output; +} + +void set_requested_connection_state_to_output(int v) { + requested_connection_state_to_output = v; +} + + void die(char *format, ...) { char s[1024]; s[0]=0; diff --git a/common.h b/common.h index e6084698..ddbd1b54 100644 --- a/common.h +++ b/common.h @@ -63,6 +63,12 @@ typedef struct { char *errfile; } shairport_cfg; +//true if Shairport Sync is supposed to be sending output to the output device, false otherwise + +int get_requested_connection_state_to_output(); + +void set_requested_connection_state_to_output(int v); + int debuglev; void die(char *format, ...); void warn(char *format, ...); diff --git a/player.c b/player.c index d7b7ddd1..a60557b2 100644 --- a/player.c +++ b/player.c @@ -82,6 +82,8 @@ static aes_context dctx; static pthread_t player_thread; static int please_stop; +static int connection_state_to_output; // if true, then play incoming stuff; if false drop everything + static alac_file *decoder_info; // debug variables @@ -296,72 +298,75 @@ static void free_buffer(void) { void player_put_packet(seq_t seqno,uint32_t timestamp, uint8_t *data, int len) { packet_count++; + time_of_last_audio_packet = get_absolute_time_in_fp(); pthread_mutex_lock(&ab_mutex); - if ((flush_rtp_timestamp!=0x7fffffff) && ((timestamp==flush_rtp_timestamp) || seq32_order(timestamp,flush_rtp_timestamp))) { - debug(2,"Dropping flushed packet in player_put_packet, seqno %u, timestamp %u, flushing to timestamp: %u.",seqno,timestamp,flush_rtp_timestamp); - } else { - if ((flush_rtp_timestamp!=0x7fffffff) && (!seq32_order(timestamp,flush_rtp_timestamp))) // if we have gone past the flush boundary time - flush_rtp_timestamp=0x7fffffff; + if (connection_state_to_output) { // if we are supposed to be processing these packets + + if ((flush_rtp_timestamp!=0x7fffffff) && ((timestamp==flush_rtp_timestamp) || seq32_order(timestamp,flush_rtp_timestamp))) { + debug(2,"Dropping flushed packet in player_put_packet, seqno %u, timestamp %u, flushing to timestamp: %u.",seqno,timestamp,flush_rtp_timestamp); + } else { + if ((flush_rtp_timestamp!=0x7fffffff) && (!seq32_order(timestamp,flush_rtp_timestamp))) // if we have gone past the flush boundary time + flush_rtp_timestamp=0x7fffffff; - abuf_t *abuf = 0; + abuf_t *abuf = 0; - if (!ab_synced) { - debug(2, "syncing to seqno %u.", seqno); - ab_write = seqno; - ab_read = seqno; - ab_synced = 1; - } - if (ab_write == seqno) { // expected packet - abuf = audio_buffer + BUFIDX(seqno); - ab_write = SUCCESSOR(seqno); - } else if (seq_order(ab_write, seqno)) { // newer than expected - //if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8) - // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and seqno: %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno); - int32_t gap = seq_diff(ab_write,PREDECESSOR(seqno))+1; - if (gap<=0) - debug(1,"Unexpected gap size: %d.",gap); - int i; - for (i=0;iready = 0; // to be sure, to be sure - abuf->timestamp = 0; - abuf->sequence_number = 0; - } - // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write); - abuf = audio_buffer + BUFIDX(seqno); - rtp_request_resend(ab_write,gap); - resend_requests++; - ab_write = SUCCESSOR(seqno); - } else if (seq_order(ab_read, seqno)) { // late but not yet played - late_packets++; - abuf = audio_buffer + BUFIDX(seqno); - } else { // too late. - too_late_packets++; - /* - if (!late_packet_message_sent) { - debug(1, "too-late packet received: %u; ab_read: %u; ab_write: %u.", seqno, ab_read, ab_write); - late_packet_message_sent=1; - } - */ - } - // pthread_mutex_unlock(&ab_mutex); + if (!ab_synced) { + debug(2, "syncing to seqno %u.", seqno); + ab_write = seqno; + ab_read = seqno; + ab_synced = 1; + } + if (ab_write == seqno) { // expected packet + abuf = audio_buffer + BUFIDX(seqno); + ab_write = SUCCESSOR(seqno); + } else if (seq_order(ab_write, seqno)) { // newer than expected + //if (ORDINATE(seqno)>(BUFFER_FRAMES*7)/8) + // debug(1,"An interval of %u frames has opened, with ab_read: %u, ab_write: %u and seqno: %u.",seq_diff(ab_read,seqno),ab_read,ab_write,seqno); + int32_t gap = seq_diff(ab_write,PREDECESSOR(seqno))+1; + if (gap<=0) + debug(1,"Unexpected gap size: %d.",gap); + int i; + for (i=0;iready = 0; // to be sure, to be sure + abuf->timestamp = 0; + abuf->sequence_number = 0; + } + // debug(1,"N %d s %u.",seq_diff(ab_write,PREDECESSOR(seqno))+1,ab_write); + abuf = audio_buffer + BUFIDX(seqno); + rtp_request_resend(ab_write,gap); + resend_requests++; + ab_write = SUCCESSOR(seqno); + } else if (seq_order(ab_read, seqno)) { // late but not yet played + late_packets++; + abuf = audio_buffer + BUFIDX(seqno); + } else { // too late. + too_late_packets++; + /* + if (!late_packet_message_sent) { + debug(1, "too-late packet received: %u; ab_read: %u; ab_write: %u.", seqno, ab_read, ab_write); + late_packet_message_sent=1; + } + */ + } + // pthread_mutex_unlock(&ab_mutex); - if (abuf) { - alac_decode(abuf->data, data, len); - abuf->ready = 1; - abuf->timestamp = timestamp; - abuf->sequence_number = seqno; - } + if (abuf) { + alac_decode(abuf->data, data, len); + abuf->ready = 1; + abuf->timestamp = timestamp; + abuf->sequence_number = seqno; + } - // pthread_mutex_lock(&ab_mutex); + // pthread_mutex_lock(&ab_mutex); - time_of_last_audio_packet = get_absolute_time_in_fp(); - } - int rc = pthread_cond_signal(&flowcontrol); - if (rc) - debug(1,"Error signalling flowcontrol."); + } + int rc = pthread_cond_signal(&flowcontrol); + if (rc) + debug(1,"Error signalling flowcontrol."); + } pthread_mutex_unlock(&ab_mutex); } @@ -411,7 +416,18 @@ static abuf_t *buffer_get_frame(void) { shutdown_requested=1; } } - + int rco = get_requested_connection_state_to_output(); + + if (connection_state_to_output != rco) { + connection_state_to_output=rco; + // change happening + if (connection_state_to_output==0) { //going off + pthread_mutex_lock(&flush_mutex); + flush_requested=1; + pthread_mutex_unlock(&flush_mutex); + } + } + pthread_mutex_lock(&flush_mutex); if (flush_requested==1) { if (config.output->flush) @@ -750,6 +766,8 @@ typedef struct stats { // statistics for running averages } stats_t; static void *player_thread_func(void *arg) { + connection_state_to_output = get_requested_connection_state_to_output(); + debug(1,"States: %d, %d.",connection_state_to_output,get_requested_connection_state_to_output()); //this is about half a minute #define trend_interval 3758 stats_t statistics[trend_interval]; diff --git a/shairport.c b/shairport.c index 654331cd..031da8d9 100644 --- a/shairport.c +++ b/shairport.c @@ -95,6 +95,13 @@ static void sig_logrotate(int foo, siginfo_t *bar, void *baz) { // log_setup(); } +static void sig_toggle_audio_output(int foo, siginfo_t *bar, void *baz) { + if (get_requested_connection_state_to_output()) + set_requested_connection_state_to_output(0); + else + set_requested_connection_state_to_output(1); +} + static void sig_pause_client(int foo, siginfo_t *bar, void *baz) { rtp_request_client_pause(); } @@ -306,7 +313,7 @@ void signal_setup(void) { sa.sa_sigaction = &sig_logrotate; sigaction(SIGHUP, &sa, NULL); - sa.sa_sigaction = &sig_pause_client; + sa.sa_sigaction = &sig_toggle_audio_output; sigaction(SIGUSR2, &sa, NULL); sa.sa_sigaction = &sig_child; @@ -426,6 +433,7 @@ int main(int argc, char **argv) { gethostname(hostname, 100); config.apname = malloc(20 + 100); snprintf(config.apname, 20 + 100, "Shairport Sync on %s", hostname); + set_requested_connection_state_to_output(1); // we expect to be able to connect to the output device // parse arguments into config int audio_arg = parse_options(argc, argv);