void set_alsa_out_dev(char *);
#endif
+// always lock use this when accessing the fp_time_at_last_debug_message
+static pthread_mutex_t debug_timing_lock = PTHREAD_MUTEX_INITIALIZER;
+
+pthread_mutex_t the_conn_lock = PTHREAD_MUTEX_INITIALIZER;
+
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"};
shairport_cfg config;
+// accessors for multi-thread-access fields in the conn structure
+
+double get_config_airplay_volume() {
+ config_lock;
+ double v = config.airplay_volume;
+ config_unlock;
+ return v;
+}
+
+void set_config_airplay_volume(double v) {
+ config_lock;
+ config.airplay_volume = v;
+ config_unlock;
+}
+
volatile int debuglev = 0;
sigset_t pselect_sigset;
void set_requested_connection_state_to_output(int v) { requested_connection_state_to_output = v; }
-void die(const char *format, ...) {
+char *generate_preliminary_string(char *buffer, size_t buffer_length, double tss, double tsl,
+ const char *filename, const int linenumber, const char *prefix) {
+ size_t space_remaining = buffer_length;
+ char *insertion_point = buffer;
+ if (config.debugger_show_elapsed_time) {
+ snprintf(insertion_point, space_remaining, "% 20.9f", tss);
+ insertion_point = insertion_point + strlen(insertion_point);
+ space_remaining = space_remaining - strlen(insertion_point);
+ }
+ if (config.debugger_show_relative_time) {
+ snprintf(insertion_point, space_remaining, "% 20.9f", tsl);
+ insertion_point = insertion_point + strlen(insertion_point);
+ space_remaining = space_remaining - strlen(insertion_point);
+ }
+ if (config.debugger_show_file_and_line) {
+ snprintf(insertion_point, space_remaining, " \"%s:%d\"", filename, linenumber);
+ insertion_point = insertion_point + strlen(insertion_point);
+ space_remaining = space_remaining - strlen(insertion_point);
+ }
+
+ if (prefix) {
+ snprintf(insertion_point, space_remaining, "%s", prefix);
+ insertion_point = insertion_point + strlen(insertion_point);
+ }
+ return insertion_point;
+}
+
+void _die(const char *filename, const int linenumber, const char *format, ...) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
- char s[1024];
- s[0] = 0;
- uint64_t time_now = get_absolute_time_in_fp();
- uint64_t time_since_start = time_now - fp_time_at_startup;
- uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
- fp_time_at_last_debug_message = time_now;
- uint64_t divisor = (uint64_t)1 << 32;
- double tss = 1.0 * time_since_start / divisor;
- double tsl = 1.0 * time_since_last_debug_message / divisor;
+ char b[1024];
+ b[0] = 0;
+ char *s;
+ if (debuglev) {
+ pthread_mutex_lock(&debug_timing_lock);
+ uint64_t time_now = get_absolute_time_in_fp();
+ uint64_t time_since_start = time_now - fp_time_at_startup;
+ uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
+ fp_time_at_last_debug_message = time_now;
+ pthread_mutex_unlock(&debug_timing_lock);
+ uint64_t divisor = (uint64_t)1 << 32;
+ s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
+ 1.0 * time_since_last_debug_message / divisor, filename,
+ linenumber, " *fatal error: ");
+ } else {
+ s = b;
+ }
va_list args;
va_start(args, format);
- vsnprintf(s, sizeof(s), format, args);
+ vsnprintf(s, sizeof(b) - (s - b), format, args);
va_end(args);
-
- if ((debuglev) && (config.debugger_show_elapsed_time) && (config.debugger_show_relative_time))
- sps_log(LOG_ERR, "|% 20.9f|% 20.9f|*fatal error: %s", tss, tsl, s);
- else if ((debuglev) && (config.debugger_show_relative_time))
- sps_log(LOG_ERR, "% 20.9f|*fatal error: %s", tsl, s);
- 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, "%s", b);
pthread_setcancelstate(oldState, NULL);
abort(); // exit() doesn't always work, by heaven.
}
-void warn(const char *format, ...) {
+void _warn(const char *filename, const int linenumber, const char *format, ...) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
- char s[1024];
- s[0] = 0;
- uint64_t time_now = get_absolute_time_in_fp();
- uint64_t time_since_start = time_now - fp_time_at_startup;
- uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
- fp_time_at_last_debug_message = time_now;
- uint64_t divisor = (uint64_t)1 << 32;
- double tss = 1.0 * time_since_start / divisor;
- double tsl = 1.0 * time_since_last_debug_message / divisor;
+ char b[1024];
+ b[0] = 0;
+ char *s;
+ if (debuglev) {
+ pthread_mutex_lock(&debug_timing_lock);
+ uint64_t time_now = get_absolute_time_in_fp();
+ uint64_t time_since_start = time_now - fp_time_at_startup;
+ uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
+ fp_time_at_last_debug_message = time_now;
+ pthread_mutex_unlock(&debug_timing_lock);
+ uint64_t divisor = (uint64_t)1 << 32;
+ s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
+ 1.0 * time_since_last_debug_message / divisor, filename,
+ linenumber, " *warning: ");
+ } else {
+ s = b;
+ }
va_list args;
va_start(args, format);
- vsnprintf(s, sizeof(s), format, args);
+ vsnprintf(s, sizeof(b) - (s - b), format, args);
va_end(args);
- if ((debuglev) && (config.debugger_show_elapsed_time) && (config.debugger_show_relative_time))
- sps_log(LOG_WARNING, "|% 20.9f|% 20.9f|*warning: %s", tss, tsl, s);
- else if ((debuglev) && (config.debugger_show_relative_time))
- sps_log(LOG_WARNING, "% 20.9f|*warning: %s", tsl, s);
- else if ((debuglev) && (config.debugger_show_elapsed_time))
- sps_log(LOG_WARNING, "% 20.9f|*warning: %s", tss, s);
- else
- sps_log(LOG_WARNING, "%s", s);
+ sps_log(LOG_WARNING, "%s", b);
pthread_setcancelstate(oldState, NULL);
}
-void debug(int level, const char *format, ...) {
+void _debug(const char *filename, const int linenumber, int level, const char *format, ...) {
if (level > debuglev)
return;
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
- char s[1024];
- s[0] = 0;
+ char b[1024];
+ b[0] = 0;
+ pthread_mutex_lock(&debug_timing_lock);
uint64_t time_now = get_absolute_time_in_fp();
uint64_t time_since_start = time_now - fp_time_at_startup;
uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
fp_time_at_last_debug_message = time_now;
+ pthread_mutex_unlock(&debug_timing_lock);
uint64_t divisor = (uint64_t)1 << 32;
- double tss = 1.0 * time_since_start / divisor;
- double tsl = 1.0 * time_since_last_debug_message / divisor;
+ char *s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
+ 1.0 * time_since_last_debug_message / divisor, filename,
+ linenumber, " ");
va_list args;
va_start(args, format);
- vsnprintf(s, sizeof(s), format, args);
+ vsnprintf(s, sizeof(b) - (s - b), format, args);
va_end(args);
- if ((config.debugger_show_elapsed_time) && (config.debugger_show_relative_time))
- sps_log(LOG_DEBUG, "|% 20.9f|% 20.9f|%s", tss, tsl, s);
- else if (config.debugger_show_relative_time)
- sps_log(LOG_DEBUG, "% 20.9f|%s", tsl, s);
- else if (config.debugger_show_elapsed_time)
- sps_log(LOG_DEBUG, "% 20.9f|%s", tss, s);
- else
- sps_log(LOG_DEBUG, "%s", s);
+ sps_log(LOG_DEBUG, "%s", b);
pthread_setcancelstate(oldState, NULL);
}
-void inform(const char *format, ...) {
+void _inform(const char *filename, const int linenumber, const char *format, ...) {
int oldState;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldState);
- char s[1024];
- s[0] = 0;
- uint64_t time_now = get_absolute_time_in_fp();
- uint64_t time_since_start = time_now - fp_time_at_startup;
- uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
- fp_time_at_last_debug_message = time_now;
- uint64_t divisor = (uint64_t)1 << 32;
- double tss = 1.0 * time_since_start / divisor;
- double tsl = 1.0 * time_since_last_debug_message / divisor;
+ char b[1024];
+ b[0] = 0;
+ char *s;
+ if (debuglev) {
+ pthread_mutex_lock(&debug_timing_lock);
+ uint64_t time_now = get_absolute_time_in_fp();
+ uint64_t time_since_start = time_now - fp_time_at_startup;
+ uint64_t time_since_last_debug_message = time_now - fp_time_at_last_debug_message;
+ fp_time_at_last_debug_message = time_now;
+ pthread_mutex_unlock(&debug_timing_lock);
+ uint64_t divisor = (uint64_t)1 << 32;
+ s = generate_preliminary_string(b, sizeof(b), 1.0 * time_since_start / divisor,
+ 1.0 * time_since_last_debug_message / divisor, filename,
+ linenumber, " ");
+ } else {
+ s = b;
+ }
va_list args;
va_start(args, format);
- vsnprintf(s, sizeof(s), format, args);
+ vsnprintf(s, sizeof(b) - (s - b), format, args);
va_end(args);
- if ((debuglev) && (config.debugger_show_elapsed_time) && (config.debugger_show_relative_time))
- sps_log(LOG_INFO, "|% 20.9f|% 20.9f|%s", tss, tsl, s);
- else if ((debuglev) && (config.debugger_show_relative_time))
- sps_log(LOG_INFO, "% 20.9f|%s", tsl, s);
- else if ((debuglev) && (config.debugger_show_elapsed_time))
- sps_log(LOG_INFO, "% 20.9f|%s", tss, s);
- else
- sps_log(LOG_INFO, "%s", s);
+ sps_log(LOG_INFO, "%s", b);
pthread_setcancelstate(oldState, NULL);
}
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
/* from http://burtleburtle.net/bob/rand/smallprng.html */
+// this is not thread-safe, so we need a mutex on it to use it properly// always lock use this when accessing the fp_time_at_last_debug_message
+pthread_mutex_t r64_mutex = PTHREAD_MUTEX_INITIALIZER;
+
// typedef uint64_t u8;
typedef struct ranctx {
uint64_t a;
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,
int64_t previous_random_number = random_number_in;
char *p = outp;
size_t sample_number;
+ r64_lock; // the random number generator is not thread safe, so we need to lock it while using it
for (sample_number = 0; sample_number < number_of_frames * 2; sample_number++) {
int64_t hyper_sample = 0;
-
int64_t r = r64i();
int64_t tpdf = (r & dither_mask) - (previous_random_number & dither_mask);
p += sample_length;
previous_random_number = r;
}
+ r64_unlock;
return previous_random_number;
}
const char *sps_format_description_string(enum sps_format_t format);
typedef struct {
+ pthread_mutex_t lock;
config_t *cfg;
int endianness;
double airplay_volume; // stored here for reloading when necessary
int volume_max_db;
int no_sync; // disable synchronisation, even if it's available
int no_mmap; // disable use of mmap-based output, even if it's available
- double resyncthreshold; // if it gets out of whack my more than this number of seconds, resync.
+ double resyncthreshold; // if it get's out of whack my more than this number of seconds, resync.
// Zero means never
// resync.
int allow_session_interruption;
int timeout; // while in play mode, exit if no packets of audio come in for more than this number
// of seconds . Zero means never exit.
- int dont_check_timeout; // this is used to maintain backward compatibility with the old -t option
+ int dont_check_timeout; // this is used to maintain backward compatability with the old -t option
// behaviour; only set by -t 0, cleared by everything else
char *output_name;
audio_output *output;
int logOutputLevel; // log output level
int debugger_show_elapsed_time; // in the debug message, display the time since startup
int debugger_show_relative_time; // in the debug message, display the time since the last one
+ int debugger_show_file_and_line; // in the debug message, display the filename and line number
int statistics_requested, use_negotiated_latencies;
enum playback_mode_type playback_mode;
char *cmd_start, *cmd_stop, *cmd_set_volume, *cmd_unfixable;
char *configfile;
char *regtype; // The regtype is the service type followed by the protocol, separated by a dot, by
// default “_raop._tcp.”.
- char *interface; // a string containing the interface name, or NULL if nothing specified
+ char *interface; // a string containg the interface name, or NULL if nothing specified
int interface_index; // only valid if the interface string is non-NULL
double audio_backend_buffer_desired_length; // this will be the length in seconds of the
// audio backend buffer -- the DAC buffer for ALSA
} shairport_cfg;
+// accessors to config for multi-thread access
+double get_config_airplay_volume();
+void set_config_airplay_volume(double v);
+
uint32_t nctohl(const uint8_t *p); // read 4 characters from *p and do ntohl on them
uint16_t nctohs(const uint8_t *p); // read 2 characters from *p and do ntohs on them
volatile int debuglev;
-void die(const char *format, ...);
-void warn(const char *format, ...);
-void inform(const char *format, ...);
-void debug(int level, const char *format, ...);
+void _die(const char *filename, const int linenumber, const char *format, ...);
+void _warn(const char *filename, const int linenumber, const char *format, ...);
+void _inform(const char *filename, const int linenumber, const char *format, ...);
+void _debug(const char *filename, const int linenumber, int level, const char *format, ...);
+
+#define die(...) _die(__FILE__, __LINE__, __VA_ARGS__)
+#define debug(...) _debug(__FILE__, __LINE__, __VA_ARGS__)
+#define warn(...) _warn(__FILE__, __LINE__, __VA_ARGS__)
+#define inform(...) _inform(__FILE__, __LINE__, __VA_ARGS__)
uint8_t *base64_dec(char *input, int *outlen);
char *base64_enc(uint8_t *input, int length);
extern sigset_t pselect_sigset;
+pthread_mutex_t the_conn_lock;
+
+#define conn_lock(arg) \
+ pthread_mutex_lock(&the_conn_lock); \
+ arg; \
+ pthread_mutex_unlock(&the_conn_lock);
+
// wait for the specified time in microseconds -- it checks every 20 milliseconds
int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
const char *debugmessage, int debuglevel);
if (_debug_mutex_lock(mu, t, #mu, __FILE__, __LINE__, d) == 0) \
pthread_cleanup_push(pthread_cleanup_debug_mutex_unlock, (void *)mu)
+#define config_lock \
+ if (pthread_mutex_trylock(&config.lock) != 0) { \
+ debug(1, "config_lock: cannot acquire config.lock"); \
+ }
+
+#define config_unlock pthread_mutex_unlock(&config.lock)
+
+pthread_mutex_t r64_mutex;
+
+#define r64_lock pthread_mutex_lock(&r64_mutex)
+
+#define r64_unlock pthread_mutex_unlock(&r64_mutex)
+
char *get_version_string(); // mallocs a string space -- remember to free it afterwards
void sps_nanosleep(const time_t sec,