Merge branch 'development'

This commit is contained in:
Mike Brady
2019-07-26 11:25:28 +01:00
22 changed files with 989 additions and 885 deletions
+68 -31
View File
@@ -535,38 +535,64 @@ Some Statistics
---------------
If you turn on the `general` `statistics` setting, a heading like this will be output to the console or log file:
```
sync error in milliseconds, net correction in ppm, corrections in ppm, total packets, missing packets, late packets, too late packets, resend requests, min DAC queue size, min buffer occupancy, max buffer occupancy
sync error in milliseconds, net correction in ppm, corrections in ppm, total packets, missing packets, late packets, too late packets, resend requests, min DAC queue size, min buffer occupancy, max buffer occupancy, source nominal frames per second, source actual frames per second, output frames per second, source clock drift in ppm, source clock drift sample count, rough calculated correction in ppm
```
This will be followed by the statistics themselves at regular intervals, for example:
```
-0.7, 0.0, 0.0, 1003, 0, 0, 0, 0, 6085, 222, 230
-0.8, 0.0, 0.0, 2006, 0, 0, 0, 0, 6087, 222, 230
-1.1, 0.0, 0.0, 3009, 0, 0, 0, 0, 6078, 223, 230
-1.1, 0.0, 0.0, 4012, 0, 0, 0, 0, 6078, 222, 230
-1.1, 0.0, 0.0, 5015, 0, 0, 0, 0, 6071, 223, 230
-1.1, 0.0, 0.0, 6018, 0, 0, 0, 0, 6081, 223, 230
-1.0, 0.0, 0.0, 7021, 0, 0, 0, 0, 6078, 223, 230
-0.8, 0.0, 0.0, 8024, 0, 0, 0, 0, 6085, 223, 230
-0.3, 0.0, 0.0, 9027, 0, 0, 0, 0, 6111, 224, 230
-0.4, 0.0, 0.0, 10030, 0, 0, 0, 0, 6104, 223, 230
-0.8, 0.0, 0.0, 11033, 0, 0, 0, 0, 6096, 223, 230
-0.5, 0.0, 0.0, 12036, 0, 0, 0, 0, 6094, 224, 230
-0.7, 0.0, 0.0, 13039, 0, 0, 0, 0, 6095, 222, 230
-0.7, 0.0, 0.0, 14042, 0, 0, 0, 0, 6094, 222, 230
-0.2, 0.0, 0.0, 15045, 0, 0, 0, 0, 6111, 223, 230
-0.6, 0.0, 0.0, 16048, 0, 2, 0, 2, 6090, 210, 230
-0.6, 0.0, 0.0, 17051, 0, 2, 0, 2, 6097, 220, 230
-0.5, 0.0, 0.0, 18054, 0, 2, 0, 2, 6097, 224, 230
-0.4, 0.0, 0.0, 19057, 0, 2, 0, 2, 6098, 222, 230
-0.4, 0.0, 0.0, 20060, 0, 2, 0, 2, 6107, 223, 230
-0.4, 0.0, 0.0, 21063, 0, 2, 0, 2, 6111, 222, 230
-0.4, 0.0, 0.0, 22066, 0, 3, 0, 3, 6108, 210, 230
-0.3, 0.0, 0.0, 23069, 0, 4, 0, 4, 6106, 214, 245
-0.4, 0.0, 0.0, 24072, 0, 4, 0, 4, 6104, 232, 245
-0.3, 0.0, 0.0, 25075, 0, 4, 0, 4, 6069, 232, 246
-0.3, 0.0, 0.0, 26078, 0, 4, 0, 4, 6110, 232, 245
-0.3, 0.0, 0.0, 27081, 0, 4, 0, 4, 6088, 231, 245
-0.2, 0.0, 0.0, 28084, 0, 4, 0, 4, 6090, 232, 245
0.15, 0.0, 0.0, 1003, 0, 0, 0, 0, 8539, 208, 225, 44099.95, 44274.21, 44099.68, 0.00, 0, -6.12
0.34, 0.0, 0.0, 2006, 0, 0, 0, 0, 8591, 206, 225, 44099.99, 44169.96, 44099.76, 0.00, 0, -5.27
0.89, 0.0, 0.0, 3009, 0, 0, 0, 0, 8571, 218, 225, 44100.01, 44152.81, 44099.85, 0.00, 0, -3.66
0.85, 0.0, 0.0, 4012, 0, 0, 0, 0, 8619, 218, 225, 44100.00, 44142.80, 44099.80, 0.00, 0, -4.62
1.29, 0.0, 0.0, 5015, 0, 0, 0, 0, 8623, 218, 225, 44100.00, 44129.39, 44099.83, 0.00, 0, -3.85
1.41, 0.0, 0.0, 6018, 0, 0, 0, 0, 8619, 216, 225, 44099.99, 44127.74, 44099.86, 0.00, 0, -2.76
1.53, 0.0, 0.0, 7021, 0, 0, 0, 0, 8603, 218, 225, 44100.00, 44109.60, 44099.81, 0.00, 0, -4.21
1.81, 0.0, 0.0, 8024, 0, 0, 0, 0, 8655, 215, 225, 44100.00, 44110.01, 44099.82, 0.00, 0, -4.16
2.00, -82.1, 82.1, 9027, 0, 0, 0, 0, 8481, 208, 225, 44100.00, 44110.69, 44099.83, 0.00, 0, -3.97
1.96, -8.5, 8.5, 10030, 0, 0, 0, 0, 8599, 209, 225, 44099.99, 44108.84, 44099.86, 0.00, 0, -3.02
1.94, 0.0, 0.0, 11033, 0, 0, 0, 0, 8583, 217, 225, 44100.00, 44109.33, 44099.87, 0.00, 0, -2.93
1.90, -5.7, 5.7, 12036, 0, 0, 0, 0, 8601, 219, 225, 44100.00, 44110.01, 44099.85, 20.47, 10, -23.88
2.02, -36.8, 36.8, 13039, 0, 0, 0, 0, 8488, 216, 225, 44100.00, 44107.18, 44099.85, 20.39, 12, -23.83
1.96, -17.0, 17.0, 14042, 0, 0, 0, 0, 8554, 218, 226, 44100.00, 44108.80, 44099.84, 20.63, 15, -24.13
1.99, -31.2, 31.2, 15045, 0, 0, 0, 0, 8592, 216, 225, 44100.00, 44106.85, 44099.85, 20.95, 18, -24.39
1.98, -28.3, 28.3, 16048, 0, 0, 0, 0, 8627, 219, 225, 44099.99, 44109.01, 44099.88, 21.23, 20, -23.90
2.01, -31.2, 31.2, 17051, 0, 0, 0, 0, 8582, 217, 225, 44100.00, 44108.86, 44099.84, 21.57, 22, -25.10
1.96, -19.8, 19.8, 18054, 0, 0, 0, 0, 8556, 215, 226, 44100.00, 44103.79, 44099.86, 21.59, 24, -24.85
1.98, -25.5, 25.5, 19057, 0, 0, 0, 0, 8563, 219, 225, 44100.00, 44102.62, 44099.86, 22.26, 25, -25.47
2.00, -28.3, 28.3, 20060, 0, 0, 0, 0, 8569, 214, 225, 44100.00, 44103.22, 44099.88, 22.57, 27, -25.41
1.98, -42.5, 42.5, 21063, 0, 0, 0, 0, 8570, 216, 225, 44100.00, 44104.14, 44099.85, 22.74, 28, -26.08
1.89, -31.2, 31.2, 22066, 0, 0, 0, 0, 8524, 218, 225, 44100.00, 44104.66, 44099.87, 22.89, 30, -25.89
1.96, 0.0, 0.0, 23069, 0, 0, 0, 0, 8650, 218, 225, 44100.00, 44105.36, 44099.88, 22.92, 32, -25.73
1.98, -34.0, 34.0, 24072, 0, 0, 0, 0, 8475, 219, 225, 44100.00, 44106.04, 44099.88, 22.90, 34, -25.70
2.07, -138.8, 138.8, 25075, 0, 0, 0, 0, 8490, 218, 226, 44100.00, 44104.93, 44099.87, 23.87, 35, -26.92
1.33, 0.0, 0.0, 26078, 0, 0, 0, 0, 8617, 216, 225, 44100.00, 44104.66, 44099.88, 23.68, 38, -26.43
1.67, -22.7, 22.7, 27081, 0, 0, 0, 0, 8562, 217, 225, 44100.00, 44106.16, 44099.87, 25.06, 40, -27.93
1.98, -28.3, 28.3, 28084, 0, 0, 0, 0, 8544, 218, 225, 44100.00, 44105.21, 44099.87, 25.91, 41, -28.84
1.99, -22.7, 22.7, 29087, 0, 0, 0, 0, 8569, 219, 225, 44100.00, 44102.30, 44099.87, 25.91, 41, -28.74
1.66, -56.6, 56.6, 30090, 0, 0, 0, 0, 8554, 218, 225, 44100.00, 44103.07, 44099.86, 26.38, 44, -29.46
1.44, 0.0, 0.0, 31093, 0, 0, 0, 0, 8642, 218, 225, 44100.00, 44102.18, 44099.87, 26.08, 46, -28.98
1.56, 0.0, 0.0, 32096, 0, 0, 0, 0, 8647, 218, 225, 44100.00, 44102.76, 44099.87, 25.79, 48, -28.64
1.68, 0.0, 0.0, 33099, 0, 0, 0, 0, 8647, 217, 225, 44100.00, 44103.41, 44099.88, 25.66, 49, -28.51
1.92, -14.2, 14.2, 34102, 0, 0, 0, 0, 8639, 217, 225, 44100.00, 44102.38, 44099.87, 25.45, 51, -28.40
2.00, -45.3, 45.3, 35105, 0, 0, 0, 0, 8593, 216, 225, 44100.00, 44103.23, 44099.86, 25.35, 53, -28.61
1.98, -17.0, 17.0, 36108, 0, 0, 0, 0, 8609, 218, 225, 44100.00, 44102.54, 44099.87, 25.21, 55, -28.12
1.99, -31.2, 31.2, 37111, 0, 0, 0, 0, 8575, 217, 225, 44100.00, 44101.98, 44099.88, 25.11, 57, -27.88
1.99, -14.2, 14.2, 38114, 0, 0, 0, 0, 8624, 218, 225, 44100.00, 44104.44, 44099.87, 24.95, 60, -27.97
1.99, -28.3, 28.3, 39117, 0, 0, 0, 0, 8582, 209, 225, 44100.00, 44104.91, 44099.86, 24.88, 62, -27.98
1.86, -17.0, 17.0, 40120, 0, 0, 0, 0, 8504, 207, 225, 44100.00, 44104.52, 44099.88, 24.66, 63, -27.39
1.96, -56.6, 56.6, 41123, 0, 0, 0, 0, 8517, 212, 226, 44100.00, 44102.29, 44099.88, 24.54, 66, -27.34
1.90, -36.8, 36.8, 42126, 0, 0, 0, 0, 8363, 218, 240, 44100.00, 44116.79, 44099.87, 24.55, 68, -27.48
1.89, 0.0, 0.0, 43129, 0, 0, 0, 0, 8487, 227, 241, 44100.00, 44117.57, 44099.88, 24.56, 69, -27.32
1.99, -22.7, 22.7, 44132, 0, 0, 0, 0, 8576, 227, 240, 44100.00, 44118.53, 44099.87, 24.57, 70, -27.44
1.98, -39.7, 39.7, 45135, 0, 0, 0, 0, 8593, 228, 240, 44100.00, 44113.88, 44099.87, 24.60, 73, -27.43
2.00, -34.0, 34.0, 46138, 0, 0, 0, 0, 8577, 227, 240, 44100.00, 44117.10, 44099.88, 24.65, 75, -27.46
1.98, -22.7, 22.7, 47141, 0, 0, 0, 0, 8591, 204, 240, 44100.00, 44102.78, 44099.87, 24.67, 77, -27.59
1.98, -17.0, 17.0, 48144, 0, 0, 0, 0, 8587, 216, 225, 44100.00, 44102.38, 44099.88, 24.67, 79, -27.42
1.99, -28.3, 28.3, 49147, 0, 0, 0, 0, 8590, 214, 225, 44100.00, 44102.70, 44099.87, 24.66, 80, -27.57
1.85, -62.3, 62.3, 50150, 0, 0, 0, 0, 8518, 217, 226, 44100.00, 44103.16, 44099.87, 24.70, 83, -27.55
1.90, -28.3, 28.3, 51153, 0, 0, 0, 0, 8617, 216, 225, 44100.00, 44102.60, 44099.88, 24.66, 85, -27.42
1.89, -5.7, 5.7, 52156, 0, 0, 0, 0, 8615, 218, 225, 44100.00, 44103.37, 44099.88, 24.65, 86, -27.39
1.96, -19.8, 19.8, 53159, 0, 0, 0, 0, 8600, 219, 225, 44100.00, 44103.77, 44099.87, 24.62, 89, -27.53
1.99, -22.7, 22.7, 54162, 0, 0, 0, 0, 8567, 216, 225, 44100.00, 44103.12, 44099.87, 24.57, 92, -27.45
```
"Sync error in milliseconds" is the average deviation from exact synchronisation. The first line of the example above indicates that the output is on average 0.7 milliseconds behind exact synchronisation. Sync is allowed to drift by the `general` `drift_tolerance_in_seconds` setting — (± 0.002 seconds) by default — before a correction will be made.
@@ -575,11 +601,22 @@ This will be followed by the statistics themselves at regular intervals, for exa
"Corrections in ppm" is the number of frame insertions plus the number of frame deletions (i.e. the total number of corrections), given as a moving average in parts per million. The closer this is to the net corrections, the fewer "unnecessary" corrections that are being made. Third party programs tend to have much larger levels of corrections.
For reference, a drift of one second per day is approximately 11.57 ppm. Left uncorrected, even a drift this small between two audio outputs will be audible after a short time. The above sample is from an iPhone 6 to Shairport Sync running on a WiFi-connected Raspberry Pi 3 using a cheap no-name USB DAC.
"Min DAC queue size" is the minimum size the queue of samples in the output device's hardware buffer was measured at. It is meant to stand at 0.2 seconds = 8,820 frames at 44,100 frames per second, and will go low if the processor is very busy. If it goes below about 2,000 then it's an indication that the processor can't really keep up.
"Source nominal frames per second" is the rate at which audio is being sent to Shairport Sync according to information supplied by the source itself.
"Source actual frames per second" is the rate at which frames of audio are actually received by Shairport Sync. This can vary a great deal due to network conditions, but over a long time (more than 30 minutes) it should settle down to an accurate value. It does not account for retransmitted packets.
"Output frames per second" is the actual rate at which frames of audio are taken by the output device. On a system with a well-conditioned `ntp`-based clock (and without output underruns) this figure should be very accurate after playing material continuously for a period.
"Source clock drift in ppm" is an estimate of the difference in timekeeping between the audio source and the Shairport Sync devive. It is calculated from a linear regression of drift sample data. The number of samples the estimate is based on is given in the next column, "Source clock drift sample count".
"Rough calculated correction in ppm" is a very crude estimate of the amount of interpolation that needs to applied, on average, to keep sync. It is not really to be relied on at this time.
For reference, a drift of one second per day is approximately 11.57 ppm. Left uncorrected, even a drift this small between two audio outputs will be audible after a short time. The above sample is from an iPhone XS Max to Shairport Sync running on a WiFi-connected Raspberry Pi 3 using an [IQaudIO Pi-DigiAMP+](http://iqaudio.co.uk/hats/9-pi-digiamp.html).
It's not unusual to have resend requests, late packets and even missing packets if some part of the connection to the Shairport Sync device is over WiFi. Late packets can sometimes be asked for and received multiple times. Sometimes late packets are sent and arrive too late, but have already been sent and received in time, so weren't needed anyway...
"Min DAC queue size" is the minimum size the queue of samples in the output device's hardware buffer was measured at. It is meant to stand at 0.15 seconds = 6,615 frames at 44,100 frames per second, and will go low if the processor is very busy. If it goes below about 2,000 then it's an indication that the processor can't really keep up.
WiFi Issues
---------
+10 -13
View File
@@ -46,7 +46,6 @@
#include "dbus-service.h"
#endif
enum am_state state;
enum ps_state { ps_inactive, ps_active } player_state;
@@ -73,10 +72,10 @@ void going_active(int block) {
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
@@ -100,12 +99,12 @@ void going_inactive(int block) {
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
}
}
@@ -255,9 +254,7 @@ void *activity_monitor_thread_code(void *arg) {
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");
+1 -1
View File
@@ -5,4 +5,4 @@ enum am_state { am_inactive, am_active, am_timing_out };
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
+1 -1
View File
@@ -116,7 +116,7 @@ void audio_ls_outputs(void) {
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);
}
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ typedef struct {
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);
+402 -286
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -54,7 +54,6 @@ static int play(__attribute__((unused)) void *buf, __attribute__((unused)) int s
static void stop(void) { debug(1, "dummy audio stopped\n"); }
audio_output audio_dummy = {.name = "dummy",
.help = NULL,
.init = &init,
+37 -40
View File
@@ -20,10 +20,10 @@
#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>
@@ -64,7 +64,7 @@ audio_output audio_jack = {.name = "jack",
// 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;
@@ -76,7 +76,6 @@ static int flush_please = 0;
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.
@@ -88,9 +87,8 @@ static inline jack_default_audio_sample_t sample_conv(short sample) {
}
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;
@@ -111,7 +109,7 @@ static void deinterleave_and_convert(const char *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;
@@ -130,7 +128,7 @@ static int process(jack_nframes_t nframes, __attribute__((unused)) void *arg) {
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);
@@ -154,13 +152,13 @@ static int process(jack_nframes_t nframes, __attribute__((unused)) void *arg) {
// 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;
@@ -170,14 +168,10 @@ static int graph(__attribute__((unused)) void * arg) {
}
// 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;
@@ -221,15 +215,16 @@ int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **a
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);
@@ -237,15 +232,16 @@ int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **a
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) {
@@ -254,15 +250,15 @@ int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **a
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 {
@@ -272,7 +268,8 @@ int jack_init(__attribute__((unused)) int argc, __attribute__((unused)) char **a
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);
}
@@ -316,10 +313,10 @@ int jack_delay(long *the_delay) {
// 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;
@@ -339,12 +336,12 @@ int play(void *buf, int samples) {
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;
}
+5 -4
View File
@@ -46,16 +46,17 @@ int warned = 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;
}
+3 -4
View File
@@ -76,7 +76,8 @@ struct sndio_formats {
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},
@@ -176,7 +177,6 @@ static int init(int argc, char **argv) {
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) {
@@ -187,7 +187,7 @@ static int init(int argc, char **argv) {
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++) {
@@ -200,7 +200,6 @@ static int init(int argc, char **argv) {
}
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;
+2 -3
View File
@@ -27,9 +27,8 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m
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) {
+119 -158
View File
@@ -87,9 +87,11 @@
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
@@ -115,12 +117,10 @@ void do_sps_log(__attribute__((unused)) int prio, const char *t, ...) {
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;
@@ -183,7 +183,7 @@ void die(const char *format, ...) {
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.
}
@@ -436,7 +436,7 @@ char *base64_enc(uint8_t *input, int length) {
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);
@@ -468,7 +468,7 @@ uint8_t *base64_dec(char *input, int *outlen) {
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);
@@ -862,17 +862,17 @@ double flat_vol2attn(double vol, long max_db, long min_db) {
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
@@ -1000,7 +1000,7 @@ ssize_t non_blocking_write_with_timeout(int fd, const void *buf, size_t count, i
}
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
@@ -1189,9 +1189,8 @@ int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time,
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,
@@ -1293,10 +1292,10 @@ char *get_version_string() {
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");
@@ -1369,61 +1368,62 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma
// return the last random number used
// assuming the buffer has been assigned
// add a TPDF dither -- see
// http://educypedia.karadimov.info/library/DitherExplained.pdf
// and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25
// 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
// http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf
// by Lipshitz, Wannamaker and Vanderkooy, 1992.
int64_t dither_mask = 0;
switch (format) {
case SPS_FORMAT_S32:
case SPS_FORMAT_S32_LE:
case SPS_FORMAT_S32_BE:
dither_mask = (int64_t)1 << (64 - 32);
break;
case SPS_FORMAT_S24:
case SPS_FORMAT_S24_LE:
case SPS_FORMAT_S24_BE:
case SPS_FORMAT_S24_3LE:
case SPS_FORMAT_S24_3BE:
dither_mask = (int64_t)1 << (64 - 24);
break;
case SPS_FORMAT_S16:
case SPS_FORMAT_S16_LE:
case SPS_FORMAT_S16_BE:
dither_mask = (int64_t)1 << (64 - 16);
break;
case SPS_FORMAT_S8:
case SPS_FORMAT_U8:
dither_mask = (int64_t)1 << (64 - 8);
break;
case SPS_FORMAT_UNKNOWN:
die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask.");
break;
case SPS_FORMAT_AUTO:
die("Unexpected SPS_FORMAT_AUTO while calculating dither mask.");
break;
case SPS_FORMAT_INVALID:
die("Unexpected SPS_FORMAT_INVALID while calculating dither mask.");
break;
}
dither_mask -= 1;
int64_t previous_random_number = random_number_in;
char *p = outp;
size_t sample_number;
for (sample_number = 0; sample_number < number_of_frames * 2; sample_number++) {
int64_t hyper_sample = 0;
// add a TPDF dither -- see
// http://educypedia.karadimov.info/library/DitherExplained.pdf
// and the discussion around https://www.hydrogenaud.io/forums/index.php?showtopic=16963&st=25
// 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
// http://www.ece.rochester.edu/courses/ECE472/resources/Papers/Lipshitz_1992.pdf
// by Lipshitz, Wannamaker and Vanderkooy, 1992.
int64_t dither_mask = 0;
switch (format) {
case SPS_FORMAT_S32:
case SPS_FORMAT_S32_LE:
case SPS_FORMAT_S32_BE:
dither_mask = (int64_t)1 << (64 - 32);
break;
case SPS_FORMAT_S24:
case SPS_FORMAT_S24_LE:
case SPS_FORMAT_S24_BE:
case SPS_FORMAT_S24_3LE:
case SPS_FORMAT_S24_3BE:
dither_mask = (int64_t)1 << (64 - 24);
break;
case SPS_FORMAT_S16:
case SPS_FORMAT_S16_LE:
case SPS_FORMAT_S16_BE:
dither_mask = (int64_t)1 << (64 - 16);
break;
case SPS_FORMAT_S8:
case SPS_FORMAT_U8:
dither_mask = (int64_t)1 << (64 - 8);
break;
case SPS_FORMAT_UNKNOWN:
die("Unexpected SPS_FORMAT_UNKNOWN while calculating dither mask.");
break;
case SPS_FORMAT_AUTO:
die("Unexpected SPS_FORMAT_AUTO while calculating dither mask.");
break;
case SPS_FORMAT_INVALID:
die("Unexpected SPS_FORMAT_INVALID while calculating dither mask.");
break;
}
dither_mask -= 1;
// int64_t r = r64i();
int64_t r = ranarray64i();
int64_t tpdf = (r & dither_mask) - (previous_random_number & dither_mask);
@@ -1435,126 +1435,87 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma
// move the result to the desired position in the int64_t
char *op = p;
int result; // this is the length of the sample
int sample_length; // this is the length of the sample
uint8_t byt;
switch (format) {
case SPS_FORMAT_S32:
hyper_sample >>= (64 - 32);
*(int32_t *)op = hyper_sample;
result = 4;
sample_length = 4;
break;
case SPS_FORMAT_S32_LE:
hyper_sample >>= (64 - 32);
byt = (uint8_t)hyper_sample;
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 16);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 24);
*op++ = byt;
result = 4;
*op++ = (uint8_t)(hyper_sample >> (64 - 32)); // 32 bits, ls byte
*op++ = (uint8_t)(hyper_sample >> (64 - 32 + 8)); // 32 bits, less significant middle byte
*op++ = (uint8_t)(hyper_sample >> (64 - 32 + 16)); // 32 bits, more significant middle byte
*op = (uint8_t)(hyper_sample >> (64 - 32 + 24)); // 32 bits, ms byte
sample_length = 4;
break;
case SPS_FORMAT_S32_BE:
hyper_sample >>= (64 - 32);
byt = (uint8_t)(hyper_sample >> 24);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 16);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
byt = (uint8_t)hyper_sample;
*op++ = byt;
result = 4;
*op++ = (uint8_t)(hyper_sample >> (64 - 32 + 24)); // 32 bits, ms byte
*op++ = (uint8_t)(hyper_sample >> (64 - 32 + 16)); // 32 bits, more significant middle byte
*op++ = (uint8_t)(hyper_sample >> (64 - 32 + 8)); // 32 bits, less significant middle byte
*op = (uint8_t)(hyper_sample >> (64 - 32)); // 32 bits, ls byte
sample_length = 4;
break;
case SPS_FORMAT_S24_3LE:
hyper_sample >>= (64 - 24);
byt = (uint8_t)hyper_sample;
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 16);
*op++ = byt;
result = 3;
*op++ = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte
*op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte
*op = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte
sample_length = 3;
break;
case SPS_FORMAT_S24_3BE:
hyper_sample >>= (64 - 24);
byt = (uint8_t)(hyper_sample >> 16);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
byt = (uint8_t)hyper_sample;
*op++ = byt;
result = 3;
*op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte
*op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte
*op = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte
sample_length = 3;
break;
case SPS_FORMAT_S24:
hyper_sample >>= (64 - 24);
*(int32_t *)op = hyper_sample;
result = 4;
sample_length = 4;
break;
case SPS_FORMAT_S24_LE:
hyper_sample >>= (64 - 24);
byt = (uint8_t)hyper_sample;
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 16);
*op++ = byt;
*op++ = 0;
result = 4;
*op++ = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte
*op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte
*op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte
*op = 0;
sample_length = 4;
break;
case SPS_FORMAT_S24_BE:
hyper_sample >>= (64 - 24);
*op++ = 0;
byt = (uint8_t)(hyper_sample >> 16);
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
byt = (uint8_t)hyper_sample;
*op++ = byt;
result = 4;
*op++ = (uint8_t)(hyper_sample >> (64 - 24 + 16)); // 24 bits, ms byte
*op++ = (uint8_t)(hyper_sample >> (64 - 24 + 8)); // 24 bits, middle byte
*op = (uint8_t)(hyper_sample >> (64 - 24)); // 24 bits, ls byte
sample_length = 4;
break;
case SPS_FORMAT_S16_LE:
hyper_sample >>= (64 - 16);
byt = (uint8_t)hyper_sample;
*op++ = byt;
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
result = 2;
*op++ = (uint8_t)(hyper_sample >> (64 - 16));
*op++ = (uint8_t)(hyper_sample >> (64 - 16 + 8)); // 16 bits, ms byte
sample_length = 2;
break;
case SPS_FORMAT_S16_BE:
hyper_sample >>= (64 - 16);
byt = (uint8_t)(hyper_sample >> 8);
*op++ = byt;
byt = (uint8_t)hyper_sample;
*op++ = byt;
result = 2;
*op++ = (uint8_t)(hyper_sample >> (64 - 16 + 8)); // 16 bits, ms byte
*op = (uint8_t)(hyper_sample >> (64 - 16));
sample_length = 2;
break;
case SPS_FORMAT_S16:
hyper_sample >>= (64 - 16);
*(int16_t *)op = (int16_t)hyper_sample;
result = 2;
*(int16_t *)op = (int16_t)(hyper_sample >> (64 - 16));
sample_length = 2;
break;
case SPS_FORMAT_S8:
hyper_sample >>= (int8_t)(64 - 8);
*op = hyper_sample;
result = 1;
*op = (int8_t)(hyper_sample >> (64 - 8));
sample_length = 1;
break;
case SPS_FORMAT_U8:
hyper_sample >>= (uint8_t)(64 - 8);
hyper_sample += 128;
*op = hyper_sample;
result = 1;
*op = 128 + (uint8_t)(hyper_sample >> (64 - 8));
sample_length = 1;
break;
default:
result = 0; // stop a compiler warning
die("Unexpected SPS_FORMAT_* with index %d while outputting silence",format);
sample_length = 0; // stop a compiler warning
die("Unexpected SPS_FORMAT_* with index %d while outputting silence", format);
}
p += result;
p += sample_length;
previous_random_number = r;
}
// hack
// memset(outp,0,number_of_frames * 4);
return previous_random_number;
}
+21 -24
View File
@@ -34,18 +34,10 @@ enum dbus_session_type {
#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,
@@ -56,7 +48,7 @@ enum endian_type {
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 {
@@ -95,7 +87,7 @@ enum sps_format_t {
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,
@@ -105,7 +97,7 @@ enum sps_format_t {
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;
@@ -172,7 +164,7 @@ typedef struct {
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
@@ -192,7 +184,8 @@ typedef struct {
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
@@ -208,23 +201,26 @@ typedef struct {
double audio_backend_buffer_interpolation_threshold_in_seconds; // below this, soxr interpolation
// will not occur -- it'll be
// basic interpolation instead.
double audio_backend_silence_threshold; // below this, silence will be added to the output buffer
double audio_backend_silence_scan_interval; // check the threshold this often
double disable_standby_mode_silence_threshold; // below this, silence will be added to the output
// buffer
double disable_standby_mode_silence_scan_interval; // check the threshold this often
double audio_backend_latency_offset; // this will be the offset in seconds to compensate for any
// 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
@@ -283,7 +279,8 @@ int get_requested_connection_state_to_output();
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
+10 -15
View File
@@ -39,6 +39,7 @@
#include <netinet/in.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
@@ -111,10 +112,7 @@ static void response_code(void *opaque, int code) {
}
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;
@@ -322,10 +320,9 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) {
// 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
}
@@ -377,7 +374,7 @@ void set_dacp_server_information(rtsp_conn_info *conn) {
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;
@@ -422,9 +419,8 @@ void set_dacp_server_information(rtsp_conn_info *conn) {
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;
@@ -472,9 +468,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
(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) {
+34 -34
View File
@@ -433,12 +433,14 @@ gboolean notify_drift_tolerance_callback(ShairportSync *skeleton,
}
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)
@@ -446,17 +448,17 @@ gboolean notify_disable_standby_mode_callback(ShairportSync *skeleton,
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;
@@ -661,7 +663,7 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
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);
@@ -749,21 +751,21 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name
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
@@ -902,9 +904,7 @@ void stop_dbus_service() {
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; }
+1 -1
View File
@@ -24,8 +24,8 @@
* 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>
+13 -13
View File
@@ -46,8 +46,8 @@
#include "metadata_hub.h"
#ifdef CONFIG_MBEDTLS
#include <mbedtls/version.h>
#include <mbedtls/md5.h>
#include <mbedtls/version.h>
#endif
#ifdef CONFIG_POLARSSL
@@ -223,7 +223,7 @@ char *metadata_write_image_file(const char *buf, int len) {
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;
@@ -233,17 +233,17 @@ char *metadata_write_image_file(const char *buf, int len) {
#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
+11 -9
View File
@@ -656,7 +656,7 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
// 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
@@ -1522,7 +1522,8 @@ int stuff_buffer_soxr_32(int32_t *inptr, int32_t *scratchBuffer, int length,
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;
@@ -1675,7 +1676,7 @@ void *player_thread_func(void *arg) {
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:
@@ -1801,7 +1802,6 @@ void *player_thread_func(void *arg) {
if ((config.output->parameters == NULL) || (conn->input_bit_depth > output_bit_depth) ||
(config.playback_mode == ST_mono))
conn->enable_dither = 1;
// remember, the output device may never have been initialised prior to this call
config.output->start(config.output_rate, config.output_format); // will need a corresponding stop
@@ -2386,15 +2386,17 @@ void *player_thread_func(void *arg) {
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);
@@ -2943,8 +2945,8 @@ int player_play(rtsp_conn_info *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");
+15 -18
View File
@@ -137,9 +137,8 @@ void *rtp_audio_receiver(void *arg) {
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;
@@ -369,10 +368,9 @@ void *rtp_control_receiver(void *arg) {
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);
}
@@ -396,11 +394,11 @@ void *rtp_control_receiver(void *arg) {
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.
}
@@ -824,7 +822,7 @@ void *rtp_timing_receiver(void *arg) {
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;
@@ -1105,7 +1103,7 @@ int sanitised_source_rate_information(uint32_t *frames, uint64_t *time, rtsp_con
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);
@@ -1188,7 +1186,7 @@ int local_time_to_frame(uint64_t time, uint32_t *frame, rtsp_conn_info *conn) {
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);
@@ -1240,9 +1238,8 @@ void rtp_request_resend(seq_t first, uint32_t count, rtsp_conn_info *conn) {
(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 {
+111 -111
View File
@@ -2572,7 +2572,7 @@ void rtsp_listen_loop(void) {
} else
#endif
family = "IPv4";
debug(1, "Unable to listen on %s port %d. The error is: \"%s\".", family, config.port,
debug(1, "unable to listen on %s port %d. The error is: \"%s\".", family, config.port,
strerror(errno));
continue;
}
@@ -2585,127 +2585,127 @@ void rtsp_listen_loop(void) {
freeaddrinfo(info);
if (!nsock)
die("Could not establish a service on port %d -- program terminating. Is another instance of "
"Shairport Sync running on this device?",
config.port);
int maxfd = -1;
fd_set fds;
FD_ZERO(&fds);
for (i = 0; i < nsock; i++) {
if (sockfd[i] > maxfd)
maxfd = sockfd[i];
}
mdns_register();
pthread_setcancelstate(oldState, NULL);
int acceptfd;
struct timeval tv;
pthread_cleanup_push(rtsp_listen_loop_cleanup_handler, (void *)sockfd);
do {
pthread_testcancel();
tv.tv_sec = 60;
tv.tv_usec = 0;
for (i = 0; i < nsock; i++)
FD_SET(sockfd[i], &fds);
ret = select(maxfd + 1, &fds, 0, 0, &tv);
if (ret < 0) {
if (errno == EINTR)
continue;
break;
if (nsock) {
int maxfd = -1;
fd_set fds;
FD_ZERO(&fds);
for (i = 0; i < nsock; i++) {
if (sockfd[i] > maxfd)
maxfd = sockfd[i];
}
cleanup_threads();
mdns_register();
acceptfd = -1;
for (i = 0; i < nsock; i++) {
if (FD_ISSET(sockfd[i], &fds)) {
acceptfd = sockfd[i];
pthread_setcancelstate(oldState, NULL);
int acceptfd;
struct timeval tv;
pthread_cleanup_push(rtsp_listen_loop_cleanup_handler, (void *)sockfd);
do {
pthread_testcancel();
tv.tv_sec = 60;
tv.tv_usec = 0;
for (i = 0; i < nsock; i++)
FD_SET(sockfd[i], &fds);
ret = select(maxfd + 1, &fds, 0, 0, &tv);
if (ret < 0) {
if (errno == EINTR)
continue;
break;
}
}
if (acceptfd < 0) // timeout
continue;
rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info));
if (conn == 0)
die("Couldn't allocate memory for an rtsp_conn_info record.");
memset(conn, 0, sizeof(rtsp_conn_info));
conn->connection_number = RTSP_connection_index++;
socklen_t slen = sizeof(conn->remote);
cleanup_threads();
conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen);
if (conn->fd < 0) {
debug(1, "Connection %d: New connection on port %d not accepted:", conn->connection_number,
config.port);
perror("failed to accept connection");
free(conn);
} else {
SOCKADDR *local_info = (SOCKADDR *)&conn->local;
socklen_t size_of_reply = sizeof(*local_info);
memset(local_info, 0, sizeof(SOCKADDR));
if (getsockname(conn->fd, (struct sockaddr *)local_info, &size_of_reply) == 0) {
// IPv4:
if (local_info->SAFAMILY == AF_INET) {
char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
char remote_ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
struct sockaddr_in *sa = (struct sockaddr_in *)local_info;
inet_ntop(AF_INET, &(sa->sin_addr), ip4, INET_ADDRSTRLEN);
unsigned short int tport = ntohs(sa->sin_port);
sa = (struct sockaddr_in *)&conn->remote;
inet_ntop(AF_INET, &(sa->sin_addr), remote_ip4, INET_ADDRSTRLEN);
unsigned short int rport = ntohs(sa->sin_port);
debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.",
conn->connection_number, remote_ip4, rport, ip4, tport);
acceptfd = -1;
for (i = 0; i < nsock; i++) {
if (FD_ISSET(sockfd[i], &fds)) {
acceptfd = sockfd[i];
break;
}
#ifdef AF_INET6
if (local_info->SAFAMILY == AF_INET6) {
// IPv6:
}
if (acceptfd < 0) // timeout
continue;
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
char remote_ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 *sa6 =
(struct sockaddr_in6 *)local_info; // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6->sin6_addr), ip6, INET6_ADDRSTRLEN);
u_int16_t tport = ntohs(sa6->sin6_port);
sa6 = (struct sockaddr_in6 *)&conn->remote; // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6->sin6_addr), remote_ip6, INET6_ADDRSTRLEN);
u_int16_t rport = ntohs(sa6->sin6_port);
debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.",
conn->connection_number, remote_ip6, rport, ip6, tport);
}
#endif
rtsp_conn_info *conn = malloc(sizeof(rtsp_conn_info));
if (conn == 0)
die("Couldn't allocate memory for an rtsp_conn_info record.");
memset(conn, 0, sizeof(rtsp_conn_info));
conn->connection_number = RTSP_connection_index++;
socklen_t slen = sizeof(conn->remote);
conn->fd = accept(acceptfd, (struct sockaddr *)&conn->remote, &slen);
if (conn->fd < 0) {
debug(1, "Connection %d: New connection on port %d not accepted:", conn->connection_number,
config.port);
perror("failed to accept connection");
free(conn);
} else {
debug(1, "Error figuring out Shairport Sync's own IP number.");
}
// usleep(500000);
// pthread_t rtsp_conversation_thread;
// conn->thread = rtsp_conversation_thread;
// conn->stop = 0; // record's memory has been zeroed
// conn->authorized = 0; // record's memory has been zeroed
// fcntl(conn->fd, F_SETFL, O_NONBLOCK);
SOCKADDR *local_info = (SOCKADDR *)&conn->local;
socklen_t size_of_reply = sizeof(*local_info);
memset(local_info, 0, sizeof(SOCKADDR));
if (getsockname(conn->fd, (struct sockaddr *)local_info, &size_of_reply) == 0) {
ret = pthread_create(&conn->thread, NULL, rtsp_conversation_thread_func,
conn); // also acts as a memory barrier
if (ret) {
char errorstring[1024];
strerror_r(ret, (char *)errorstring, sizeof(errorstring));
die("Connection %d: cannot create an RTSP conversation thread. Error %d: \"%s\".",
conn->connection_number, ret, (char *)errorstring);
}
debug(3, "Successfully created RTSP receiver thread %d.", conn->connection_number);
conn->running = 1; // this must happen before the thread is tracked
track_thread(conn);
}
} while (1);
// IPv4:
if (local_info->SAFAMILY == AF_INET) {
char ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
char remote_ip4[INET_ADDRSTRLEN]; // space to hold the IPv4 string
struct sockaddr_in *sa = (struct sockaddr_in *)local_info;
inet_ntop(AF_INET, &(sa->sin_addr), ip4, INET_ADDRSTRLEN);
unsigned short int tport = ntohs(sa->sin_port);
sa = (struct sockaddr_in *)&conn->remote;
inet_ntop(AF_INET, &(sa->sin_addr), remote_ip4, INET_ADDRSTRLEN);
unsigned short int rport = ntohs(sa->sin_port);
debug(2, "Connection %d: new connection from %s:%u to self at %s:%u.",
conn->connection_number, remote_ip4, rport, ip4, tport);
}
#ifdef AF_INET6
if (local_info->SAFAMILY == AF_INET6) {
// IPv6:
pthread_cleanup_pop(1); // should never happen
debug(1, "Oops -- fell out of the RTSP select loop");
char ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
char remote_ip6[INET6_ADDRSTRLEN]; // space to hold the IPv6 string
struct sockaddr_in6 *sa6 =
(struct sockaddr_in6 *)local_info; // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6->sin6_addr), ip6, INET6_ADDRSTRLEN);
u_int16_t tport = ntohs(sa6->sin6_port);
sa6 = (struct sockaddr_in6 *)&conn->remote; // pretend this is loaded with something
inet_ntop(AF_INET6, &(sa6->sin6_addr), remote_ip6, INET6_ADDRSTRLEN);
u_int16_t rport = ntohs(sa6->sin6_port);
debug(2, "Connection %d: new connection from [%s]:%u to self at [%s]:%u.",
conn->connection_number, remote_ip6, rport, ip6, tport);
}
#endif
} else {
debug(1, "Error figuring out Shairport Sync's own IP number.");
}
// usleep(500000);
// pthread_t rtsp_conversation_thread;
// conn->thread = rtsp_conversation_thread;
// conn->stop = 0; // record's memory has been zeroed
// conn->authorized = 0; // record's memory has been zeroed
// fcntl(conn->fd, F_SETFL, O_NONBLOCK);
ret = pthread_create(&conn->thread, NULL, rtsp_conversation_thread_func,
conn); // also acts as a memory barrier
if (ret) {
char errorstring[1024];
strerror_r(ret, (char *)errorstring, sizeof(errorstring));
die("Connection %d: cannot create an RTSP conversation thread. Error %d: \"%s\".",
conn->connection_number, ret, (char *)errorstring);
}
debug(3, "Successfully created RTSP receiver thread %d.", conn->connection_number);
conn->running = 1; // this must happen before the thread is tracked
track_thread(conn);
}
} while (1);
pthread_cleanup_pop(1); // should never happen
} else {
warn("could not establish a service on port %d -- program terminating. Is another instance of "
"Shairport Sync running on this device?",
config.port);
}
// debug(1, "Oops -- fell out of the RTSP select loop");
}
+4 -2
View File
@@ -91,8 +91,10 @@ alsa =
// use_mmap_if_available = "yes"; // Use this optional advanced setting to control whether MMAP-based output is used to communicate with the DAC. Default is "yes"
// use_hardware_mute_if_available = "no"; // Use this optional advanced setting to control whether the hardware in the DAC is used for muting. Default is "no", for compatibility with other audio players.
// maximum_stall_time = 0.200; // Use this optional advanced setting to control how long to wait for data to be consumed by the output device before considering it an error. It should never approach 200 ms.
// use_precision_timing = "auto"; // If the output device is a real hardware device, precision timing will be used, which is needed for "disable_standby_mode" below. Choose "no" for more compatible standard timing, choose yes to force the use of precision timing, which may cause problems.
// disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never". Needs precision timing to be available.
// use_precision_timing = "auto"; // Use this optional advanced setting to control how Shairport Sync gathers timing information. When set to "auto", if the output device is a real hardware device, precision timing will be used. Choose "no" for more compatible standard timing, choose "yes" to force the use of precision timing, which may cause problems.
// disable_standby_mode = "never"; // This setting prevents the DAC from entering the standby mode. Some DACs make small "popping" noises when they go in and out of standby mode. Settings can be: "always", "auto" or "never". Default is "never", but only for backwards compatibility. The "auto" setting prevents entry to standby mode while Shairport Sync is in the "active" mode. You can use "yes" instead of "always" and "no" instead of "never".
// disable_standby_mode_silence_threshold = 0.040; // Use this optional advanced setting to control how little audio should remain in the output buffer before the disable_standby code should start sending silence to the output device.
// disable_standby_mode_silence_scan_interval = 0.004; // Use this optional advanced setting to control how often the amount of audio remaining in the output buffer should be checked.
};
// Parameters for the "sndio" audio back end. All are optional.
+120 -115
View File
@@ -31,6 +31,7 @@
#include <libconfig.h>
#include <libgen.h>
#include <memory.h>
#include <net/if.h>
#include <popt.h>
#include <stdio.h>
#include <stdlib.h>
@@ -38,10 +39,8 @@
#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
@@ -97,15 +96,14 @@
#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
@@ -165,33 +163,34 @@ void print_version(void) {
}
#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 -- no need to be able to cancel it, do _don't even try_!
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;
@@ -201,37 +200,39 @@ void* soxr_time_check(__attribute__((unused)) void *arg) {
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
@@ -428,10 +429,14 @@ int parse_options(int argc, char **argv) {
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";
@@ -536,13 +541,13 @@ int parse_options(int argc, char **argv) {
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)) {
@@ -550,9 +555,9 @@ int parse_options(int argc, char **argv) {
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
@@ -1003,10 +1008,10 @@ int parse_options(int argc, char **argv) {
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)) {
@@ -1051,11 +1056,11 @@ int parse_options(int argc, char **argv) {
}
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
}
@@ -1125,7 +1130,6 @@ int parse_options(int argc, char **argv) {
// 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 "
@@ -1138,7 +1142,9 @@ int parse_options(int argc, char **argv) {
#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);
}
@@ -1156,25 +1162,27 @@ int parse_options(int argc, char **argv) {
if (tdebuglev != 0)
debuglev = tdebuglev;
/* if the Service Name wasn't specified, do it now */
if (raw_service_name == NULL)
raw_service_name = strdup("%H");
// now, do the substitutions in the service name
char hostname[100];
gethostname(hostname, 100);
char *i1 = str_replace(raw_service_name, "%h", hostname);
if (raw_service_name) {
free(raw_service_name);
raw_service_name = NULL;
}
char *i0;
if (raw_service_name == NULL)
i0 = strdup("%H"); // this is the default it the Service Name wasn't specified
else
i0 = strdup(raw_service_name);
// here, do the substitutions for %h, %H, %v and %V
char *i1 = str_replace(i0, "%h", hostname);
if ((hostname[0] >= 'a') && (hostname[0] <= 'z'))
hostname[0] = hostname[0] - 0x20; // convert a lowercase first letter into a capital letter
char *i2 = str_replace(i1, "%H", hostname);
char *i3 = str_replace(i2, "%v", PACKAGE_VERSION);
char *vs = get_version_string();
config.service_name = str_replace(i3, "%V", vs);
config.service_name = str_replace(i3, "%V", vs); // service name complete
free(i0);
free(i1);
free(i2);
free(i3);
@@ -1271,8 +1279,8 @@ const char *pid_file_proc(void) {
#endif
void main_cleanup_handler(__attribute__((unused)) void *arg) {
// it doesn't look like this is called when the main function is cancelled eith a pthread cancel.
debug(2, "main cleanup handler called.");
// it doesn't look like this is called when the main function is cancelled with a pthread cancel.
debug(1, "main cleanup handler called.");
#ifdef CONFIG_MQTT
if (config.mqtt_enabled) {
// terminate_mqtt();
@@ -1313,22 +1321,27 @@ void main_cleanup_handler(__attribute__((unused)) void *arg) {
debug(2, "Deinitialise the audio backend.");
config.output->deinit();
}
#ifdef CONFIG_SOXR
// be careful -- not sure if the thread can be cancelled cleanly, so wait for it to shut down
pthread_join(soxr_time_check_thread, NULL);
#endif
#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);
}
void exit_function() {
debug(2, "exit function called...");
debug(1, "exit function called...");
main_cleanup_handler(NULL);
if (conns)
free(conns); // make sure the connections have been deleted first
@@ -1366,12 +1379,12 @@ int main(int argc, char **argv) {
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);
@@ -1410,8 +1423,9 @@ int main(int argc, char **argv) {
// 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 =
@@ -1419,14 +1433,13 @@ int main(int argc, char **argv) {
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);
@@ -1488,7 +1501,6 @@ int main(int argc, char **argv) {
#endif
// parse arguments into config -- needed to locate pid_dir
int audio_arg = parse_options(argc, argv);
@@ -1499,7 +1511,7 @@ int main(int argc, char **argv) {
}
/* Check if we are called with -k or --kill option */
if (killOption != 0) {
if (killOption != 0) {
#ifdef CONFIG_LIBDAEMON
int ret;
@@ -1512,7 +1524,8 @@ int main(int argc, char **argv) {
}
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
}
@@ -1591,7 +1604,7 @@ int main(int argc, char **argv) {
daemon_signal_done();
return 0;
}
if (daemon_pid_file_create() < 0) {
daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno));
@@ -1608,7 +1621,7 @@ int main(int argc, char **argv) {
}
#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!");
@@ -1618,7 +1631,6 @@ int main(int argc, char **argv) {
// 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();
@@ -1628,7 +1640,7 @@ int main(int argc, char **argv) {
} else {
debug(1, "can't print the version information!");
}
debug(1, "log verbosity is %d.", debuglev);
config.output = audio_get_output(config.output_name);
@@ -1638,7 +1650,7 @@ int main(int argc, char **argv) {
}
config.output->init(argc - audio_arg, argv + audio_arg);
pthread_cleanup_push(main_cleanup_handler, NULL);
// pthread_cleanup_push(main_cleanup_handler, NULL);
// daemon_log(LOG_NOTICE, "startup");
@@ -1701,7 +1713,9 @@ int main(int argc, char **argv) {
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);
@@ -1715,25 +1729,25 @@ int main(int argc, char **argv) {
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);
@@ -1783,7 +1797,7 @@ int main(int argc, char **argv) {
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
@@ -1847,15 +1861,6 @@ int main(int argc, char **argv) {
#endif
activity_monitor_start();
// debug(1, "Successful Startup");
rtsp_listen_loop();
// should not reach this...
// daemon_log(LOG_NOTICE, "Unexpected exit...");
// daemon_retval_send(0);
// daemon_pid_file_remove();
pthread_cleanup_pop(1);
debug(1, "Odd exit point");
pthread_exit(NULL);
return 0;
}