#include "dbus-service.h"
#endif
-
enum am_state state;
enum ps_state { ps_inactive, ps_active } player_state;
if (config.disable_standby_mode == disable_standby_auto) {
#ifdef CONFIG_DBUS_INTERFACE
- if (dbus_service_is_running())
- shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
- else
- config.keep_dac_busy = 1;
+ if (dbus_service_is_running())
+ shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), TRUE);
+ else
+ config.keep_dac_busy = 1;
#else
config.keep_dac_busy = 1;
#endif
if (config.disable_standby_mode == disable_standby_auto) {
#ifdef CONFIG_DBUS_INTERFACE
- if (dbus_service_is_running())
- shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE);
- else
- config.keep_dac_busy = 0;
+ if (dbus_service_is_running())
+ shairport_sync_set_disable_standby(SHAIRPORT_SYNC(shairportSyncSkeleton), FALSE);
+ else
+ config.keep_dac_busy = 0;
#else
- config.keep_dac_busy = 0;
+ config.keep_dac_busy = 0;
#endif
}
}
pthread_exit(NULL);
}
-enum am_state activity_status() {
- return (state);
-}
+enum am_state activity_status() { return (state); }
void activity_monitor_start() {
// debug(1,"activity_monitor_start");
void activity_monitor_start();
void activity_monitor_stop();
void activity_monitor_signify_activity(int active); // 0 means inactive, non-zero means active
-enum am_state activity_status(); // true if non inactive; false if inactive
+enum am_state activity_status(); // true if non inactive; false if inactive
printf("Settings and options for the audio backend \"%s\":\n", (*out)->name);
(*out)->help();
} else {
- printf("There are no settings or options for the audio backend \"%s\".\n", (*out)->name);
+ printf("There are no settings or options for the audio backend \"%s\".\n", (*out)->name);
}
}
}
int (*init)(int argc, char **argv);
// at end of program
void (*deinit)(void);
-
+
int (*prepare)(void); // looks and sets stuff in the config data structure
void (*start)(int sample_rate, int sample_format);
#include "config.h"
-#include "common.h"
#include "activity_monitor.h"
#include "audio.h"
+#include "common.h"
enum alsa_backend_mode {
abm_disconnected,
} alsa_backend_state; // under the control of alsa_mutex
typedef struct {
- snd_pcm_format_t alsa_code;
- int frame_size;
+ snd_pcm_format_t alsa_code;
+ int frame_size;
} format_record;
static void help(void);
int mute(int do_mute); // returns true if it actually is allowed to use the mute
static double set_volume;
static int output_method_signalled = 0; // for reporting whether it's using mmap or not
-int delay_type_notified = -1; // for controlling the reporting of whether the output device can do precison delays (e.g. alsa->pulsaudio virtual devices can't)
-int use_monotonic_clock = 0; // this value will be set when the hardware is initialised
+int delay_type_notified = -1; // for controlling the reporting of whether the output device can do
+ // precison delays (e.g. alsa->pulsaudio virtual devices can't)
+int use_monotonic_clock = 0; // this value will be set when the hardware is initialised
audio_output audio_alsa = {
.name = "alsa",
int alsa_device_initialised; // boolean to ensure the initialisation is only
// done once
-enum yndk_type precision_delay_available_status = YNDK_DONT_KNOW; // initially, we don't know if the device can do precision delay
-
+enum yndk_type precision_delay_available_status =
+ YNDK_DONT_KNOW; // initially, we don't know if the device can do precision delay
+
snd_pcm_t *alsa_handle = NULL;
static snd_pcm_hw_params_t *alsa_params = NULL;
static snd_pcm_sw_params_t *alsa_swparams = NULL;
0; // set when muting is being done by a setting the volume to a magic value
// use this to allow the use of snd_pcm_writei or snd_pcm_mmap_writei
-snd_pcm_sframes_t (*alsa_pcm_write)(snd_pcm_t *, const void *,
- snd_pcm_uframes_t) = snd_pcm_writei;
+snd_pcm_sframes_t (*alsa_pcm_write)(snd_pcm_t *, const void *, snd_pcm_uframes_t) = snd_pcm_writei;
+int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
+ enum yndk_type *using_update_timestamps);
+int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
+ enum yndk_type *using_update_timestamps);
-int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps);
-int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps);
-
-// use this to allow the use of standard or precision delay calculations, with standard the, uh, standard.
-int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) = standard_delay_and_status;
+// use this to allow the use of standard or precision delay calculations, with standard the, uh,
+// standard.
+int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
+ enum yndk_type *using_update_timestamps) = standard_delay_and_status;
// this will return true if the DAC can return precision delay information and false if not
// if it is not yet known, it will test the output device to find out
int precision_delay_available() {
if (precision_delay_available_status == YNDK_DONT_KNOW) {
- // this is very crude -- if the device is a hardware device, then it's assumed the delay is precise
+ // this is very crude -- if the device is a hardware device, then it's assumed the delay is
+ // precise
const char *output_device_name = snd_pcm_name(alsa_handle);
- int is_a_real_hardware_device = (strstr(output_device_name,"hw:") == output_device_name);
+ int is_a_real_hardware_device = (strstr(output_device_name, "hw:") == output_device_name);
// The criteria as to whether precision delay is available
// is whether the device driver returns non-zero update timestamps
// If it does, and the device is a hardware device (i.e. its name begins with "hw:"),
- // it is considered that precision delay is available. Otherwise, it's considered to be unavailable.
-
+ // it is considered that precision delay is available. Otherwise, it's considered to be
+ // unavailable.
+
// To test, we play a silence buffer (fairly large to avoid underflow)
- // and then we check the delay return. It will tell us if it
+ // and then we check the delay return. It will tell us if it
// was able to use the (non-zero) update timestamps
-
+
int frames_of_silence = 4410;
size_t size_of_silence_buffer = frames_of_silence * frame_size;
void *silence = malloc(size_of_silence_buffer);
// now we can get the delay, and we'll note if it uses update timestamps
enum yndk_type uses_update_timestamps;
snd_pcm_state_t state;
- snd_pcm_sframes_t delay;
+ snd_pcm_sframes_t delay;
int ret = precision_delay_and_status(&state, &delay, &uses_update_timestamps);
- // debug(3,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps);
+ // debug(3,"alsa: precision_delay_available asking for delay and status with a return status
+ // of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay,
+ // uses_update_timestamps);
if (ret == 0) {
if ((uses_update_timestamps == YNDK_YES) && (is_a_real_hardware_device)) {
precision_delay_available_status = YNDK_YES;
- debug(2,"alsa: precision delay timing is available.");
+ debug(2, "alsa: precision delay timing is available.");
} else {
if ((uses_update_timestamps == YNDK_YES) && (!is_a_real_hardware_device)) {
- debug(2,"alsa: precision delay timing is not available because it's not definitely a hardware device.");
+ debug(2, "alsa: precision delay timing is not available because it's not definitely a "
+ "hardware device.");
} else {
- debug(2,"alsa: precision delay timing is not available.");
- }
- precision_delay_available_status = YNDK_NO;
+ debug(2, "alsa: precision delay timing is not available.");
+ }
+ precision_delay_available_status = YNDK_NO;
}
}
}
return (precision_delay_available_status == YNDK_YES);
}
-
// static int play_number;
// static int64_t accumulated_delay, accumulated_da_delay;
int alsa_characteristics_already_listed = 0;
" -c mixer-control set the mixer control name, default is to use no mixer.\n"
" -m mixer-device set the mixer device, default is the output device.\n"
" -i mixer-index set the mixer index, default is 0.\n");
- int r = system("if [ -d /proc/asound ] ; then echo \" hardware output devices:\" ; ls -al /proc/asound/ 2>/dev/null | grep '\\->' | tr -s ' ' | cut -d ' ' -f 9 | while read line; do echo \" \\\"hw:$line\\\"\" ; done ; fi");
+ int r = system("if [ -d /proc/asound ] ; then echo \" hardware output devices:\" ; ls -al "
+ "/proc/asound/ 2>/dev/null | grep '\\->' | tr -s ' ' | cut -d ' ' -f 9 | while "
+ "read line; do echo \" \\\"hw:$line\\\"\" ; done ; fi");
if (r != 0)
debug(2, "error %d executing a script to list alsa hardware device names", r);
}
}
}
-// This array is a sequence of the output rates to be tried if automatic speed selection is requested.
+// This array is a sequence of the output rates to be tried if automatic speed selection is
+// requested.
// There is no benefit to upconverting the frame rate, other than for compatibility.
// The lowest rate that the DAC is capable of is chosen.
unsigned int auto_speed_output_rates[] = {
- 44100,
- 88200,
- 176400,
- 352800,
+ 44100, 88200, 176400, 352800,
};
-// This array is of all the formats known to Shairport Sync, in order of the SPS_FORMAT definitions, with their equivalent alsa codes and their frame sizes.
-// If just one format is requested, then its entry is searched for in the array and checked on the device
+// This array is of all the formats known to Shairport Sync, in order of the SPS_FORMAT definitions,
+// with their equivalent alsa codes and their frame sizes.
+// If just one format is requested, then its entry is searched for in the array and checked on the
+// device
// If auto format is requested, then each entry in turn is tried until a working format is found.
// So, it should be in the search order.
-
- format_record fr[] = {
- {SND_PCM_FORMAT_UNKNOWN,0}, // unknown
- {SND_PCM_FORMAT_S8,2},
- {SND_PCM_FORMAT_U8,2},
- {SND_PCM_FORMAT_S16,4},
- {SND_PCM_FORMAT_S16_LE,4},
- {SND_PCM_FORMAT_S16_BE,4},
- {SND_PCM_FORMAT_S24,4},
- {SND_PCM_FORMAT_S24_LE,8},
- {SND_PCM_FORMAT_S24_BE,8},
- {SND_PCM_FORMAT_S24_3LE,6},
- {SND_PCM_FORMAT_S24_3BE,6},
- {SND_PCM_FORMAT_S32,8},
- {SND_PCM_FORMAT_S32_LE,8},
- {SND_PCM_FORMAT_S32_BE,8},
- {SND_PCM_FORMAT_UNKNOWN,0}, // auto
- {SND_PCM_FORMAT_UNKNOWN,0}, // illegal
- };
-
- // This array is the sequence of formats to be tried if automatic selection of the format is requested.
- // Ideally, audio should pass through Shairport Sync unaltered, apart from occasional interpolation.
- // If the user chooses a hardware mixer, then audio could go straight through, unaltered, as signed 16 bit stereo.
- // However, the user might, at any point, select an option that requires modification, such as stereo to mono mixing,
- // additional volume attenuation, convolution, and so on. For this reason,
- // we look for the greatest depth the DAC is capable of, since upconverting it is completely lossless.
- // If audio processing is required, then the dither that must be added will
- // be added at the lowest possible level.
- // Hence, selecting the greatest bit depth is always either beneficial or neutral.
-
- enum sps_format_t auto_format_check_sequence[] = {
- SPS_FORMAT_S32,
- SPS_FORMAT_S32_LE,
- SPS_FORMAT_S32_BE,
- SPS_FORMAT_S24,
- SPS_FORMAT_S24_LE,
- SPS_FORMAT_S24_BE,
- SPS_FORMAT_S24_3LE,
- SPS_FORMAT_S24_3BE,
- SPS_FORMAT_S16,
- SPS_FORMAT_S16_LE,
- SPS_FORMAT_S16_BE,
- SPS_FORMAT_S8,
- SPS_FORMAT_U8,
- };
+
+format_record fr[] = {
+ {SND_PCM_FORMAT_UNKNOWN, 0}, // unknown
+ {SND_PCM_FORMAT_S8, 2}, {SND_PCM_FORMAT_U8, 2}, {SND_PCM_FORMAT_S16, 4},
+ {SND_PCM_FORMAT_S16_LE, 4}, {SND_PCM_FORMAT_S16_BE, 4}, {SND_PCM_FORMAT_S24, 4},
+ {SND_PCM_FORMAT_S24_LE, 8}, {SND_PCM_FORMAT_S24_BE, 8}, {SND_PCM_FORMAT_S24_3LE, 6},
+ {SND_PCM_FORMAT_S24_3BE, 6}, {SND_PCM_FORMAT_S32, 8}, {SND_PCM_FORMAT_S32_LE, 8},
+ {SND_PCM_FORMAT_S32_BE, 8}, {SND_PCM_FORMAT_UNKNOWN, 0}, // auto
+ {SND_PCM_FORMAT_UNKNOWN, 0}, // illegal
+};
+
+// This array is the sequence of formats to be tried if automatic selection of the format is
+// requested.
+// Ideally, audio should pass through Shairport Sync unaltered, apart from occasional interpolation.
+// If the user chooses a hardware mixer, then audio could go straight through, unaltered, as signed
+// 16 bit stereo.
+// However, the user might, at any point, select an option that requires modification, such as
+// stereo to mono mixing,
+// additional volume attenuation, convolution, and so on. For this reason,
+// we look for the greatest depth the DAC is capable of, since upconverting it is completely
+// lossless.
+// If audio processing is required, then the dither that must be added will
+// be added at the lowest possible level.
+// Hence, selecting the greatest bit depth is always either beneficial or neutral.
+
+enum sps_format_t auto_format_check_sequence[] = {
+ SPS_FORMAT_S32, SPS_FORMAT_S32_LE, SPS_FORMAT_S32_BE, SPS_FORMAT_S24, SPS_FORMAT_S24_LE,
+ SPS_FORMAT_S24_BE, SPS_FORMAT_S24_3LE, SPS_FORMAT_S24_3BE, SPS_FORMAT_S16, SPS_FORMAT_S16_LE,
+ SPS_FORMAT_S16_BE, SPS_FORMAT_S8, SPS_FORMAT_U8,
+};
// assuming pthread cancellation is disabled
// if do_auto_setting is true and auto format or auto speed has been requested,
*/
int ret, dir = 0;
- unsigned int actual_sample_rate; // this will be given the rate requested and will be given the actual rate
+ unsigned int
+ actual_sample_rate; // this will be given the rate requested and will be given the actual rate
// snd_pcm_uframes_t frames = 441 * 10;
snd_pcm_uframes_t actual_buffer_length;
snd_pcm_access_t access;
} else {
char errorstring[1024];
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
- warn("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring, alsa_out_dev);
+ warn("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring,
+ alsa_out_dev);
}
return ret;
}
ret = snd_pcm_hw_params_any(alsa_handle, alsa_params);
if (ret < 0) {
die("audio_alsa: Broken configuration for device \"%s\": no configurations "
- "available",
- alsa_out_dev);
+ "available",
+ alsa_out_dev);
return ret;
}
snd_strerror(ret));
return ret;
}
-
ret = snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, 2);
if (ret < 0) {
}
snd_pcm_format_t sf;
-
+
if ((do_auto_setup == 0) || (config.output_format_auto_requested == 0)) { // no auto format
- if ((config.output_format > SPS_FORMAT_UNKNOWN) && (config.output_format < SPS_FORMAT_AUTO)) {
- sf = fr[config.output_format].alsa_code;
- frame_size = fr[config.output_format].frame_size;
- } else {
- warn("alsa: unexpected output format %d. Set to S16_LE.",config.output_format);
- config.output_format = SPS_FORMAT_S16_LE;
- sf = fr[config.output_format].alsa_code;
- frame_size = fr[config.output_format].frame_size;
- }
- ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf);
- if (ret < 0) {
- warn("audio_alsa: Alsa sample format %d not available for device \"%s\": %s", sf,
- alsa_out_dev, snd_strerror(ret));
- return ret;
- }
+ if ((config.output_format > SPS_FORMAT_UNKNOWN) && (config.output_format < SPS_FORMAT_AUTO)) {
+ sf = fr[config.output_format].alsa_code;
+ frame_size = fr[config.output_format].frame_size;
+ } else {
+ warn("alsa: unexpected output format %d. Set to S16_LE.", config.output_format);
+ config.output_format = SPS_FORMAT_S16_LE;
+ sf = fr[config.output_format].alsa_code;
+ frame_size = fr[config.output_format].frame_size;
+ }
+ ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf);
+ if (ret < 0) {
+ warn("audio_alsa: Alsa sample format %d not available for device \"%s\": %s", sf,
+ alsa_out_dev, snd_strerror(ret));
+ return ret;
+ }
} else { // auto format
- int number_of_formats_to_try;
- enum sps_format_t *formats;
- formats = auto_format_check_sequence;
- number_of_formats_to_try = sizeof(auto_format_check_sequence)/sizeof(sps_format_t);
- int i = 0;
- int format_found = 0;
- enum sps_format_t trial_format = SPS_FORMAT_UNKNOWN;
- while ((i < number_of_formats_to_try) && (format_found == 0)) {
- trial_format = formats[i];
- sf = fr[trial_format].alsa_code;
- frame_size = fr[trial_format].frame_size;
- ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf);
- if (ret == 0)
- format_found = 1;
- else
- i++;
- }
- if (ret == 0) {
- config.output_format = trial_format;
- debug(1,"alsa: output format chosen is \"%s\".",sps_format_description_string(config.output_format));
- } else {
- warn("audio_alsa: Could not automatically set the output format for device \"%s\": %s",
- alsa_out_dev, snd_strerror(ret));
- return ret;
- }
+ int number_of_formats_to_try;
+ enum sps_format_t *formats;
+ formats = auto_format_check_sequence;
+ number_of_formats_to_try = sizeof(auto_format_check_sequence) / sizeof(sps_format_t);
+ int i = 0;
+ int format_found = 0;
+ enum sps_format_t trial_format = SPS_FORMAT_UNKNOWN;
+ while ((i < number_of_formats_to_try) && (format_found == 0)) {
+ trial_format = formats[i];
+ sf = fr[trial_format].alsa_code;
+ frame_size = fr[trial_format].frame_size;
+ ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf);
+ if (ret == 0)
+ format_found = 1;
+ else
+ i++;
+ }
+ if (ret == 0) {
+ config.output_format = trial_format;
+ debug(1, "alsa: output format chosen is \"%s\".",
+ sps_format_description_string(config.output_format));
+ } else {
+ warn("audio_alsa: Could not automatically set the output format for device \"%s\": %s",
+ alsa_out_dev, snd_strerror(ret));
+ return ret;
+ }
}
-
+
if ((do_auto_setup == 0) || (config.output_rate_auto_requested == 0)) { // no auto format
- actual_sample_rate = config.output_rate; // this is the requested rate -- it'll be changed to the actual rate
- ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir);
- if (ret < 0) {
- warn("audio_alsa: Rate %iHz not available for playback: %s", config.output_rate,
- snd_strerror(ret));
- return ret;
- }
- } else {
- int number_of_speeds_to_try;
- unsigned int *speeds;
-
- speeds = auto_speed_output_rates;
- number_of_speeds_to_try = sizeof(auto_speed_output_rates)/sizeof(int);
-
- int i = 0;
- int speed_found = 0;
-
- while ((i < number_of_speeds_to_try) && (speed_found == 0)) {
- actual_sample_rate = speeds[i];
- ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir);
- if (ret == 0) {
- speed_found = 1;
- if (actual_sample_rate != speeds[i])
- warn("Speed requested: %d. Speed available: %d.",speeds[i],actual_sample_rate);
- } else {
- i++;
- }
- }
- if (ret == 0) {
- config.output_rate = actual_sample_rate;
- debug(1,"alsa: output speed chosen is %d.",config.output_rate);
- } else {
- warn("audio_alsa: Could not automatically set the output rate for device \"%s\": %s",
- alsa_out_dev, snd_strerror(ret));
- return ret;
- }
- }
-
+ actual_sample_rate =
+ config.output_rate; // this is the requested rate -- it'll be changed to the actual rate
+ ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir);
+ if (ret < 0) {
+ warn("audio_alsa: Rate %iHz not available for playback: %s", config.output_rate,
+ snd_strerror(ret));
+ return ret;
+ }
+ } else {
+ int number_of_speeds_to_try;
+ unsigned int *speeds;
+
+ speeds = auto_speed_output_rates;
+ number_of_speeds_to_try = sizeof(auto_speed_output_rates) / sizeof(int);
+
+ int i = 0;
+ int speed_found = 0;
+
+ while ((i < number_of_speeds_to_try) && (speed_found == 0)) {
+ actual_sample_rate = speeds[i];
+ ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &actual_sample_rate, &dir);
+ if (ret == 0) {
+ speed_found = 1;
+ if (actual_sample_rate != speeds[i])
+ warn("Speed requested: %d. Speed available: %d.", speeds[i], actual_sample_rate);
+ } else {
+ i++;
+ }
+ }
+ if (ret == 0) {
+ config.output_rate = actual_sample_rate;
+ debug(1, "alsa: output speed chosen is %d.", config.output_rate);
+ } else {
+ warn("audio_alsa: Could not automatically set the output rate for device \"%s\": %s",
+ alsa_out_dev, snd_strerror(ret));
+ return ret;
+ }
+ }
+
if (set_period_size_request != 0) {
debug(1, "Attempting to set the period size to %lu", period_size_requested);
ret = snd_pcm_hw_params_set_period_size_near(alsa_handle, alsa_params, &period_size_requested,
warn("Can't set the D/A converter to sample rate %d.", config.output_rate);
return -EINVAL;
}
-
- use_monotonic_clock = snd_pcm_hw_params_is_monotonic(alsa_params);
+
+ use_monotonic_clock = snd_pcm_hw_params_is_monotonic(alsa_params);
ret = snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_length);
if (ret < 0) {
snd_strerror(ret));
return ret;
}
-
+
ret = snd_pcm_prepare(alsa_handle);
if (ret < 0) {
- warn("audio_alsa: Unable to prepare the device: \"%s\": %s.", alsa_out_dev,
- snd_strerror(ret));
+ warn("audio_alsa: Unable to prepare the device: \"%s\": %s.", alsa_out_dev, snd_strerror(ret));
return ret;
}
"length (%ld) you have chosen.",
actual_buffer_length, config.audio_backend_buffer_desired_length);
}
-
-
- if (config.use_precision_timing == YNA_YES)
+
+ if (config.use_precision_timing == YNA_YES)
delay_and_status = precision_delay_and_status;
else if (config.use_precision_timing == YNA_AUTO) {
if (precision_delay_available()) {
delay_and_status = precision_delay_and_status;
- debug(2,"alsa: precision timing selected for \"auto\" mode");
+ debug(2, "alsa: precision timing selected for \"auto\" mode");
}
}
stall_monitor_start_time = 0;
stall_monitor_frame_count = 0;
-
+
config.disable_standby_mode = disable_standby_off;
config.keep_dac_busy = 0;
config.use_precision_timing = YNA_AUTO;
config.alsa_use_hardware_mute = 0;
}
}
-
/* Get the output format, using the same names as aplay does*/
if (config_lookup_string(config.cfg, "alsa.output_format", &str)) {
- int temp_output_format_auto_requested = config.output_format_auto_requested;
- config.output_format_auto_requested = 0; // assume a valid format will be given.
+ int temp_output_format_auto_requested = config.output_format_auto_requested;
+ config.output_format_auto_requested = 0; // assume a valid format will be given.
if (strcasecmp(str, "S16") == 0)
config.output_format = SPS_FORMAT_S16;
else if (strcasecmp(str, "S16_LE") == 0)
else if (strcasecmp(str, "auto") == 0)
config.output_format_auto_requested = 1;
else {
- config.output_format_auto_requested = temp_output_format_auto_requested; //format was invalid; recall the original setting
+ config.output_format_auto_requested =
+ temp_output_format_auto_requested; // format was invalid; recall the original setting
warn("Invalid output format \"%s\". It should be \"auto\", \"U8\", \"S8\", "
"\"S16\", \"S24\", \"S24_LE\", \"S24_BE\", "
"\"S24_3LE\", \"S24_3BE\" or "
- "\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".", str,
- config.output_format_auto_requested == 1 ? "auto" : sps_format_description_string(config.output_format));
+ "\"S32\", \"S32_LE\", \"S32_BE\". It remains set to \"%s\".",
+ str, config.output_format_auto_requested == 1 ? "auto" : sps_format_description_string(
+ config.output_format));
}
}
if (config_lookup_string(config.cfg, "alsa.output_rate", &str)) {
- if (strcasecmp(str, "auto") == 0) {
- config.output_rate_auto_requested = 1;
- } else {
- if (config.output_rate_auto_requested == 1)
- warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. "
- "It remains set to \"auto\". Note: numbers should not be placed in quotes.", str);
- else
- warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. "
- "It remains set to %d. Note: numbers should not be placed in quotes.", str, config.output_rate);
- }
- }
-
- /* Get the output rate, which must be a multiple of 44,100*/
- if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) {
- debug(1, "alsa output rate is %d frames per second", value);
- switch (value) {
- case 44100:
- case 88200:
- case 176400:
- case 352800:
- config.output_rate = value;
- config.output_rate_auto_requested = 0;
- break;
- default:
- if (config.output_rate_auto_requested == 1)
- warn("Invalid output rate \"%d\". It should be \"auto\", 44100, 88200, 176400 or 352800. "
- "It remains set to \"auto\".",value);
- else
- warn("Invalid output rate \"%d\".It should be \"auto\", 44100, 88200, 176400 or 352800. "
- "It remains set to %d.", value, config.output_rate);
- }
- }
+ if (strcasecmp(str, "auto") == 0) {
+ config.output_rate_auto_requested = 1;
+ } else {
+ if (config.output_rate_auto_requested == 1)
+ warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. "
+ "It remains set to \"auto\". Note: numbers should not be placed in quotes.",
+ str);
+ else
+ warn("Invalid output rate \"%s\". It should be \"auto\", 44100, 88200, 176400 or 352800. "
+ "It remains set to %d. Note: numbers should not be placed in quotes.",
+ str, config.output_rate);
+ }
+ }
+
+ /* Get the output rate, which must be a multiple of 44,100*/
+ if (config_lookup_int(config.cfg, "alsa.output_rate", &value)) {
+ debug(1, "alsa output rate is %d frames per second", value);
+ switch (value) {
+ case 44100:
+ case 88200:
+ case 176400:
+ case 352800:
+ config.output_rate = value;
+ config.output_rate_auto_requested = 0;
+ break;
+ default:
+ if (config.output_rate_auto_requested == 1)
+ warn("Invalid output rate \"%d\". It should be \"auto\", 44100, 88200, 176400 or 352800. "
+ "It remains set to \"auto\".",
+ value);
+ else
+ warn("Invalid output rate \"%d\".It should be \"auto\", 44100, 88200, 176400 or 352800. "
+ "It remains set to %d.",
+ value, config.output_rate);
+ }
+ }
/* Get the use_mmap_if_available setting. */
if (config_lookup_string(config.cfg, "alsa.use_mmap_if_available", &str)) {
}
}
-
/* Get the optional disable_standby_mode setting. */
if (config_lookup_string(config.cfg, "alsa.disable_standby_mode", &str)) {
- if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || (strcasecmp(str, "never") == 0))
+ if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) ||
+ (strcasecmp(str, "never") == 0))
config.disable_standby_mode = disable_standby_off;
- else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) || (strcasecmp(str, "always") == 0)) {
+ else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) ||
+ (strcasecmp(str, "always") == 0)) {
config.disable_standby_mode = disable_standby_always;
config.keep_dac_busy = 1;
} else if (strcasecmp(str, "auto") == 0)
else {
warn("Invalid disable_standby_mode option choice \"%s\". It should be "
"\"always\", \"auto\" or \"never\". "
- "It remains set to \"never\".", str);
+ "It remains set to \"never\".",
+ str);
}
}
-
if (config_lookup_string(config.cfg, "alsa.use_precision_timing", &str)) {
- if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) || (strcasecmp(str, "never") == 0))
+ if ((strcasecmp(str, "no") == 0) || (strcasecmp(str, "off") == 0) ||
+ (strcasecmp(str, "never") == 0))
config.use_precision_timing = YNA_NO;
- else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) || (strcasecmp(str, "always") == 0)) {
+ else if ((strcasecmp(str, "yes") == 0) || (strcasecmp(str, "on") == 0) ||
+ (strcasecmp(str, "always") == 0)) {
config.use_precision_timing = YNA_YES;
config.keep_dac_busy = 1;
} else if (strcasecmp(str, "auto") == 0)
else {
warn("Invalid use_precision_timing option choice \"%s\". It should be "
"\"yes\", \"auto\" or \"no\". "
- "It remains set to \"%s\".", config.use_precision_timing == YNA_NO ? "no" : config.use_precision_timing == YNA_AUTO ? "auto" : "yes");
+ "It remains set to \"%s\".",
+ config.use_precision_timing == YNA_NO ? "no" : config.use_precision_timing == YNA_AUTO
+ ? "auto"
+ : "yes");
}
}
- debug(1, "alsa: disable_standby_mode is \"%s\".", config.disable_standby_mode == disable_standby_off ? "never" : config.disable_standby_mode == disable_standby_always ? "always" : "auto");
+ debug(1, "alsa: disable_standby_mode is \"%s\".",
+ config.disable_standby_mode == disable_standby_off
+ ? "never"
+ : config.disable_standby_mode == disable_standby_always ? "always" : "auto");
}
optind = 1; // optind=0 is equivalent to optind=1 plus special behaviour
close_mixer();
}
debug_mutex_unlock(&alsa_mixer_mutex, 3); // release the mutex
- pthread_cleanup_pop(0); // release the mutex
+ pthread_cleanup_pop(0); // release the mutex
pthread_setcancelstate(oldState, NULL);
return response;
}
-static void start(__attribute__((unused)) int i_sample_rate, __attribute__((unused)) int i_sample_format) {
+static void start(__attribute__((unused)) int i_sample_rate,
+ __attribute__((unused)) int i_sample_format) {
debug(3, "audio_alsa start called.");
-
+
frame_index = 0;
measurement_data_is_valid = 0;
}
}
-int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) {
+int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
+ enum yndk_type *using_update_timestamps) {
int ret = 0;
if (using_update_timestamps)
*using_update_timestamps = YNDK_NO;
*state = snd_pcm_state(alsa_handle);
if ((*state == SND_PCM_STATE_RUNNING) || (*state == SND_PCM_STATE_DRAINING)) {
- ret = snd_pcm_delay(alsa_handle,delay);
+ ret = snd_pcm_delay(alsa_handle, delay);
} else {
- // not running, thus no delay information, thus can't check for frame
- // rates
+ // not running, thus no delay information, thus can't check for frame
+ // rates
frame_index = 0; // we'll be starting over...
measurement_data_is_valid = 0;
- *delay = 0;
+ *delay = 0;
}
-
+
stall_monitor_start_time = 0; // zero if not initialised / not started / zeroed by flush
stall_monitor_frame_count = 0; // set to delay at start of time, incremented by any writes
return ret;
}
-int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) {
+int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
+ enum yndk_type *using_update_timestamps) {
snd_pcm_status_t *alsa_snd_pcm_status;
snd_pcm_status_alloca(&alsa_snd_pcm_status);
-
+
if (using_update_timestamps)
*using_update_timestamps = YNDK_DONT_KNOW;
int ret = snd_pcm_status(alsa_handle, alsa_snd_pcm_status);
if (ret == 0) {
-
- // must be 1.1 or later to use snd_pcm_status_get_driver_htstamp
+
+// must be 1.1 or later to use snd_pcm_status_get_driver_htstamp
#if SND_LIB_MINOR == 0
- snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp);
+ snd_pcm_status_get_htstamp(alsa_snd_pcm_status, &update_timestamp);
#else
- snd_pcm_status_get_driver_htstamp(alsa_snd_pcm_status, &update_timestamp);
+ snd_pcm_status_get_driver_htstamp(alsa_snd_pcm_status, &update_timestamp);
#endif
*state = snd_pcm_status_get_state(alsa_snd_pcm_status);
else
*using_update_timestamps = YNDK_YES;
}
-
-// user information
+
+ // user information
if (update_timestamp_ns == 0) {
if (delay_type_notified != 1) {
- debug(2,"alsa: update timestamps unavailable");
+ debug(2, "alsa: update timestamps unavailable");
delay_type_notified = 1;
}
} else {
-// diagnostic
+ // diagnostic
if (delay_type_notified != 0) {
- debug(2,"alsa: update timestamps available");
+ debug(2, "alsa: update timestamps available");
delay_type_notified = 0;
}
}
if (update_timestamp_ns == 0) {
- ret = snd_pcm_delay (alsa_handle,delay);
+ ret = snd_pcm_delay(alsa_handle, delay);
} else {
*delay = snd_pcm_status_get_delay(alsa_snd_pcm_status);
-/*
-// It seems that the alsa library uses CLOCK_REALTIME before 1.0.28, even though
-// the check for monotonic returns true. Might have to watch out for this.
- #if SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR < 28
- clock_gettime(CLOCK_REALTIME, &tn);
- #else
- clock_gettime(CLOCK_MONOTONIC, &tn);
- #endif
-*/
+ /*
+ // It seems that the alsa library uses CLOCK_REALTIME before 1.0.28, even though
+ // the check for monotonic returns true. Might have to watch out for this.
+ #if SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR < 28
+ clock_gettime(CLOCK_REALTIME, &tn);
+ #else
+ clock_gettime(CLOCK_MONOTONIC, &tn);
+ #endif
+ */
if (use_monotonic_clock)
clock_gettime(CLOCK_MONOTONIC, &tn);
else
clock_gettime(CLOCK_REALTIME, &tn);
-
+
uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec;
// see if it's stalled
debug(2, "DAC seems to have stalled with time_now: %lx,%lx"
", update_timestamp: %lx,%lx, stall_monitor_start_time %" PRIX64
", stall_monitor_error_threshold %" PRIX64 ".",
- tn.tv_sec, tn.tv_nsec, update_timestamp.tv_sec, update_timestamp.tv_nsec, stall_monitor_start_time,
- stall_monitor_error_threshold);
+ tn.tv_sec, tn.tv_nsec, update_timestamp.tv_sec, update_timestamp.tv_nsec,
+ stall_monitor_start_time, stall_monitor_error_threshold);
ret = sps_extra_code_output_stalled;
}
} else {
} else {
char errorstring[1024];
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
- debug(1, "alsa: error %d (\"%s\") writing %d samples to alsa device.", ret, (char *)errorstring, samples);
+ debug(1, "alsa: error %d (\"%s\") writing %d samples to alsa device.", ret,
+ (char *)errorstring, samples);
}
-
}
}
} else {
// set accordingly
// do_mute(0); // complete unmute
}
-
+
alsa_backend_state = abm_connected; // only do this if it really opened it.
}
} else {
if (alsa_backend_state != abm_playing) {
debug(2, "alsa: play() -- alsa_backend_state => abm_playing");
alsa_backend_state = abm_playing;
-
+
// mute_requested_internally = 0; // stop requesting a mute for backend's own
- // reasons, which might have been a flush
- //debug(2, "play() set_mute_state");
- //set_mute_state(); // try to action the request and return a status
+ // reasons, which might have been a flush
+ // debug(2, "play() set_mute_state");
+ // set_mute_state(); // try to action the request and return a status
// do_mute(0); // unmute for backend's reason
}
ret = do_play(buf, samples);
}
int prepare(void) {
- // this will leave the DAC open / connected.
+ // this will leave the DAC open / connected.
int ret = 0;
pthread_cleanup_debug_mutex_lock(&alsa_mutex, 50000, 0);
if (alsa_backend_state == abm_disconnected) {
- ret = do_open(1); // do auto setup
+ ret = do_open(1); // do auto setup
if (ret == 0)
debug(2, "alsa: prepare() -- opened output device");
-
}
debug_mutex_unlock(&alsa_mutex, 0);
int error_count = 0;
int error_threshold_exceeded = 0;
int okb = -1;
- while (error_threshold_exceeded == 0) { // if too many play errors occur early on, we will turn off the disable stanby mode
+ while (error_threshold_exceeded ==
+ 0) { // if too many play errors occur early on, we will turn off the disable stanby mode
if (okb != config.keep_dac_busy) {
- debug(2,"keep_dac_busy is now \"%s\"",config.keep_dac_busy == 0 ? "no" : "yes");
+ debug(2, "keep_dac_busy is now \"%s\"", config.keep_dac_busy == 0 ? "no" : "yes");
okb = config.keep_dac_busy;
}
if ((config.keep_dac_busy != 0) && (alsa_device_initialised == 0)) {
// to be in the
// abm_connected state in the first place...) then do the silence-filling
// thing, if needed /* only if the output device is capable of precision delay */.
- if ((alsa_backend_state != abm_disconnected) && (config.keep_dac_busy != 0) /* && precision_delay_available() */ ) {
+ if ((alsa_backend_state != abm_disconnected) &&
+ (config.keep_dac_busy != 0) /* && precision_delay_available() */) {
int reply;
long buffer_size = 0;
snd_pcm_state_t state;
error_count++;
char errorstring[1024];
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
- debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples to alsa device -- %d errors in %d trials.", ret, (char *)errorstring, frames_of_silence, error_count, frame_count);
+ debug(2, "alsa: alsa_buffer_monitor_thread_code error %d (\"%s\") writing %d samples "
+ "to alsa device -- %d errors in %d trials.",
+ ret, (char *)errorstring, frames_of_silence, error_count, frame_count);
if ((error_count > 40) && (frame_count < 100)) {
- warn("disable_standby_mode has been turned off because too many underruns occurred. Is Shairport Sync outputting to a virtual device or running in a virtual machine?");
+ warn("disable_standby_mode has been turned off because too many underruns "
+ "occurred. Is Shairport Sync outputting to a virtual device or running in a "
+ "virtual machine?");
error_threshold_exceeded = 1;
}
}
static void stop(void) { debug(1, "dummy audio stopped\n"); }
-
audio_output audio_dummy = {.name = "dummy",
.help = NULL,
.init = &init,
#include "audio.h"
#include "common.h"
-#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
+#include <stdlib.h>
#include <string.h>
#include <jack/jack.h>
// So make it exactly the number of incoming audio channels!
#define NPORTS 2
static jack_port_t *port[NPORTS];
-static const char* port_name[NPORTS] = { "out_L", "out_R" };
+static const char *port_name[NPORTS] = {"out_L", "out_R"};
static jack_client_t *client;
static jack_nframes_t sample_rate;
static jack_latency_range_t latest_latency_range[NPORTS];
static int64_t time_of_latest_transfer;
-
static inline jack_default_audio_sample_t sample_conv(short sample) {
// It sounds correct, but I don't understand it.
// Zero int needs to be zero float. Check.
}
static void deinterleave_and_convert(const char *interleaved_input_buffer,
- jack_default_audio_sample_t* jack_output_buffer[],
- jack_nframes_t offset,
- jack_nframes_t nframes) {
+ jack_default_audio_sample_t *jack_output_buffer[],
+ jack_nframes_t offset, jack_nframes_t nframes) {
jack_nframes_t f;
// We're dealing with 16bit audio here:
short *ifp = (short *)interleaved_input_buffer;
static int process(jack_nframes_t nframes, __attribute__((unused)) void *arg) {
jack_default_audio_sample_t *buffer[NPORTS];
// Expect an array of two elements because of possible ringbuffer wrap-around:
- jack_ringbuffer_data_t v[2] = { 0 };
+ jack_ringbuffer_data_t v[2] = {0};
jack_nframes_t i, thisbuf;
int frames_written = 0;
int frames_required = 0;
thisbuf = v[i].len / bytes_per_frame;
if (thisbuf > nframes) {
frames_required = nframes;
- } else {
+ } else {
frames_required = thisbuf;
}
deinterleave_and_convert(v[i].buf, buffer, frames_written, frames_required);
// This is the JACK graph reorder callback. Now we know some JACK connections
// have changed, so we recompute the latency.
-static int graph(__attribute__((unused)) void * arg) {
+static int graph(__attribute__((unused)) void *arg) {
int latency = 0;
debug(2, "JACK graph reorder callback called.");
- for (int i=0; i<NPORTS; i++) {
+ for (int i = 0; i < NPORTS; i++) {
jack_port_get_latency_range(port[i], JackPlaybackLatency, &latest_latency_range[i]);
- debug(2, "JACK latency for port %s\tmin: %d\t max: %d",
- port_name[i], latest_latency_range[i].min, latest_latency_range[i].max);
+ debug(2, "JACK latency for port %s\tmin: %d\t max: %d", port_name[i],
+ latest_latency_range[i].min, latest_latency_range[i].max);
latency += latest_latency_range[i].max;
}
latency /= NPORTS;
}
// This the function JACK will call in case of an error in the library.
-static void error(const char *desc) {
- warn("JACK error: \"%s\"", desc);
-}
+static void error(const char *desc) { warn("JACK error: \"%s\"", desc); }
// This is the function JACK will call in case of a non-critical event in the library.
-static void info(const char *desc) {
- inform("JACK information: \"%s\"", desc);
-}
+static void info(const char *desc) { inform("JACK information: \"%s\"", desc); }
int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **argv) {
int i;
sample_rate = jack_get_sample_rate(client);
if (sample_rate != 44100) {
die("The JACK server is running at the wrong sample rate (%d) for Shairport Sync."
- " Must be 44100 Hz.", sample_rate);
+ " Must be 44100 Hz.",
+ sample_rate);
}
jack_set_process_callback(client, &process, NULL);
jack_set_graph_order_callback(client, &graph, NULL);
jack_set_error_function(&error);
jack_set_info_function(&info);
- for (i=0; i < NPORTS; i++) {
- port[i] = jack_port_register(client, port_name[i], JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsOutput, 0);
+ for (i = 0; i < NPORTS; i++) {
+ port[i] =
+ jack_port_register(client, port_name[i], JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
}
if (jack_activate(client)) {
die("Could not activate %s JACK client.", config.jack_client_name);
debug(2, "JACK client %s activated sucessfully.", config.jack_client_name);
}
if (config.jack_autoconnect_pattern != NULL) {
- inform("config.jack_autoconnect_pattern is %s. If you see the program die after this,"
- "you made a syntax error.", config.jack_autoconnect_pattern);
+ inform("config.jack_autoconnect_pattern is %s. If you see the program die after this,"
+ "you made a syntax error.",
+ config.jack_autoconnect_pattern);
// Sadly, this will throw a segfault if the user provides a syntactically incorrect regex.
// I've reported it to the jack-devel mailing list, they're in a better place to fix it.
- const char** port_list = jack_get_ports(client, config.jack_autoconnect_pattern,
+ const char **port_list = jack_get_ports(client, config.jack_autoconnect_pattern,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput);
if (port_list != NULL) {
- for (i = 0; i < NPORTS ; i++) {
- char* full_port_name[NPORTS];
+ for (i = 0; i < NPORTS; i++) {
+ char *full_port_name[NPORTS];
full_port_name[i] = malloc(sizeof(char) * jack_port_name_size());
sprintf(full_port_name[i], "%s:%s", config.jack_client_name, port_name[i]);
if (port_list[i] != NULL) {
err = jack_connect(client, full_port_name[i], port_list[i]);
switch (err) {
case EEXIST:
- inform("The requested connection from %s to %s already exists.",
- full_port_name[i], port_list[i]);
+ inform("The requested connection from %s to %s already exists.", full_port_name[i],
+ port_list[i]);
break;
case 0:
// success
break;
default:
- warn("JACK error no. %d occured while trying to connect %s to %s.",
- err, full_port_name[i], port_list[i]);
+ warn("JACK error no. %d occured while trying to connect %s to %s.", err,
+ full_port_name[i], port_list[i]);
break;
}
} else {
free(full_port_name[i]);
}
while (port_list[i++] != NULL) {
- inform("Additional matching port %s found. Check that the connections are what you intended.");
+ inform(
+ "Additional matching port %s found. Check that the connections are what you intended.");
}
jack_free(port_list);
}
// occupancy after another transfer had occurred, so we could "lose" a full transfer
// (e.g. 1024 frames @ 44,100 fps ~ 23.2 milliseconds)
pthread_mutex_lock(&buffer_mutex);
- int64_t time_now = get_absolute_time_in_fp();
- int64_t delta = time_now - time_of_latest_transfer;
- size_t audio_occupancy_now = jack_ringbuffer_read_space(jackbuf) / bytes_per_frame;
- debug(2, "audio_occupancy_now is %d.", audio_occupancy_now);
+ int64_t time_now = get_absolute_time_in_fp();
+ int64_t delta = time_now - time_of_latest_transfer;
+ size_t audio_occupancy_now = jack_ringbuffer_read_space(jackbuf) / bytes_per_frame;
+ debug(2, "audio_occupancy_now is %d.", audio_occupancy_now);
pthread_mutex_unlock(&buffer_mutex);
int64_t frames_processed_since_latest_latency_check = (delta * 44100) >> 32;
bytes_to_transfer = samples * bytes_per_frame;
// It's ok to lock here since we're not in the realtime callback:
pthread_mutex_lock(&buffer_mutex);
- bytes_transferred = jack_ringbuffer_write(jackbuf, buf, bytes_to_transfer);
- time_of_latest_transfer = get_absolute_time_in_fp();
+ bytes_transferred = jack_ringbuffer_write(jackbuf, buf, bytes_to_transfer);
+ time_of_latest_transfer = get_absolute_time_in_fp();
pthread_mutex_unlock(&buffer_mutex);
if (bytes_transferred < bytes_to_transfer) {
- warn("JACK ringbuffer overrun. Only wrote %d of %d bytes.",
- bytes_transferred, bytes_to_transfer);
+ warn("JACK ringbuffer overrun. Only wrote %d of %d bytes.", bytes_transferred,
+ bytes_to_transfer);
}
return 0;
}
static void start(__attribute__((unused)) int sample_rate,
__attribute__((unused)) int sample_format) {
-
-
+
// this will leave fd as -1 if a reader hasn't been attached to the pipe
// we check that it's not a "real" error though. From the "man 2 open" page:
- // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO open for reading."
+ // "ENXIO O_NONBLOCK | O_WRONLY is set, the named file is a FIFO, and no process has the FIFO
+ // open for reading."
fd = open(pipename, O_WRONLY | O_NONBLOCK);
if ((fd == -1) && (errno != ENXIO) && (warned == 0)) {
char errorstring[1024];
strerror_r(errno, (char *)errorstring, sizeof(errorstring));
- debug(1, "pipe: start -- error %d (\"%s\") opening the pipe named \"%s\".", errno, (char*)errorstring, pipename);
+ debug(1, "pipe: start -- error %d (\"%s\") opening the pipe named \"%s\".", errno,
+ (char *)errorstring, pipename);
warn("Error %d opening the pipe named \"%s\".", errno, pipename);
warned = 1;
}
static struct sndio_formats formats[] = {{"S8", SPS_FORMAT_S8, 44100, 8, 1, 1, SIO_LE_NATIVE},
{"U8", SPS_FORMAT_U8, 44100, 8, 1, 0, SIO_LE_NATIVE},
{"S16", SPS_FORMAT_S16, 44100, 16, 2, 1, SIO_LE_NATIVE},
- {"AUTOMATIC", SPS_FORMAT_S16, 44100, 16, 2, 1, SIO_LE_NATIVE}, // TODO: make this really automatic?
+ {"AUTOMATIC", SPS_FORMAT_S16, 44100, 16, 2, 1,
+ SIO_LE_NATIVE}, // TODO: make this really automatic?
{"S24", SPS_FORMAT_S24, 44100, 24, 4, 1, SIO_LE_NATIVE},
{"S24_3LE", SPS_FORMAT_S24_3LE, 44100, 24, 3, 1, 1},
{"S24_3BE", SPS_FORMAT_S24_3BE, 44100, 24, 3, 1, 0},
written = played = 0;
time_of_last_onmove_cb = 0;
at_least_one_onmove_cb_seen = 0;
-
for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
if (formats[i].fmt == config.output_format) {
break;
}
}
-
+
if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par))
die("sndio: failed to set audio parameters");
for (i = 0, found = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
}
if (!found)
die("sndio: could not set output device to the required format and rate.");
-
framesize = par.bps * par.pchan;
config.output_rate = par.rate;
int fill_bytes = soundio_ring_buffer_fill_count(ring_buffer);
int fill_count = fill_bytes / outstream->bytes_per_frame;
- debug(3,
- "[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , "
- "outstream->bytes_per_frame: %d",
+ debug(3, "[--->>] frame_count_min: %d , frame_count_max: %d , fill_bytes: %d , fill_count: %d , "
+ "outstream->bytes_per_frame: %d",
frame_count_min, frame_count_max, fill_bytes, fill_count, outstream->bytes_per_frame);
if (frame_count_min > fill_count) {
void set_alsa_out_dev(char *);
#endif
-const char * sps_format_description_string_array[] = {"unknown", "S8", "U8" ,"S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE", "S32_BE", "auto", "invalid" };
+const char *sps_format_description_string_array[] = {
+ "unknown", "S8", "U8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE",
+ "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE", "S32_BE", "auto", "invalid"};
-const char * sps_format_description_string(enum sps_format_t format) {
+const char *sps_format_description_string(enum sps_format_t format) {
if ((format >= SPS_FORMAT_UNKNOWN) && (format <= SPS_FORMAT_AUTO))
return sps_format_description_string_array[format];
else
va_start(args, t);
vsnprintf(s, sizeof(s), t, args);
va_end(args);
- fprintf(stderr,"%s\n",s);
+ fprintf(stderr, "%s\n", s);
}
-void log_to_stderr() {
- sps_log = do_sps_log;
-}
+void log_to_stderr() { sps_log = do_sps_log; }
shairport_cfg config;
else if ((debuglev) && (config.debugger_show_elapsed_time))
sps_log(LOG_ERR, "% 20.9f|*fatal error: %s", tss, s);
else
- sps_log(LOG_ERR, "fatal error: %s", s);
+ sps_log(LOG_ERR, "fatal error: %s", s);
pthread_setcancelstate(oldState, NULL);
abort(); // exit() doesn't always work, by heaven.
}
b64 = BIO_push(b64, bmem);
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
BIO_write(b64, input, length);
- (void) BIO_flush(b64);
+ (void)BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
char *buf = (char *)malloc(bptr->length);
BIO_write(bmem, input, inlen);
while (inlen++ & 3)
BIO_write(bmem, "=", 1);
- (void) BIO_flush(bmem);
+ (void)BIO_flush(bmem);
int bufsize = strlen(input) * 3 / 4 + 1;
uint8_t *buf = malloc(bufsize);
double vol2attn(double vol, long max_db, long min_db) {
- // We use a little coordinate geometry to build a transfer function from the volume passed in to
- // the device's dynamic range. (See the diagram in the documents folder.) The x axis is the
- // "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from
- // the bottom of the range to the top. We build the transfer function from one or more lines. We
- // characterise each line with two numbers: the first is where on x the line starts when y=0 (x
- // can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the
- // line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a
- // dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer
- // function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range
- // Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30
- // Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30
+// We use a little coordinate geometry to build a transfer function from the volume passed in to
+// the device's dynamic range. (See the diagram in the documents folder.) The x axis is the
+// "volume in" which will be from -30 to 0. The y axis will be the "volume out" which will be from
+// the bottom of the range to the top. We build the transfer function from one or more lines. We
+// characterise each line with two numbers: the first is where on x the line starts when y=0 (x
+// can be from 0 to -30); the second is where on y the line stops when when x is -30. thus, if the
+// line was characterised as {0,-30}, it would be an identity transfer. Assuming, for example, a
+// dynamic range of lv=-60 to hv=0 Typically we'll use three lines -- a three order transfer
+// function First: {0,30} giving a gentle slope -- the 30 comes from half the dynamic range
+// Second: {-5,-30-(lv+30)/2} giving a faster slope from y=0 at x=-12 to y=-42.5 at x=-30
+// Third: {-17,lv} giving a fast slope from y=0 at x=-19 to y=-60 at x=-30
#define order 3
}
ssize_t non_blocking_write(int fd, const void *buf, size_t count) {
- return non_blocking_write_with_timeout(fd,buf,count,5000); // default is 5 seconds.
+ return non_blocking_write_with_timeout(fd, buf, count, 5000); // default is 5 seconds.
}
/* from
et = (et * 1000000) >> 32; // microseconds
char errstr[1000];
if (r == ETIMEDOUT)
- debug(debuglevel,
- "timed out waiting for a mutex, having waiting %f seconds, with a maximum "
- "waiting time of %d microseconds. \"%s\".",
+ debug(debuglevel, "timed out waiting for a mutex, having waiting %f seconds, with a maximum "
+ "waiting time of %d microseconds. \"%s\".",
(1.0 * et) / 1000000, dally_time, debugmessage);
else
debug(debuglevel, "error %d: \"%s\" waiting for a mutex: \"%s\".", r,
strcpy(version_string, PACKAGE_VERSION);
#ifdef CONFIG_LIBDAEMON
- strcat(version_string, "-libdaemon");
+ strcat(version_string, "-libdaemon");
#endif
#ifdef CONFIG_MBEDTLS
- strcat(version_string, "-mbedTLS");
+ strcat(version_string, "-mbedTLS");
#endif
#ifdef CONFIG_POLARSSL
strcat(version_string, "-PolarSSL");
// I think, for a 32 --> 16 bits, the range of
// random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from
// -32768 to +32767
-
+
// Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits.
// See the original paper at
break;
default:
result = 0; // stop a compiler warning
- die("Unexpected SPS_FORMAT_* with index %d while outputting silence",format);
+ die("Unexpected SPS_FORMAT_* with index %d while outputting silence", format);
}
p += result;
previous_random_number = r;
#define sps_extra_code_output_state_cannot_make_ready 32769
// yeah/no/auto
-enum yna_type {
- YNA_AUTO = -1,
- YNA_NO = 0,
- YNA_YES = 1
-} yna_type;
+enum yna_type { YNA_AUTO = -1, YNA_NO = 0, YNA_YES = 1 } yna_type;
// yeah/no/dont-care
-enum yndk_type {
- YNDK_DONT_KNOW = -1,
- YNDK_NO = 0,
- YNDK_YES = 1
-} yndk_type;
+enum yndk_type { YNDK_DONT_KNOW = -1, YNDK_NO = 0, YNDK_YES = 1 } yndk_type;
enum endian_type {
SS_LITTLE_ENDIAN = 0,
enum stuffing_type {
ST_basic = 0, // straight deletion or insertion of a frame in a 352-frame packet
ST_soxr, // use libsoxr to make a 352 frame packet one frame longer or shorter
- ST_auto, // use soxr if compiled for it and if the soxr_index is low enough
+ ST_auto, // use soxr if compiled for it and if the soxr_index is low enough
} s_type;
enum playback_mode_type {
SPS_FORMAT_S16_BE,
SPS_FORMAT_S24,
SPS_FORMAT_S24_LE,
- SPS_FORMAT_S24_BE,
+ SPS_FORMAT_S24_BE,
SPS_FORMAT_S24_3LE,
SPS_FORMAT_S24_3BE,
SPS_FORMAT_S32,
SPS_FORMAT_INVALID,
} sps_format_t;
-const char * sps_format_description_string(enum sps_format_t format);
+const char *sps_format_description_string(enum sps_format_t format);
typedef struct {
config_t *cfg;
uint32_t userSuppliedLatency; // overrides all other latencies -- use with caution
uint32_t fixedLatencyOffset; // add this to all automatic latencies supplied to get the actual
// total latency
- // the total latency will be limited to the min and max-latency values, if supplied
+// the total latency will be limited to the min and max-latency values, if supplied
#ifdef CONFIG_LIBDAEMON
int daemonise;
int daemonise_store_pid; // don't try to save a PID file
double tolerance; // allow this much drift before attempting to correct it
enum stuffing_type packet_stuffing;
int soxr_delay_index;
- int soxr_delay_threshold; // the soxr delay must be less or equal to this for soxr interpolation to be enabled under the auto setting
+ int soxr_delay_threshold; // the soxr delay must be less or equal to this for soxr interpolation
+ // to be enabled under the auto setting
int decoders_supported;
int use_apple_decoder; // set to 1 if you want to use the apple decoder instead of the original by
// David Hammerton
// fixed latency there might be in the audio path
double audio_backend_silent_lead_in_time; // the length of the silence that should precede a play.
double active_state_timeout; // the amount of time from when play ends to when the system leaves
- // into the "active" mode.
- uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's
- // native range.
- int volume_range_hw_priority; // when extending the volume range by combining sw and hw attenuators, lowering the volume, use all the hw attenuation before using
+ // into the "active" mode.
+ uint32_t volume_range_db; // the range, in dB, from max dB to min dB. Zero means use the mixer's
+ // native range.
+ int volume_range_hw_priority; // when extending the volume range by combining sw and hw
+ // attenuators, lowering the volume, use all the hw attenuation
+ // before using
// sw attenuation
enum volume_control_profile_type volume_control_profile;
- int output_format_auto_requested; // true if the configuration requests auto configuration
+ int output_format_auto_requested; // true if the configuration requests auto configuration
enum sps_format_t output_format;
- int output_rate_auto_requested; // true if the configuration requests auto configuration
+ int output_rate_auto_requested; // true if the configuration requests auto configuration
unsigned int output_rate;
#ifdef CONFIG_CONVOLUTION
void set_requested_connection_state_to_output(int v);
-ssize_t non_blocking_write_with_timeout(int fd, const void *buf, size_t count, int timeout); // timeout in milliseconds
+ssize_t non_blocking_write_with_timeout(int fd, const void *buf, size_t count,
+ int timeout); // timeout in milliseconds
ssize_t non_blocking_write(int fd, const void *buf, size_t count); // used in a few places
#include <netinet/in.h>
#include <pthread.h>
#include <stdlib.h>
-#include <time.h>
#include <sys/time.h>
+#include <time.h>
#include <unistd.h>
#include "metadata_hub.h"
}
static const struct http_funcs responseFuncs = {
- response_realloc,
- response_body,
- response_header,
- response_code,
+ response_realloc, response_body, response_header, response_code,
};
// static pthread_mutex_t dacp_conversation_lock = PTHREAD_MUTEX_INITIALIZER;
// debug(1,"Sent command\"%s\" with a response body of size %d.",command,response.size);
// debug(1,"dacp_conversation_lock released.");
} else {
- debug(3,
- "dacp_send_command: could not acquire a lock on the dacp transmit/receive section "
- "when attempting to "
- "send the command \"%s\". Possible timeout?",
+ debug(3, "dacp_send_command: could not acquire a lock on the dacp transmit/receive section "
+ "when attempting to "
+ "send the command \"%s\". Possible timeout?",
command);
response.code = 494; // This client is already busy
}
if ((conn->dacp_id == NULL) || (strcmp(conn->dacp_id, dacp_server.dacp_id) != 0)) {
if (conn->dacp_id)
- strncpy(dacp_server.dacp_id, conn->dacp_id, sizeof(dacp_server.dacp_id)-1);
+ strncpy(dacp_server.dacp_id, conn->dacp_id, sizeof(dacp_server.dacp_id) - 1);
else
dacp_server.dacp_id[0] = '\0';
dacp_server.port = 0;
void dacp_monitor_port_update_callback(char *dacp_id, uint16_t port) {
debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
- debug(3,
- "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
- "number %d.",
+ debug(3, "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
+ "number %d.",
dacp_id, dacp_server.dacp_id, port);
if (strcmp(dacp_id, dacp_server.dacp_id) == 0) {
dacp_server.port = port;
(metadata_store.advanced_dacp_server_active != 0);
metadata_store.dacp_server_active = 0;
metadata_store.advanced_dacp_server_active = 0;
- debug(2,
- "setting dacp_server_active and advanced_dacp_server_active to 0 with an update "
- "flag value of %d",
+ debug(2, "setting dacp_server_active and advanced_dacp_server_active to 0 with an update "
+ "flag value of %d",
ch);
metadata_hub_modify_epilog(ch);
while (dacp_server.scan_enable == 0) {
}
gboolean notify_disable_standby_mode_callback(ShairportSync *skeleton,
- __attribute__((unused)) gpointer user_data) {
+ __attribute__((unused)) gpointer user_data) {
char *th = (char *)shairport_sync_get_disable_standby_mode(skeleton);
- if ((strcasecmp(th, "no") == 0) || (strcasecmp(th, "off") == 0) || (strcasecmp(th, "never") == 0)) {
+ if ((strcasecmp(th, "no") == 0) || (strcasecmp(th, "off") == 0) ||
+ (strcasecmp(th, "never") == 0)) {
config.disable_standby_mode = disable_standby_off;
config.keep_dac_busy = 0;
- } else if ((strcasecmp(th, "yes") == 0) || (strcasecmp(th, "on") == 0) || (strcasecmp(th, "always") == 0)) {
+ } else if ((strcasecmp(th, "yes") == 0) || (strcasecmp(th, "on") == 0) ||
+ (strcasecmp(th, "always") == 0)) {
config.disable_standby_mode = disable_standby_always;
config.keep_dac_busy = 1;
} else if (strcasecmp(th, "auto") == 0)
else {
warn("An unrecognised disable_standby_mode: \"%s\" was requested via D-Bus interface.", th);
switch (config.disable_standby_mode) {
- case disable_standby_off:
- shairport_sync_set_disable_standby_mode(skeleton, "off");
- break;
- case disable_standby_always:
- shairport_sync_set_disable_standby_mode(skeleton, "always");
- break;
- case disable_standby_auto:
- shairport_sync_set_disable_standby_mode(skeleton, "auto");
- break;
- default:
- break;
+ case disable_standby_off:
+ shairport_sync_set_disable_standby_mode(skeleton, "off");
+ break;
+ case disable_standby_always:
+ shairport_sync_set_disable_standby_mode(skeleton, "always");
+ break;
+ case disable_standby_auto:
+ shairport_sync_set_disable_standby_mode(skeleton, "auto");
+ break;
+ default:
+ break;
}
}
return TRUE;
G_CALLBACK(notify_alacdecoder_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::disable-standby-mode",
G_CALLBACK(notify_disable_standby_mode_callback), NULL);
- g_signal_connect(shairportSyncSkeleton, "notify::volume-control-profile",
+ g_signal_connect(shairportSyncSkeleton, "notify::volume-control-profile",
G_CALLBACK(notify_volume_control_profile_callback), NULL);
g_signal_connect(shairportSyncSkeleton, "notify::disable-standby",
G_CALLBACK(notify_disable_standby_callback), NULL);
debug(1, ">> Active set to \"false\"");
switch (config.disable_standby_mode) {
- case disable_standby_off:
- shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "off");
- debug(1, ">> disable standby mode set to \"off\"");
- break;
- case disable_standby_always:
- shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "always");
- debug(1, ">> disable standby mode set to \"always\"");
- break;
- case disable_standby_auto:
- shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "auto");
- debug(1, ">> disable standby mode set to \"auto\"");
- break;
- default:
- debug(1,"invalid disable_standby mode!");
- break;
+ case disable_standby_off:
+ shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "off");
+ debug(1, ">> disable standby mode set to \"off\"");
+ break;
+ case disable_standby_always:
+ shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "always");
+ debug(1, ">> disable standby mode set to \"always\"");
+ break;
+ case disable_standby_auto:
+ shairport_sync_set_disable_standby_mode(SHAIRPORT_SYNC(shairportSyncSkeleton), "auto");
+ debug(1, ">> disable standby mode set to \"auto\"");
+ break;
+ default:
+ debug(1, "invalid disable_standby mode!");
+ break;
}
#ifdef CONFIG_SOXR
g_bus_unown_name(ownerID);
else
debug(1, "Zero OwnerID for \"org.gnome.ShairportSync\".");
- service_is_running = 0;
+ service_is_running = 0;
}
-int dbus_service_is_running() {
- return service_is_running;
-}
+int dbus_service_is_running() { return service_is_running; }
* OTHER DEALINGS IN THE SOFTWARE.
*/
-#include "common.h"
#include "mdns.h"
+#include "common.h"
#include <arpa/inet.h>
#include <dns_sd.h>
#include <stdlib.h>
#include "metadata_hub.h"
#ifdef CONFIG_MBEDTLS
-#include <mbedtls/version.h>
#include <mbedtls/md5.h>
+#include <mbedtls/version.h>
#endif
#ifdef CONFIG_POLARSSL
char *path = NULL; // this will be what is returned
uint8_t img_md5[16];
- // uint8_t ap_md5[16];
+// uint8_t ap_md5[16];
#ifdef CONFIG_OPENSSL
MD5_CTX ctx;
#endif
#ifdef CONFIG_MBEDTLS
- #if MBEDTLS_VERSION_MINOR >= 7
- mbedtls_md5_context tctx;
- mbedtls_md5_starts_ret(&tctx);
- mbedtls_md5_update_ret(&tctx, (const unsigned char *)buf, len);
- mbedtls_md5_finish_ret(&tctx, img_md5);
- #else
- mbedtls_md5_context tctx;
- mbedtls_md5_starts(&tctx);
- mbedtls_md5_update(&tctx, (const unsigned char *)buf, len);
- mbedtls_md5_finish(&tctx, img_md5);
- #endif
+#if MBEDTLS_VERSION_MINOR >= 7
+ mbedtls_md5_context tctx;
+ mbedtls_md5_starts_ret(&tctx);
+ mbedtls_md5_update_ret(&tctx, (const unsigned char *)buf, len);
+ mbedtls_md5_finish_ret(&tctx, img_md5);
+#else
+ mbedtls_md5_context tctx;
+ mbedtls_md5_starts(&tctx);
+ mbedtls_md5_update(&tctx, (const unsigned char *)buf, len);
+ mbedtls_md5_finish(&tctx, img_md5);
+#endif
#endif
#ifdef CONFIG_POLARSSL
// I think, for a 32 --> 16 bits, the range of
// random numbers needs to be from -2^16 to 2^16, i.e. from -65536 to 65536 inclusive, not from
// -32768 to +32767
-
+
// Actually, what would be generated here is from -65535 to 65535, i.e. one less on the limits.
// See the original paper at
debug(3, "soxr_oneshot execution time in microseconds: mean, standard deviation and max "
"for %" PRId32 " interpolations in the last "
"1250 packets. %10.1f, %10.1f, %10.1f.",
- stat_n, stat_mean, stat_n <= 1 ? 0.0 : sqrtf(stat_M2 / (stat_n - 1)), longest_soxr_execution_time_us);
+ stat_n, stat_mean, stat_n <= 1 ? 0.0 : sqrtf(stat_M2 / (stat_n - 1)),
+ longest_soxr_execution_time_us);
stat_n = 0;
stat_mean = 0.0;
stat_M2 = 0.0;
case SPS_FORMAT_S24_3BE:
conn->output_bytes_per_frame = 6;
break;
-
+
case SPS_FORMAT_S24:
case SPS_FORMAT_S24_LE:
case SPS_FORMAT_S24_BE:
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
if ((current_delay < conn->dac_buffer_queue_minimum_length) ||
(config.packet_stuffing == ST_basic) ||
(config.soxr_delay_index == 0) || // not computed yet
- ((config.packet_stuffing == ST_auto) && (config.soxr_delay_index > config.soxr_delay_threshold)) // if the CPU is deemed too slow
+ ((config.packet_stuffing == ST_auto) &&
+ (config.soxr_delay_index >
+ config.soxr_delay_threshold)) // if the CPU is deemed too slow
) {
#endif
play_samples =
stuff_buffer_basic_32((int32_t *)conn->tbuf, inbuflength, config.output_format,
conn->outbuf, amount_to_stuff, conn->enable_dither, conn);
#ifdef CONFIG_SOXR
- }
- else { // soxr requested or auto requested with the index less or equal to the threshold
+ } else { // soxr requested or auto requested with the index less or equal to the
+ // threshold
play_samples = stuff_buffer_soxr_32((int32_t *)conn->tbuf, (int32_t *)conn->sbuf,
inbuflength, config.output_format, conn->outbuf,
amount_to_stuff, conn->enable_dither, conn);
command_start();
// call on the output device to prepare itself
if ((config.output) && (config.output->prepare))
- config.output->prepare();
-
+ config.output->prepare();
+
pthread_t *pt = malloc(sizeof(pthread_t));
if (pt == NULL)
die("Couldn't allocate space for pthread_t");
stat_mean += stat_delta / stat_n;
stat_M2 += stat_delta * (time_interval_us - stat_mean);
if (stat_n % 2500 == 0) {
- debug(2,
- "Packet reception interval stats: mean, standard deviation and max for the last "
- "2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",
+ debug(2, "Packet reception interval stats: mean, standard deviation and max for the last "
+ "2,500 packets in microseconds: %10.1f, %10.1f, %10.1f.",
stat_mean, sqrtf(stat_M2 / (stat_n - 1)), longest_packet_time_interval_us);
stat_n = 0;
stat_mean = 0.0;
if (la != conn->latency) {
conn->latency = la;
- debug(3,
- "New latency detected: %" PRIu32 ", sync latency: %" PRIu32
- ", minimum latency: %" PRIu32 ", maximum "
- "latency: %" PRIu32 ", fixed offset: %" PRIu32 ".",
+ debug(3, "New latency detected: %" PRIu32 ", sync latency: %" PRIu32
+ ", minimum latency: %" PRIu32 ", maximum "
+ "latency: %" PRIu32 ", fixed offset: %" PRIu32 ".",
la, sync_rtp_timestamp - rtp_timestamp_less_latency, conn->minimum_latency,
conn->maximum_latency, config.fixedLatencyOffset);
}
remote_frame_time_interval; // an IEEE double calculation with a 32-bit
// numerator and 64-bit denominator
// integers
- conn->remote_frame_rate = conn->remote_frame_rate *
- (uint64_t)0x100000000; // this should just change the
- // [binary] exponent in the IEEE
- // FP representation; the
- // mantissa should be unaffected.
+ conn->remote_frame_rate =
+ conn->remote_frame_rate * (uint64_t)0x100000000; // this should just change the
+ // [binary] exponent in the IEEE
+ // FP representation; the
+ // mantissa should be unaffected.
} else {
conn->remote_frame_rate = 0.0; // use as a flag.
}
conn->local_to_remote_time_gradient = (1.0 * mtl) / mbl;
else {
conn->local_to_remote_time_gradient = 1.0;
- debug(1,"rtp_timing_receiver: mbl is 0");
+ debug(1, "rtp_timing_receiver: mbl is 0");
}
} else {
conn->local_to_remote_time_gradient = 1.0;
if (local_time)
calculated_frame_rate = ((1.0 * local_frames) / local_time) * one_fp;
else
- debug(1,"sanitised_source_rate_information: local_time is zero");
+ debug(1, "sanitised_source_rate_information: local_time is zero");
if ((local_time == 0) || ((calculated_frame_rate / conn->input_rate) > 1.002) ||
((calculated_frame_rate / conn->input_rate) < 0.998)) {
debug(3, "input frame rate out of bounds at %.2f fps.", calculated_frame_rate);
if (time_difference)
frame_interval = (time_interval * frame_difference) / time_difference;
else
- debug(1,"local_time_to_frame: time_difference is zero");
+ debug(1, "local_time_to_frame: time_difference is zero");
if (reference_time_was_earlier) {
// debug(1,"Frame interval is %" PRId64 " frames.",frame_interval);
*frame = (conn->reference_timestamp + frame_interval);
(struct sockaddr *)&conn->rtp_client_control_socket, msgsize) == -1) {
char em[1024];
strerror_r(errno, em, sizeof(em));
- debug(1,
- "Error %d using sendto to an audio socket: \"%s\". Backing off for 1/16th of a "
- "second.",
+ debug(1, "Error %d using sendto to an audio socket: \"%s\". Backing off for 1/16th of a "
+ "second.",
errno, em);
conn->rtp_time_of_last_resend_request_error_fp = time_of_sending_fp;
} else {
#include <libconfig.h>
#include <libgen.h>
#include <memory.h>
+#include <net/if.h>
#include <popt.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
-#include <net/if.h>
#include <unistd.h>
-
#include "config.h"
#ifdef CONFIG_MBEDTLS
#endif
#ifdef CONFIG_SOXR
-#include <soxr.h>
#include <math.h>
+#include <soxr.h>
#endif
#ifdef CONFIG_CONVOLUTION
#include <FFTConvolver/convolver.h>
#endif
-
#ifdef CONFIG_LIBDAEMON
pid_t pid;
#endif
}
#ifdef CONFIG_SOXR
-pthread_t soxr_time_check_thread;
-void* soxr_time_check(__attribute__((unused)) void *arg) {
+pthread_t soxr_time_check_thread;
+void *soxr_time_check(__attribute__((unused)) void *arg) {
const int buffer_length = 352;
- int32_t inbuffer[buffer_length*2];
- int32_t outbuffer[(buffer_length+1)*2];
-
- //int32_t *outbuffer = (int32_t*)malloc((buffer_length+1)*2*sizeof(int32_t));
- //int32_t *inbuffer = (int32_t*)malloc((buffer_length)*2*sizeof(int32_t));
-
+ int32_t inbuffer[buffer_length * 2];
+ int32_t outbuffer[(buffer_length + 1) * 2];
+
+ // int32_t *outbuffer = (int32_t*)malloc((buffer_length+1)*2*sizeof(int32_t));
+ // int32_t *inbuffer = (int32_t*)malloc((buffer_length)*2*sizeof(int32_t));
+
// generate a sample signal
- const double frequency = 440; //
-
+ const double frequency = 440; //
+
int i;
-
+
int number_of_iterations = 0;
uint64_t soxr_start_time = get_absolute_time_in_fp();
- uint64_t loop_until_time = (uint64_t)0x180000000 + soxr_start_time; // loop for a second and a half, max
+ uint64_t loop_until_time =
+ (uint64_t)0x180000000 + soxr_start_time; // loop for a second and a half, max
while (get_absolute_time_in_fp() < loop_until_time) {
-
+
number_of_iterations++;
- for (i = 0; i < buffer_length ; i++) {
- double w = sin(i * (frequency + number_of_iterations * 2) * 2 * M_PI/44100);
+ for (i = 0; i < buffer_length; i++) {
+ double w = sin(i * (frequency + number_of_iterations * 2) * 2 * M_PI / 44100);
int32_t wint = (int32_t)(w * INT32_MAX);
inbuffer[i * 2] = wint;
inbuffer[i * 2 + 1] = wint;
}
-
+
soxr_io_spec_t io_spec;
io_spec.itype = SOXR_INT32_I;
io_spec.otype = SOXR_INT32_I;
size_t odone;
- soxr_oneshot(buffer_length, buffer_length + 1, 2, // Rates and # of chans.
- inbuffer, buffer_length, NULL, // Input.
- outbuffer, buffer_length + 1, &odone, // Output.
- &io_spec, // Input, output and transfer spec.
- NULL, NULL); // Default configuration.
-
+ soxr_oneshot(buffer_length, buffer_length + 1, 2, // Rates and # of chans.
+ inbuffer, buffer_length, NULL, // Input.
+ outbuffer, buffer_length + 1, &odone, // Output.
+ &io_spec, // Input, output and transfer spec.
+ NULL, NULL); // Default configuration.
+
io_spec.itype = SOXR_INT32_I;
io_spec.otype = SOXR_INT32_I;
io_spec.scale = 1.0; // this seems to crash if not = 1.0
io_spec.e = NULL;
io_spec.flags = 0;
- soxr_oneshot(buffer_length, buffer_length - 1, 2, // Rates and # of chans.
- inbuffer, buffer_length, NULL, // Input.
- outbuffer, buffer_length - 1, &odone, // Output.
- &io_spec, // Input, output and transfer spec.
- NULL, NULL); // Default configuration.
-
+ soxr_oneshot(buffer_length, buffer_length - 1, 2, // Rates and # of chans.
+ inbuffer, buffer_length, NULL, // Input.
+ outbuffer, buffer_length - 1, &odone, // Output.
+ &io_spec, // Input, output and transfer spec.
+ NULL, NULL); // Default configuration.
}
double soxr_execution_time_us =
- (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0;
+ (((get_absolute_time_in_fp() - soxr_start_time) * 1000000) >> 32) * 1.0;
// free(outbuffer);
// free(inbuffer);
- config.soxr_delay_index = (int)(0.9 + soxr_execution_time_us/(number_of_iterations *1000));
- debug(2,"soxr_delay_index: %d.", config.soxr_delay_index);
- if ((config.packet_stuffing == ST_soxr) && (config.soxr_delay_index > config.soxr_delay_threshold))
- inform("Note: this device may be too slow for \"soxr\" interpolation. Consider choosing the \"basic\" or \"auto\" interpolation setting.");
+ config.soxr_delay_index = (int)(0.9 + soxr_execution_time_us / (number_of_iterations * 1000));
+ debug(2, "soxr_delay_index: %d.", config.soxr_delay_index);
+ if ((config.packet_stuffing == ST_soxr) &&
+ (config.soxr_delay_index > config.soxr_delay_threshold))
+ inform("Note: this device may be too slow for \"soxr\" interpolation. Consider choosing the "
+ "\"basic\" or \"auto\" interpolation setting.");
if (config.packet_stuffing == ST_auto)
- debug(1,"\"%s\" interpolation has been chosen.", config.soxr_delay_index <= config.soxr_delay_threshold ? "soxr" : "basic");
- pthread_exit(NULL);
+ debug(1, "\"%s\" interpolation has been chosen.",
+ config.soxr_delay_index <= config.soxr_delay_threshold ? "soxr" : "basic");
+ pthread_exit(NULL);
}
#endif
config.fixedLatencyOffset = 11025; // this sounds like it works properly.
config.diagnostic_drop_packet_fraction = 0.0;
config.active_state_timeout = 10.0;
- config.soxr_delay_threshold = 30; // the soxr measurement time (milliseconds) of two oneshots must not exceed this if soxr interpolation is to be chosen automatically.
- config.volume_range_hw_priority = 0; // if combining software and hardware volume control, give the software priority
+ config.soxr_delay_threshold = 30; // the soxr measurement time (milliseconds) of two oneshots must
+ // not exceed this if soxr interpolation is to be chosen
+ // automatically.
+ config.volume_range_hw_priority =
+ 0; // if combining software and hardware volume control, give the software priority
// i.e. when reducing volume, reduce the sw first before reducing the software.
-// this is because some hw mixers mute at the bottom of their range, and they don't always advertise this fact
+// this is because some hw mixers mute at the bottom of their range, and they don't always advertise
+// this fact
#ifdef CONFIG_METADATA_HUB
config.cover_art_cache_dir = "/tmp/shairport-sync/.cache/coverart";
config.packet_stuffing = ST_soxr;
#else
warn("The soxr option not available because this version of shairport-sync was built "
- "without libsoxr "
- "support. Change the \"general/interpolation\" setting in the configuration file.");
+ "without libsoxr "
+ "support. Change the \"general/interpolation\" setting in the configuration file.");
#endif
else
die("Invalid interpolation option choice. It should be \"auto\", \"basic\" or \"soxr\"");
}
-
+
#ifdef CONFIG_SOXR
/* Get the soxr_delay_threshold setting. */
if (config_lookup_int(config.cfg, "general.soxr_delay_threshold", &value)) {
config.soxr_delay_threshold = value;
else
warn("Invalid general soxr_delay_threshold setting option choice \"%d\". It should be "
- "between 0 and 100, "
- "inclusive. Default is %d (milliseconds).",
- value, config.soxr_delay_threshold);
+ "between 0 and 100, "
+ "inclusive. Default is %d (milliseconds).",
+ value, config.soxr_delay_threshold);
}
#endif
config.mqtt_port = 1883;
if (config_lookup_int(config.cfg, "mqtt.port", &value)) {
if ((value < 0) || (value > 65535))
- die("Invalid mqtt port number \"%sd\". It should be between 0 and 65535, default is 1883",
- value);
+ die("Invalid mqtt port number \"%sd\". It should be between 0 and 65535, default is 1883",
+ value);
else
- config.mqtt_port = value;
+ config.mqtt_port = value;
}
if (config_lookup_string(config.cfg, "mqtt.username", &str)) {
}
config_set_lookup_bool(config.cfg, "mqtt.enable_remote", &config.mqtt_enable_remote);
#ifndef CONFIG_AVAHI
- if (config.mqtt_enable_remote) {
- die("You have enabled MQTT remote control which requires shairport-sync to be built with "
- "Avahi, but your installation is not using avahi. Please reinstall/recompile with "
- "avahi enabled, or disable remote control.");
- }
+ if (config.mqtt_enable_remote) {
+ die("You have enabled MQTT remote control which requires shairport-sync to be built with "
+ "Avahi, but your installation is not using avahi. Please reinstall/recompile with "
+ "avahi enabled, or disable remote control.");
+ }
#endif
#endif
}
// here, we are finally finished reading the options
-
#ifdef CONFIG_LIBDAEMON
if ((daemonisewith) && (daemonisewithout))
die("Select either daemonize_with_pid_file or daemonize_without_pid_file -- you have selected "
#else
/* Check if we are called with -d or --daemon or -j or justDaemoniseNoPIDFile options*/
if ((daemonisewith != 0) || (daemonisewithout != 0)) {
- fprintf(stderr,"%s was built without libdaemon, so does not support daemonisation using the -d, --deamon, -j or --justDaemoniseNoPIDFile options\n",config.appName);
+ fprintf(stderr, "%s was built without libdaemon, so does not support daemonisation using the "
+ "-d, --deamon, -j or --justDaemoniseNoPIDFile options\n",
+ config.appName);
exit(EXIT_FAILURE);
}
debug(2, "Deinitialise the audio backend.");
config.output->deinit();
}
-
+
#ifdef CONFIG_LIBDAEMON
-// only do this if you are the daemon
+ // only do this if you are the daemon
if (pid == 0) {
daemon_retval_send(0);
daemon_pid_file_remove();
daemon_signal_done();
- }
+ }
#endif
-
+
debug(2, "Exit...");
exit(EXIT_SUCCESS);
}
die("can not allocate memory for the app name!");
free(basec);
- // debug(1,"startup");
+// debug(1,"startup");
#ifdef CONFIG_LIBDAEMON
daemon_set_verbosity(LOG_DEBUG);
#else
- setlogmask (LOG_UPTO (LOG_DEBUG));
- openlog(NULL,0,LOG_DAEMON);
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ openlog(NULL, 0, LOG_DAEMON);
#endif
atexit(exit_function);
// config.statistics_requested = 0; // don't print stats in the log
// config.userSuppliedLatency = 0; // zero means none supplied
-
- config.debugger_show_relative_time = 1; // by default, log the time back to the previous debug message
+
+ config.debugger_show_relative_time =
+ 1; // by default, log the time back to the previous debug message
config.resyncthreshold = 0.05; // 50 ms
config.timeout = 120; // this number of seconds to wait for [more] audio before switching to idle.
config.tolerance =
config.buffer_start_fill = 220;
config.port = 5000;
-
#ifdef CONFIG_SOXR
- config.packet_stuffing = ST_auto; // use soxr interpolation by default if support has been included and if the CPU is fast enough
+ config.packet_stuffing = ST_auto; // use soxr interpolation by default if support has been
+ // included and if the CPU is fast enough
#else
config.packet_stuffing = ST_basic; // simple interpolation or deletion
#endif
-
// char hostname[100];
// gethostname(hostname, 100);
// config.service_name = malloc(20 + 100);
#endif
-
// parse arguments into config -- needed to locate pid_dir
int audio_arg = parse_options(argc, argv);
}
/* Check if we are called with -k or --kill option */
- if (killOption != 0) {
+ if (killOption != 0) {
#ifdef CONFIG_LIBDAEMON
int ret;
}
return ret < 0 ? 1 : 0;
#else
- fprintf(stderr,"%s was built without libdaemon, so does not support the -k or --kill option\n",config.appName);
+ fprintf(stderr, "%s was built without libdaemon, so does not support the -k or --kill option\n",
+ config.appName);
return 1;
#endif
}
daemon_signal_done();
return 0;
}
-
+
if (daemon_pid_file_create() < 0) {
daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno));
}
#endif
- debug(1,"Started!");
+ debug(1, "Started!");
main_thread_id = pthread_self();
if (!main_thread_id)
debug(1, "Main thread is set up to be NULL!");
// make sure the program can create files that group and world can read
umask(S_IWGRP | S_IWOTH);
-
/* print out version */
char *version_dbs = get_version_string();
} else {
debug(1, "can't print the version information!");
}
-
+
debug(1, "log verbosity is %d.", debuglev);
config.output = audio_get_output(config.output_name);
debug(1, "active_state_timeout is %f seconds.", config.active_state_timeout);
debug(1, "mdns backend \"%s\".", config.mdns_name);
debug(2, "userSuppliedLatency is %d.", config.userSuppliedLatency);
- debug(1, "interpolation setting is \"%s\".", config.packet_stuffing == ST_basic ? "basic" : config.packet_stuffing == ST_soxr ? "soxr" : "auto");
+ debug(1, "interpolation setting is \"%s\".",
+ config.packet_stuffing == ST_basic ? "basic" : config.packet_stuffing == ST_soxr ? "soxr"
+ : "auto");
debug(1, "interpolation soxr_delay_threshold is %d.", config.soxr_delay_threshold);
debug(1, "resync time is %f seconds.", config.resyncthreshold);
debug(1, "allow a session to be interrupted: %d.", config.allow_session_interruption);
debug(1, "volume_max_db is not set");
debug(1, "volume range in dB (zero means use the range specified by the mixer): %u.",
config.volume_range_db);
- debug(
- 1,
- "volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used first) is %d.",
- config.volume_range_hw_priority);
+ debug(1, "volume_range_combined_hardware_priority (1 means hardware mixer attenuation is used "
+ "first) is %d.",
+ config.volume_range_hw_priority);
debug(1, "playback_mode is %d (0-stereo, 1-mono, 1-reverse_stereo, 2-both_left, 3-both_right).",
config.playback_mode);
debug(1, "disable_synchronization is %d.", config.no_sync);
debug(1, "use_mmap_if_available is %d.", config.no_mmap ? 0 : 1);
- debug(1, "output_format automatic selection is %sabled.", config.output_format_auto_requested ? "en" : "dis");
+ debug(1, "output_format automatic selection is %sabled.",
+ config.output_format_auto_requested ? "en" : "dis");
if (config.output_format_auto_requested == 0)
- debug(1,
- "output_format is \"%s\".",
- sps_format_description_string(config.output_format));
- debug(1, "output_rate automatic selection is %sabled.", config.output_rate_auto_requested ? "en" : "dis");
+ debug(1, "output_format is \"%s\".", sps_format_description_string(config.output_format));
+ debug(1, "output_rate automatic selection is %sabled.",
+ config.output_rate_auto_requested ? "en" : "dis");
if (config.output_rate_auto_requested == 0)
- debug(1, "output_rate is %d.", config.output_rate);
+ debug(1, "output_rate is %d.", config.output_rate);
debug(1, "audio backend desired buffer length is %f seconds.",
config.audio_backend_buffer_desired_length);
- debug(1, "audio_backend_buffer_interpolation_threshold_in_seconds is %f seconds.", config.audio_backend_buffer_interpolation_threshold_in_seconds);
+ debug(1, "audio_backend_buffer_interpolation_threshold_in_seconds is %f seconds.",
+ config.audio_backend_buffer_interpolation_threshold_in_seconds);
debug(1, "audio backend latency offset is %f seconds.", config.audio_backend_latency_offset);
debug(1, "audio backend silence lead-in time is %f seconds. A value -1.0 means use the default.",
config.audio_backend_silent_lead_in_time);
uint8_t ap_md5[16];
#ifdef CONFIG_SOXR
- pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL);
+ pthread_create(&soxr_time_check_thread, NULL, &soxr_time_check, NULL);
#endif
#ifdef CONFIG_OPENSSL