Improve support for bigendian devices. Add support for S16_LE, S16_BE, S24_LE, S24_BE, S32_LE and S32_BE formats. Make the default S16_LE. Allow precision timing only on alsa devices with names beginning with "hw:". Use snd_pcm_hw_params_is_monotonic() to see if clock is monotonic.
This commit is contained in:
+71
-90
@@ -66,6 +66,7 @@ int mute(int do_mute); // returns true if it actually is allowed to use the mute
|
||||
static double set_volume;
|
||||
static int output_method_signalled = 0; // for reporting whether it's using mmap or not
|
||||
int delay_type_notified = -1; // for controlling the reporting of whether the output device can do precison delays (e.g. alsa->pulsaudio virtual devices can't)
|
||||
int use_monotonic_clock = 0; // this value will be set when the hardware is initialised
|
||||
|
||||
audio_output audio_alsa = {
|
||||
.name = "alsa",
|
||||
@@ -115,9 +116,6 @@ int frame_size; // in bytes for interleaved stereo
|
||||
int alsa_device_initialised; // boolean to ensure the initialisation is only
|
||||
// done once
|
||||
|
||||
|
||||
enum yndk_type precision_delay_available_status = YNDK_DONT_KNOW; // initially, we don't know if the device can do precision delay
|
||||
|
||||
snd_pcm_t *alsa_handle = NULL;
|
||||
static snd_pcm_hw_params_t *alsa_params = NULL;
|
||||
static snd_pcm_sw_params_t *alsa_swparams = NULL;
|
||||
@@ -159,6 +157,12 @@ int standard_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
|
||||
// use this to allow the use of standard or precision delay calculations, with standard the, uh, standard.
|
||||
int (*delay_and_status)(snd_pcm_state_t *state, snd_pcm_sframes_t *delay, enum yndk_type *using_update_timestamps) = standard_delay_and_status;
|
||||
|
||||
int precision_delay_available() {
|
||||
// this is very crude -- if the device is a hardware device, then it's assumed the delay is precise
|
||||
const char *output_device_name = snd_pcm_name(alsa_handle);
|
||||
return (strstr(output_device_name,"hw:") == output_device_name);
|
||||
}
|
||||
|
||||
// static int play_number;
|
||||
// static int64_t accumulated_delay, accumulated_da_delay;
|
||||
int alsa_characteristics_already_listed = 0;
|
||||
@@ -295,17 +299,16 @@ int actual_open_alsa_device(void) {
|
||||
if (config.no_sync != 0)
|
||||
audio_alsa.delay = NULL;
|
||||
|
||||
// ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK,
|
||||
// SND_PCM_NONBLOCK);
|
||||
ret = snd_pcm_open(&alsa_handle, alsa_out_dev, SND_PCM_STREAM_PLAYBACK, 0);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT) {
|
||||
die("the alsa output_device \"%s\" can not be found.", alsa_out_dev);
|
||||
warn("the alsa output_device \"%s\" can not be found.", alsa_out_dev);
|
||||
} else {
|
||||
char errorstring[1024];
|
||||
strerror_r(-ret, (char *)errorstring, sizeof(errorstring));
|
||||
die("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring, alsa_out_dev);
|
||||
warn("alsa: error %d (\"%s\") opening alsa device \"%s\".", ret, (char *)errorstring, alsa_out_dev);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_pcm_hw_params_alloca(&alsa_params);
|
||||
@@ -339,7 +342,7 @@ int actual_open_alsa_device(void) {
|
||||
|
||||
ret = snd_pcm_hw_params_set_access(alsa_handle, alsa_params, access);
|
||||
if (ret < 0) {
|
||||
die("audio_alsa: Access type not available for device \"%s\": %s", alsa_out_dev,
|
||||
warn("audio_alsa: Access type not available for device \"%s\": %s", alsa_out_dev,
|
||||
snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
@@ -347,14 +350,14 @@ int actual_open_alsa_device(void) {
|
||||
|
||||
ret = snd_pcm_hw_params_set_channels(alsa_handle, alsa_params, 2);
|
||||
if (ret < 0) {
|
||||
die("audio_alsa: Channels count (2) not available for device \"%s\": %s", alsa_out_dev,
|
||||
warn("audio_alsa: Channels count (2) not available for device \"%s\": %s", alsa_out_dev,
|
||||
snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_pcm_hw_params_set_rate_near(alsa_handle, alsa_params, &my_sample_rate, &dir);
|
||||
if (ret < 0) {
|
||||
die("audio_alsa: Rate %iHz not available for playback: %s", desired_sample_rate,
|
||||
warn("audio_alsa: Rate %iHz not available for playback: %s", desired_sample_rate,
|
||||
snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
@@ -373,10 +376,26 @@ int actual_open_alsa_device(void) {
|
||||
sf = SND_PCM_FORMAT_S16;
|
||||
frame_size = 4;
|
||||
break;
|
||||
case SPS_FORMAT_S16_LE:
|
||||
sf = SND_PCM_FORMAT_S16_LE;
|
||||
frame_size = 4;
|
||||
break;
|
||||
case SPS_FORMAT_S16_BE:
|
||||
sf = SND_PCM_FORMAT_S16_BE;
|
||||
frame_size = 4;
|
||||
break;
|
||||
case SPS_FORMAT_S24:
|
||||
sf = SND_PCM_FORMAT_S24;
|
||||
frame_size = 8;
|
||||
break;
|
||||
case SPS_FORMAT_S24_LE:
|
||||
sf = SND_PCM_FORMAT_S24_LE;
|
||||
frame_size = 8;
|
||||
break;
|
||||
case SPS_FORMAT_S24_BE:
|
||||
sf = SND_PCM_FORMAT_S24_BE;
|
||||
frame_size = 8;
|
||||
break;
|
||||
case SPS_FORMAT_S24_3LE:
|
||||
sf = SND_PCM_FORMAT_S24_3LE;
|
||||
frame_size = 6;
|
||||
@@ -389,8 +408,16 @@ int actual_open_alsa_device(void) {
|
||||
sf = SND_PCM_FORMAT_S32;
|
||||
frame_size = 8;
|
||||
break;
|
||||
case SPS_FORMAT_S32_LE:
|
||||
sf = SND_PCM_FORMAT_S32_LE;
|
||||
frame_size = 8;
|
||||
break;
|
||||
case SPS_FORMAT_S32_BE:
|
||||
sf = SND_PCM_FORMAT_S32_BE;
|
||||
frame_size = 8;
|
||||
break;
|
||||
default:
|
||||
sf = SND_PCM_FORMAT_S16; // this is just to quieten a compiler warning
|
||||
sf = SND_PCM_FORMAT_S16_LE; // this is just to quieten a compiler warning
|
||||
frame_size = 4;
|
||||
debug(1, "Unsupported output format at audio_alsa.c");
|
||||
return -EINVAL;
|
||||
@@ -398,7 +425,7 @@ int actual_open_alsa_device(void) {
|
||||
|
||||
ret = snd_pcm_hw_params_set_format(alsa_handle, alsa_params, sf);
|
||||
if (ret < 0) {
|
||||
die("audio_alsa: Sample format %d not available for device \"%s\": %s", sample_format,
|
||||
warn("audio_alsa: Sample format %d not available for device \"%s\": %s", sample_format,
|
||||
alsa_out_dev, snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
@@ -443,7 +470,7 @@ int actual_open_alsa_device(void) {
|
||||
|
||||
ret = snd_pcm_hw_params(alsa_handle, alsa_params);
|
||||
if (ret < 0) {
|
||||
die("audio_alsa: Unable to set hw parameters for device \"%s\": %s.", alsa_out_dev,
|
||||
warn("audio_alsa: Unable to set hw parameters for device \"%s\": %s.", alsa_out_dev,
|
||||
snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
@@ -474,6 +501,8 @@ int actual_open_alsa_device(void) {
|
||||
warn("Can't set the D/A converter to sample rate %d.", desired_sample_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
use_monotonic_clock = snd_pcm_hw_params_is_monotonic(alsa_params);
|
||||
|
||||
ret = snd_pcm_hw_params_get_buffer_size(alsa_params, &actual_buffer_length);
|
||||
if (ret < 0) {
|
||||
@@ -500,14 +529,14 @@ int actual_open_alsa_device(void) {
|
||||
/* write the sw parameters */
|
||||
ret = snd_pcm_sw_params(alsa_handle, alsa_swparams);
|
||||
if (ret < 0) {
|
||||
die("audio_alsa: Unable to set software parameters of device: \"%s\": %s.", alsa_out_dev,
|
||||
warn("audio_alsa: Unable to set software parameters of device: \"%s\": %s.", alsa_out_dev,
|
||||
snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_pcm_prepare(alsa_handle);
|
||||
if (ret < 0) {
|
||||
die("audio_alsa: Unable to prepare the device: \"%s\": %s.", alsa_out_dev,
|
||||
warn("audio_alsa: Unable to prepare the device: \"%s\": %s.", alsa_out_dev,
|
||||
snd_strerror(ret));
|
||||
return ret;
|
||||
}
|
||||
@@ -545,10 +574,9 @@ int actual_open_alsa_device(void) {
|
||||
if (config.use_precision_timing == YNA_YES)
|
||||
delay_and_status = precision_delay_and_status;
|
||||
else if (config.use_precision_timing == YNA_AUTO) {
|
||||
const char *output_device_name = snd_pcm_name(alsa_handle);
|
||||
if (strstr(output_device_name,"hw:") == output_device_name) {
|
||||
if (precision_delay_available()) {
|
||||
delay_and_status = precision_delay_and_status;
|
||||
debug(1,"alsa: using precision timing");
|
||||
debug(1,"alsa: precision timing selected for \"auto\" mode");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -932,25 +960,36 @@ static int init(int argc, char **argv) {
|
||||
if (config_lookup_string(config.cfg, "alsa.output_format", &str)) {
|
||||
if (strcasecmp(str, "S16") == 0)
|
||||
config.output_format = SPS_FORMAT_S16;
|
||||
else if (strcasecmp(str, "S16_LE") == 0)
|
||||
config.output_format = SPS_FORMAT_S16_LE;
|
||||
else if (strcasecmp(str, "S16_BE") == 0)
|
||||
config.output_format = SPS_FORMAT_S16_BE;
|
||||
else if (strcasecmp(str, "S24") == 0)
|
||||
config.output_format = SPS_FORMAT_S24;
|
||||
else if (strcasecmp(str, "S24_LE") == 0)
|
||||
config.output_format = SPS_FORMAT_S24_LE;
|
||||
else if (strcasecmp(str, "S24_BE") == 0)
|
||||
config.output_format = SPS_FORMAT_S24_BE;
|
||||
else if (strcasecmp(str, "S24_3LE") == 0)
|
||||
config.output_format = SPS_FORMAT_S24_3LE;
|
||||
else if (strcasecmp(str, "S24_3BE") == 0)
|
||||
config.output_format = SPS_FORMAT_S24_3BE;
|
||||
else if (strcasecmp(str, "S32") == 0)
|
||||
config.output_format = SPS_FORMAT_S32;
|
||||
else if (strcasecmp(str, "S32_LE") == 0)
|
||||
config.output_format = SPS_FORMAT_S32_LE;
|
||||
else if (strcasecmp(str, "S32_BE") == 0)
|
||||
config.output_format = SPS_FORMAT_S32_BE;
|
||||
else if (strcasecmp(str, "U8") == 0)
|
||||
config.output_format = SPS_FORMAT_U8;
|
||||
else if (strcasecmp(str, "S8") == 0)
|
||||
config.output_format = SPS_FORMAT_S8;
|
||||
else {
|
||||
warn("Invalid output format \"%s\". It should be \"U8\", \"S8\", "
|
||||
"\"S16\", \"S24\", "
|
||||
"\"S16\", \"S24\", \"S24_LE\", \"S24_BE\", "
|
||||
"\"S24_3LE\", \"S24_3BE\" or "
|
||||
"\"S32\". It is set to \"S16\".",
|
||||
str);
|
||||
config.output_format = SPS_FORMAT_S16;
|
||||
"\"S32\", \"S32_LE\", \"S32_BE\". It is set to \"%s\".",
|
||||
sps_format_description_string(config.output_format));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1249,23 +1288,23 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
|
||||
if (update_timestamp_ns == 0) {
|
||||
if (delay_type_notified != 1) {
|
||||
inform("Note: the alsa output device \"%s\" is not capable of high precision delay timing.", snd_pcm_name(alsa_handle));
|
||||
debug(2,"alsa: delay_and_status must use snd_pcm_delay() to calculate delay");
|
||||
debug(1,"alsa: delay_and_status must use snd_pcm_delay() to calculate delay");
|
||||
delay_type_notified = 1;
|
||||
}
|
||||
} else {
|
||||
// diagnostic
|
||||
if (delay_type_notified != 0) {
|
||||
debug(2,"alsa: delay_and_status using snd_pcm_status_get_delay() to calculate delay");
|
||||
debug(1,"alsa: delay_and_status using snd_pcm_status_get_delay() to calculate delay");
|
||||
delay_type_notified = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_timestamp_ns == 0) {
|
||||
ret = snd_pcm_delay (alsa_handle,delay);
|
||||
measurement_data_is_valid = 0; // frame rates are likely to be very unreliable if it can't set the update_timestamp, so don't publish them.
|
||||
} else {
|
||||
*delay = snd_pcm_status_get_delay(alsa_snd_pcm_status);
|
||||
|
||||
/*
|
||||
// It seems that the alsa library uses CLOCK_REALTIME before 1.0.28, even though
|
||||
// the check for monotonic returns true. Might have to watch out for this.
|
||||
#if SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR < 28
|
||||
@@ -1273,7 +1312,13 @@ int precision_delay_and_status(snd_pcm_state_t *state, snd_pcm_sframes_t *delay,
|
||||
#else
|
||||
clock_gettime(CLOCK_MONOTONIC, &tn);
|
||||
#endif
|
||||
|
||||
*/
|
||||
|
||||
if (use_monotonic_clock)
|
||||
clock_gettime(CLOCK_MONOTONIC, &tn);
|
||||
else
|
||||
clock_gettime(CLOCK_REALTIME, &tn);
|
||||
|
||||
uint64_t time_now_ns = tn.tv_sec * (uint64_t)1000000000 + tn.tv_nsec;
|
||||
|
||||
// see if it's stalled
|
||||
@@ -1664,70 +1709,6 @@ void alsa_buffer_monitor_thread_cleanup_function(__attribute__((unused)) void
|
||||
}
|
||||
*/
|
||||
|
||||
// this will return true if the DAC can return precision delay information and false if not
|
||||
// if it is not yet known, it will test the output device to find out
|
||||
|
||||
// note -- once it has done the test, it decides -- even if the delay comes back with
|
||||
// "don't know", it will take that as a "No" and remember it.
|
||||
// If you want it to check again, set precision_delay_available_status to YNDK_DONT_KNOW
|
||||
// first.
|
||||
|
||||
|
||||
int precision_delay_available() {
|
||||
if (precision_delay_available_status == YNDK_DONT_KNOW) {
|
||||
// At present, the only criterion as to whether precision delay is available
|
||||
// is whether the device driver returns non-zero update timestamps
|
||||
// If it does, it is considered precision delay is available
|
||||
// Otherwise, it's considered to be unavailable
|
||||
|
||||
// To test, we play a silence buffer (fairly large to avoid underflow)
|
||||
// and then we check the delay return. It will tell us if it
|
||||
// was able to use the (non-zero) update timestamps
|
||||
|
||||
int frames_of_silence = 4410;
|
||||
size_t size_of_silence_buffer = frames_of_silence * frame_size;
|
||||
void *silence = malloc(size_of_silence_buffer);
|
||||
if (silence == NULL) {
|
||||
debug(1, "alsa: precision_delay_available -- failed to "
|
||||
"allocate memory for a "
|
||||
"silent frame buffer.");
|
||||
} else {
|
||||
pthread_cleanup_push(malloc_cleanup, silence);
|
||||
int use_dither = 0;
|
||||
if ((hardware_mixer == 0) && (config.ignore_volume_control == 0) &&
|
||||
(config.airplay_volume != 0.0))
|
||||
use_dither = 1;
|
||||
dither_random_number_store =
|
||||
generate_zero_frames(silence, frames_of_silence, config.output_format,
|
||||
use_dither, // i.e. with dither
|
||||
dither_random_number_store);
|
||||
// debug(1,"Play %d frames of silence with most_recent_write_time of
|
||||
// %" PRIx64 ".",
|
||||
// frames_of_silence,most_recent_write_time);
|
||||
do_play(silence, frames_of_silence);
|
||||
pthread_cleanup_pop(1);
|
||||
// now we can get the delay, and we'll note if it uses update timestamps
|
||||
enum yndk_type uses_update_timestamps;
|
||||
snd_pcm_state_t state;
|
||||
snd_pcm_sframes_t delay;
|
||||
int ret = delay_and_status(&state, &delay, &uses_update_timestamps);
|
||||
// debug(3,"alsa: precision_delay_available asking for delay and status with a return status of %d, a delay of %ld and a uses_update_timestamps of %d.", ret, delay, uses_update_timestamps);
|
||||
if (ret == 0) {
|
||||
if (uses_update_timestamps == YNDK_YES) {
|
||||
precision_delay_available_status = YNDK_YES;
|
||||
debug(2,"alsa: precision delay timing available.");
|
||||
} else {
|
||||
precision_delay_available_status = YNDK_NO;
|
||||
debug(2,"alsa: precision delay timing not available.");
|
||||
if (config.disable_standby_mode != disable_standby_off)
|
||||
inform("Note: disable_standby_mode has been turned off because precision timing is not available.", snd_pcm_name(alsa_handle));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (precision_delay_available_status == YNDK_YES);
|
||||
}
|
||||
|
||||
void *alsa_buffer_monitor_thread_code(__attribute__((unused)) void *arg) {
|
||||
int okb = -1;
|
||||
while (1) {
|
||||
|
||||
@@ -87,6 +87,15 @@
|
||||
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", "invalid" };
|
||||
|
||||
const char * sps_format_description_string(enum sps_format_t format) {
|
||||
if ((format >= SPS_FORMAT_UNKNOWN) && (format < SPS_FORMAT_INVALID))
|
||||
return sps_format_description_string_array[format];
|
||||
else
|
||||
return sps_format_description_string_array[SPS_FORMAT_INVALID];
|
||||
}
|
||||
|
||||
// true if Shairport Sync is supposed to be sending output to the output device, false otherwise
|
||||
|
||||
static volatile int requested_connection_state_to_output = 1;
|
||||
@@ -1381,14 +1390,20 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma
|
||||
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 + 1 - 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 + 1 - 24);
|
||||
break;
|
||||
case SPS_FORMAT_S16:
|
||||
case SPS_FORMAT_S16_LE:
|
||||
case SPS_FORMAT_S16_BE:
|
||||
dither_mask = (int64_t)1 << (64 + 1 - 16);
|
||||
break;
|
||||
case SPS_FORMAT_S8:
|
||||
@@ -1398,6 +1413,9 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma
|
||||
case SPS_FORMAT_UNKNOWN:
|
||||
die("Unexpected SPS_FORMAT_UNKNOWN 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();
|
||||
@@ -1446,6 +1464,22 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma
|
||||
*(int32_t *)op = hyper_sample;
|
||||
result = 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;
|
||||
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;
|
||||
break;
|
||||
case SPS_FORMAT_S16:
|
||||
hyper_sample >>= (64 - 16);
|
||||
*(int16_t *)op = (int16_t)hyper_sample;
|
||||
|
||||
@@ -91,14 +91,24 @@ enum sps_format_t {
|
||||
SPS_FORMAT_S8,
|
||||
SPS_FORMAT_U8,
|
||||
SPS_FORMAT_S16,
|
||||
SPS_FORMAT_S16_LE,
|
||||
SPS_FORMAT_S16_BE,
|
||||
SPS_FORMAT_S24,
|
||||
SPS_FORMAT_S24_LE,
|
||||
SPS_FORMAT_S24_BE,
|
||||
SPS_FORMAT_S24_3LE,
|
||||
SPS_FORMAT_S24_3BE,
|
||||
SPS_FORMAT_S32,
|
||||
SPS_FORMAT_S32_LE,
|
||||
SPS_FORMAT_S32_BE,
|
||||
SPS_FORMAT_INVALID,
|
||||
} sps_format_t;
|
||||
|
||||
const char * sps_format_description_string(enum sps_format_t format);
|
||||
|
||||
typedef struct {
|
||||
config_t *cfg;
|
||||
int endianness;
|
||||
double airplay_volume; // stored here for reloading when necessary
|
||||
char *appName; // normally the app is called shairport-syn, but it may be symlinked
|
||||
char *password;
|
||||
@@ -330,7 +340,6 @@ uint64_t fp_time_at_startup, fp_time_at_last_debug_message;
|
||||
|
||||
// this is for reading an unsigned 32 bit number, such as an RTP timestamp
|
||||
|
||||
long endianness;
|
||||
uint32_t uatoi(const char *nptr);
|
||||
|
||||
// this is for allowing us to cancel the whole program
|
||||
|
||||
@@ -664,14 +664,20 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
|
||||
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 + 1 - 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 + 1 - 24);
|
||||
break;
|
||||
case SPS_FORMAT_S16:
|
||||
case SPS_FORMAT_S16_LE:
|
||||
case SPS_FORMAT_S16_BE:
|
||||
dither_mask = (int64_t)1 << (64 + 1 - 16);
|
||||
break;
|
||||
case SPS_FORMAT_S8:
|
||||
@@ -681,6 +687,9 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
|
||||
case SPS_FORMAT_UNKNOWN:
|
||||
die("Unexpected SPS_FORMAT_UNKNOWN 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();
|
||||
@@ -709,6 +718,30 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
|
||||
char *op = *outp;
|
||||
uint8_t byt;
|
||||
switch (format) {
|
||||
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;
|
||||
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;
|
||||
break;
|
||||
case SPS_FORMAT_S32:
|
||||
hyper_sample >>= (64 - 32);
|
||||
*(int32_t *)op = hyper_sample;
|
||||
@@ -734,11 +767,49 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
|
||||
*op++ = byt;
|
||||
result = 3;
|
||||
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;
|
||||
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;
|
||||
break;
|
||||
case SPS_FORMAT_S24:
|
||||
hyper_sample >>= (64 - 24);
|
||||
*(int32_t *)op = hyper_sample;
|
||||
result = 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;
|
||||
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;
|
||||
break;
|
||||
case SPS_FORMAT_S16:
|
||||
hyper_sample >>= (64 - 16);
|
||||
*(int16_t *)op = (int16_t)hyper_sample;
|
||||
@@ -758,6 +829,9 @@ static inline void process_sample(int32_t sample, char **outp, enum sps_format_t
|
||||
case SPS_FORMAT_UNKNOWN:
|
||||
die("Unexpected SPS_FORMAT_UNKNOWN while outputting samples");
|
||||
break;
|
||||
case SPS_FORMAT_INVALID:
|
||||
die("Unexpected SPS_FORMAT_INVALID while outputting samples");
|
||||
break;
|
||||
}
|
||||
|
||||
*outp += result;
|
||||
@@ -1674,19 +1748,28 @@ void *player_thread_func(void *arg) {
|
||||
output_bit_depth = 8;
|
||||
break;
|
||||
case SPS_FORMAT_S16:
|
||||
case SPS_FORMAT_S16_LE:
|
||||
case SPS_FORMAT_S16_BE:
|
||||
output_bit_depth = 16;
|
||||
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:
|
||||
output_bit_depth = 24;
|
||||
break;
|
||||
case SPS_FORMAT_S32:
|
||||
case SPS_FORMAT_S32_LE:
|
||||
case SPS_FORMAT_S32_BE:
|
||||
output_bit_depth = 32;
|
||||
break;
|
||||
case SPS_FORMAT_UNKNOWN:
|
||||
die("Unknown format choosing output bit depth");
|
||||
break;
|
||||
case SPS_FORMAT_INVALID:
|
||||
die("Invalid format choosing output bit depth");
|
||||
break;
|
||||
}
|
||||
|
||||
debug(3, "Output bit depth is %d.", output_bit_depth);
|
||||
|
||||
@@ -84,7 +84,7 @@ alsa =
|
||||
// mixer_control_name = "PCM"; // the name of the mixer to use to adjust output volume. If not specified, volume in adjusted in software.
|
||||
// mixer_device = "default"; // the mixer_device default is whatever the output_device is. Normally you wouldn't have to use this.
|
||||
// output_rate = 44100; // can be 44100, 88200, 176400 or 352800, but the device must have the capability.
|
||||
// output_format = "S16"; // can be "U8", "S8", "S16", "S24", "S24_3LE", "S24_3BE" or "S32", but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor.
|
||||
// output_format = "S16_LE"; // can be "U8", "S8", "S16", "S16_LE", "S16_BE", "S24", "S24_LE", "S24_BE", "S24_3LE", "S24_3BE", "S32", "S32_LE" or "S32_BE" but the device must have the capability. Except where stated using (*LE or *BE), endianness matches that of the processor.
|
||||
// disable_synchronization = "no"; // Set to "yes" to disable synchronization. Default is "no".
|
||||
// period_size = <number>; // Use this optional advanced setting to set the alsa period size near to this value
|
||||
// buffer_size = <number>; // Use this optional advanced setting to set the alsa buffer size near to this value
|
||||
|
||||
+8
-8
@@ -1378,11 +1378,11 @@ int main(int argc, char **argv) {
|
||||
xn.arr[3] = 0x11; /* Highest-address byte */
|
||||
|
||||
if (xn.u32 == 0x11223344)
|
||||
endianness = SS_LITTLE_ENDIAN;
|
||||
config.endianness = SS_LITTLE_ENDIAN;
|
||||
else if (xn.u32 == 0x33441122)
|
||||
endianness = SS_PDP_ENDIAN;
|
||||
config.endianness = SS_PDP_ENDIAN;
|
||||
else if (xn.u32 == 0x44332211)
|
||||
endianness = SS_BIG_ENDIAN;
|
||||
config.endianness = SS_BIG_ENDIAN;
|
||||
else
|
||||
die("Can not recognise the endianness of the processor.");
|
||||
|
||||
@@ -1425,8 +1425,8 @@ int main(int argc, char **argv) {
|
||||
config.audio_backend_buffer_desired_length = 6615; // 0.15 seconds.
|
||||
config.udp_port_base = 6001;
|
||||
config.udp_port_range = 10;
|
||||
config.output_format = SPS_FORMAT_S16; // default
|
||||
config.output_rate = 44100; // default
|
||||
config.output_format = SPS_FORMAT_S16_LE; // default
|
||||
config.output_rate = 44100; // default
|
||||
config.decoders_supported =
|
||||
1 << decoder_hammerton; // David Hammerton's decoder supported by default
|
||||
#ifdef CONFIG_APPLE_ALAC
|
||||
@@ -1629,7 +1629,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
// daemon_log(LOG_NOTICE, "startup");
|
||||
|
||||
switch (endianness) {
|
||||
switch (config.endianness) {
|
||||
case SS_LITTLE_ENDIAN:
|
||||
debug(2, "The processor is running little-endian.");
|
||||
break;
|
||||
@@ -1712,8 +1712,8 @@ int main(int argc, char **argv) {
|
||||
debug(1, "use_mmap_if_available is %d.", config.no_mmap ? 0 : 1);
|
||||
debug(1, "output_rate is %d.", config.output_rate);
|
||||
debug(1,
|
||||
"output_format is %d (0-unknown, 1-S8, 2-U8, 3-S16, 4-S24, 5-S24_3LE, 6-S24_3BE, 7-S32).",
|
||||
config.output_format);
|
||||
"output_format is \"%s\".",
|
||||
sps_format_description_string(config.output_format));
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user