-// Based on
+// Based (distantly, with thanks) on
// http://stackoverflow.com/questions/29977651/how-can-the-pulseaudio-asynchronous-library-be-used-to-play-raw-pcm-data
#include "audio.h"
// default values
config.audio_backend_buffer_desired_length = 0.35;
config.audio_backend_latency_offset = 0;
+
+ // get settings from settings file first, allow them to be overridden by
+ // command line options
+
+ if (config.cfg != NULL) {
+ const char *str;
+ double dvalue;
+
+ /* Get the desired buffer size setting. */
+ if (config_lookup_float(config.cfg, "pa.audio_backend_buffer_desired_length_in_seconds",
+ &dvalue)) {
+ if ((dvalue < 0) || (dvalue > 1.5)) {
+ die("Invalid pa audio_backend_buffer_desired_length_in_seconds \"%f\". It "
+ "should be between 0 and "
+ "1.5, default is 0.35 seconds",
+ dvalue);
+ } else {
+ config.audio_backend_buffer_desired_length = dvalue;
+ }
+ }
+
+ /* Get the latency offset. */
+ if (config_lookup_float(config.cfg, "pa.audio_backend_latency_offset_in_seconds", &dvalue)) {
+ if ((dvalue < -1.0) || (dvalue > 1.5)) {
+ die("Invalid pa audio_backend_latency_offset_in_seconds \"%f\". It "
+ "should be between -1.0 and +1.5, default is 0 seconds",
+ dvalue);
+ } else {
+ config.audio_backend_latency_offset = dvalue;
+ }
+ }
+
+ /* Get the Application Name. */
+ if (config_lookup_string(config.cfg, "pa.application_name", &str)) {
+ config.pa_application_name = (char *)str;
+ }
+ }
+
+
+ // finish collecting settings
// allocate space for the audio buffer
audio_lmb = malloc(audio_size);
mainloop = pa_threaded_mainloop_new();
assert(mainloop);
mainloop_api = pa_threaded_mainloop_get_api(mainloop);
- context = pa_context_new(mainloop_api, "Shairport Sync");
+ if (config.pa_application_name)
+ context = pa_context_new(mainloop_api, config.pa_application_name);
+ else
+ context = pa_context_new(mainloop_api, "Shairport Sync");
assert(context);
// Set a callback so we can wait for the context to be ready
int64_t filler_size = max_dac_delay; // 0.1 second -- the maximum we'll add to the DAC
if (local_time_now >= conn->first_packet_time_to_play) {
+ debug(1,"Gone past starting time");
+ have_sent_prefiller_silence = 1;
+ conn->ab_buffering = 0;
+
// we've gone past the time...
// debug(1,"Run past the exact start time by %llu frames, with time now of %llx, fpttp
// of %llx and dac_delay of %d and %d packets;
// flush.",(((tn-conn->first_packet_time_to_play)*config.output_rate)>>32)+dac_delay,tn,conn->first_packet_time_to_play,dac_delay,seq_diff(ab_read,
// ab_write));
+/*
if (config.output->flush)
config.output->flush();
ab_resync(conn);
conn->first_packet_timestamp = 0;
conn->first_packet_time_to_play = 0;
conn->time_since_play_started = 0;
+*/
} else {
- // conn->first_packet_time_to_play is definitely later than local_time_now
- if ((config.output->delay) && (have_sent_prefiller_silence != 0)) {
- int resp = config.output->delay(&dac_delay);
- if (resp != 0) {
- debug(1, "Error %d getting dac_delay in buffer_get_frame.", resp);
- dac_delay = 0;
- }
- } else
- dac_delay = 0;
- int64_t gross_frame_gap =
- ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) >> 32;
- int64_t exact_frame_gap = gross_frame_gap - dac_delay;
- if (exact_frame_gap < 0) {
- // we've gone past the time...
- // debug(1,"Run a bit past the exact start time by %lld frames, with time now of
- // %llx, fpttp of %llx and dac_delay of %d and %d packets;
- // flush.",-exact_frame_gap,tn,conn->first_packet_time_to_play,dac_delay,seq_diff(ab_read,
- // ab_write));
- if (config.output->flush)
- config.output->flush();
- ab_resync(conn);
- conn->first_packet_timestamp = 0;
- conn->first_packet_time_to_play = 0;
- } else {
- int64_t fs = filler_size;
- if (fs > (max_dac_delay - dac_delay))
- fs = max_dac_delay - dac_delay;
- if (fs < 0) {
- debug(2, "frame size (fs) < 0 with max_dac_delay of %lld and dac_delay of %ld",
- max_dac_delay, dac_delay);
- fs = 0;
- }
- if ((exact_frame_gap <= fs) ||
- (exact_frame_gap <= conn->max_frames_per_packet * 2)) {
- fs = exact_frame_gap;
- // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d,
- // with %d packets, ab_read is %04x, ab_write is
- // %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read,
- // ab_write),ab_read,ab_write);
- conn->ab_buffering = 0;
- }
- signed short *silence;
- // if (fs==0)
- // debug(2,"Zero length silence buffer needed with gross_frame_gap of %lld and
- // dac_delay of %lld.",gross_frame_gap,dac_delay);
- // the fs (number of frames of silence to play) can be zero in the DAC doesn't start
- // ouotputting frames for a while -- it could get loaded up but not start responding
- // for many milliseconds.
- if (fs != 0) {
- silence = malloc(conn->output_bytes_per_frame * fs);
- if (silence == NULL)
- debug(1, "Failed to allocate %d byte silence buffer.", fs);
- else {
- memset(silence, 0, conn->output_bytes_per_frame * fs);
- // debug(1,"Frames to start: %llu, DAC delay %ld, buffer: %d
- // packets.",exact_frame_gap,dac_delay,seq_diff(conn->ab_read, conn->ab_write,
- // conn->ab_read));
- config.output->play(silence, fs);
- free(silence);
- have_sent_prefiller_silence = 1;
+ // do some calculations
+ int64_t lead_time = conn->first_packet_time_to_play-local_time_now;
+ int64_t lead_in_time = (int64_t)(config.audio_backend_silent_lead_in_time*(int64_t)0x100000000);
+ //debug(1,"Lead in time is %llx.",lead_in_time);
+ // an audio_backend_silent_lead_in_time of less than zero means start filling ASAP
+ if ((lead_in_time<0) || (lead_time<=lead_in_time)) {
+ // debug(1,"Continuing...");
+ if (config.output->delay) {
+ // conn->first_packet_time_to_play is definitely later than local_time_now
+ if (have_sent_prefiller_silence != 0) {
+ int resp = config.output->delay(&dac_delay);
+ if (resp != 0) {
+ debug(1, "Error %d getting dac_delay in buffer_get_frame.", resp);
+ dac_delay = 0;
+ }
+ } else {
+ dac_delay = 0;
+ }
+ int64_t gross_frame_gap =
+ ((conn->first_packet_time_to_play - local_time_now) * config.output_rate) >> 32;
+ int64_t exact_frame_gap = gross_frame_gap - dac_delay;
+ if (exact_frame_gap < 0) {
+ // we've gone past the time...
+ // debug(1,"Run a bit past the exact start time by %lld frames, with time now of
+ // %llx, fpttp of %llx and dac_delay of %d and %d packets;
+ // flush.",-exact_frame_gap,tn,conn->first_packet_time_to_play,dac_delay,seq_diff(ab_read,
+ // ab_write));
+ if (config.output->flush)
+ config.output->flush();
+ ab_resync(conn);
+ conn->first_packet_timestamp = 0;
+ conn->first_packet_time_to_play = 0;
+ } else {
+ int64_t fs = filler_size;
+ if (fs > (max_dac_delay - dac_delay))
+ fs = max_dac_delay - dac_delay;
+ if (fs < 0) {
+ debug(2, "frame size (fs) < 0 with max_dac_delay of %lld and dac_delay of %ld",
+ max_dac_delay, dac_delay);
+ fs = 0;
+ }
+ if ((exact_frame_gap <= fs) ||
+ (exact_frame_gap <= conn->max_frames_per_packet * 2)) {
+ fs = exact_frame_gap;
+ // debug(1,"Exact frame gap is %llu; play %d frames of silence. Dac_delay is %d,
+ // with %d packets, ab_read is %04x, ab_write is
+ // %04x.",exact_frame_gap,fs,dac_delay,seq_diff(ab_read,
+ // ab_write),ab_read,ab_write);
+ conn->ab_buffering = 0;
+ }
+ signed short *silence;
+ // if (fs==0)
+ // debug(2,"Zero length silence buffer needed with gross_frame_gap of %lld and
+ // dac_delay of %lld.",gross_frame_gap,dac_delay);
+ // the fs (number of frames of silence to play) can be zero in the DAC doesn't start
+ // ouotputting frames for a while -- it could get loaded up but not start responding
+ // for many milliseconds.
+ if (fs != 0) {
+ silence = malloc(conn->output_bytes_per_frame * fs);
+ if (silence == NULL)
+ debug(1, "Failed to allocate %d byte silence buffer.", fs);
+ else {
+ memset(silence, 0, conn->output_bytes_per_frame * fs);
+ debug(1,"Frames to start: %llu, DAC delay %ld, buffer: %d packets.",exact_frame_gap,dac_delay,seq_diff(conn->ab_read, conn->ab_write, conn->ab_read));
+ config.output->play(silence, fs);
+ free(silence);
+ have_sent_prefiller_silence = 1;
+ }
+ }
}
+ } else {
+ //no delay function on back end -- just send the prefiller silence
+ debug(1,"Back end has no delay function.");
+ // send the appropriate prefiller here...
+
+ signed short *silence;
+ if (lead_time != 0) {
+ int64_t frame_gap = (lead_time * config.output_rate) >> 32;
+ debug(1,"%d frames needed.",frame_gap);
+ while (frame_gap>0) {
+ size_t fs = config.output_rate/10;
+ if (fs>frame_gap)
+ fs = frame_gap;
+
+ silence = malloc(conn->output_bytes_per_frame * fs);
+ if (silence == NULL)
+ debug(1, "Failed to allocate %d frame silence buffer.", fs);
+ else {
+ debug(1, "Outputting %d frames of silence.", fs);
+ memset(silence, 0, conn->output_bytes_per_frame * fs);
+ config.output->play(silence, fs);
+ free(silence);
+
+ }
+ frame_gap -= fs;
+ }
+ }
+ have_sent_prefiller_silence = 1;
+ conn->ab_buffering = 0;
}
}
}
}
if (sync_error_out_of_bounds > 3) {
- // debug(1, "New lost sync with source for %d consecutive packets -- flushing and "
- // "resyncing. Error: %lld.",
- // sync_error_out_of_bounds, sync_error);
+ // debug(1, "New lost sync with source for %d consecutive packets -- flushing and "
+ // "resyncing. Error: %lld.",
+ // sync_error_out_of_bounds, sync_error);
sync_error_out_of_bounds = 0;
size_t filler_length =
sync_error_out_of_bounds++;
// debug(1,"Sync error out of bounds: Error: %lld; previous error: %lld; DAC: %lld;
// timestamp: %llx, time now
- // %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now);
+ //
+ %llx",sync_error,previous_sync_error,current_delay,inframe->timestamp,local_time_now);
if (sync_error_out_of_bounds > 3) {
debug(1, "Lost sync with source for %d consecutive packets -- flushing and "
"resyncing. Error: %lld.",