Skip to content

Commit

Permalink
add configurable silence delay on rate change in similar manner to do…
Browse files Browse the repository at this point in the history
…p delay
  • Loading branch information
triode committed Mar 11, 2014
1 parent 99df760 commit d8ad375
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 59 deletions.
78 changes: 43 additions & 35 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static void usage(const char *argv0) {
#if ALSA
" -p <priority>\t\tSet real time priority of output thread (1-99)\n"
#endif
" -r <rates>\t\tSpecify sample rates supported by device, enables output device to be off when squeezelite is started; rates = <maxrate> | <minrate>-<maxrate> | <rate1>,<rate2>,<rate3>\n"
" -r <rates>[:<delay>]\tSample rates supported, allows output to be off when squeezelite is started; rates = <maxrate>|<minrate>-<maxrate>|<rate1>,<rate2>,<rate3>; delay = optional delay switching rates in ms\n"
#if RESAMPLE
" -R -u [params]\tResample, params = <recipe>:<flags>:<attenuation>:<precision>:<passband_end>:<stopband_start>:<phase_response>,\n"
" \t\t\t recipe = (v|h|m|l|q)(L|I|M)(s) [E|X], E = exception - resample only if native rate not supported, X = async - resample to max rate for device, otherwise to max sync rate\n"
Expand Down Expand Up @@ -159,6 +159,7 @@ int main(int argc, char **argv) {
unsigned stream_buf_size = STREAMBUF_SIZE;
unsigned output_buf_size = 0; // set later
unsigned rates[MAX_SUPPORTED_SAMPLERATES] = { 0 };
unsigned rate_delay = 0;
char *resample = NULL;
char *output_params = NULL;
#if LINUX
Expand Down Expand Up @@ -260,41 +261,48 @@ int main(int argc, char **argv) {
}
break;
case 'r':
if (strstr(optarg,",")) {
// parse sample rates and sort them
char *r = next_param(optarg, ',');
unsigned tmp[MAX_SUPPORTED_SAMPLERATES] = { 0 };
int i, j;
int last = 999999;
for (i = 0; r && i < MAX_SUPPORTED_SAMPLERATES; ++i) {
tmp[i] = atoi(r);
r = next_param(NULL, ',');
}
for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) {
int largest = 0;
for (j = 0; j < MAX_SUPPORTED_SAMPLERATES; ++j) {
if (tmp[j] > largest && tmp[j] < last) {
largest = tmp[j];
{
char *rstr = next_param(optarg, ':');
char *dstr = next_param(NULL, ':');
if (rstr && strstr(rstr, ",")) {
// parse sample rates and sort them
char *r = next_param(rstr, ',');
unsigned tmp[MAX_SUPPORTED_SAMPLERATES] = { 0 };
int i, j;
int last = 999999;
for (i = 0; r && i < MAX_SUPPORTED_SAMPLERATES; ++i) {
tmp[i] = atoi(r);
r = next_param(NULL, ',');
}
for (i = 0; i < MAX_SUPPORTED_SAMPLERATES; ++i) {
int largest = 0;
for (j = 0; j < MAX_SUPPORTED_SAMPLERATES; ++j) {
if (tmp[j] > largest && tmp[j] < last) {
largest = tmp[j];
}
}
rates[i] = last = largest;
}
rates[i] = last = largest;
}
} else {
// optstr is <min>-<max> or <max>, extract rates from test rates within this range
unsigned ref[] TEST_RATES;
char *str1 = next_param(optarg, '-');
char *str2 = next_param(NULL, '-');
unsigned max = str2 ? atoi(str2) : (str1 ? atoi(str1) : ref[0]);
unsigned min = str1 && str2 ? atoi(str1) : 0;
unsigned tmp;
int i, j;
if (max < min) { tmp = max; max = min; min = tmp; }
rates[0] = max;
for (i = 0, j = 1; i < MAX_SUPPORTED_SAMPLERATES; ++i) {
if (ref[i] < rates[j-1] && ref[i] >= min) {
rates[j++] = ref[i];
} else if (rstr) {
// optstr is <min>-<max> or <max>, extract rates from test rates within this range
unsigned ref[] TEST_RATES;
char *str1 = next_param(rstr, '-');
char *str2 = next_param(NULL, '-');
unsigned max = str2 ? atoi(str2) : (str1 ? atoi(str1) : ref[0]);
unsigned min = str1 && str2 ? atoi(str1) : 0;
unsigned tmp;
int i, j;
if (max < min) { tmp = max; max = min; min = tmp; }
rates[0] = max;
for (i = 0, j = 1; i < MAX_SUPPORTED_SAMPLERATES; ++i) {
if (ref[i] < rates[j-1] && ref[i] >= min) {
rates[j++] = ref[i];
}
}
}
if (dstr) {
rate_delay = atoi(dstr);
}
}
break;
case 's':
Expand Down Expand Up @@ -405,13 +413,13 @@ int main(int argc, char **argv) {
stream_init(log_stream, stream_buf_size);

if (!strcmp(output_device, "-")) {
output_init_stdout(log_output, output_buf_size, output_params, rates);
output_init_stdout(log_output, output_buf_size, output_params, rates, rate_delay);
} else {
#if ALSA
output_init_alsa(log_output, output_device, output_buf_size, output_params, rates, rt_priority);
output_init_alsa(log_output, output_device, output_buf_size, output_params, rates, rate_delay, rt_priority);
#endif
#if PORTAUDIO
output_init_pa(log_output, output_device, output_buf_size, output_params, rates);
output_init_pa(log_output, output_device, output_buf_size, output_params, rates, rate_delay);
#endif
}

Expand Down
41 changes: 24 additions & 17 deletions output.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,28 +119,35 @@ frames_t _output_frames(frames_t avail) {

if (output.track_start && !silence) {
if (output.track_start == outputbuf->readp) {
frames -= size;
unsigned delay = 0;
if (output.current_sample_rate != output.next_sample_rate) {
delay = output.rate_delay;
}
IF_DSD(
if (output.dop != output.next_dop) {
if (output.dop_delay) {
// add silence delay in two halves, before and after track start and pcm-dop change
if (!output.dop_delay_active) {
output.pause_frames = output.current_sample_rate * output.dop_delay / 2000;
output.dop_delay_active = true; // first delay - don't process track start
break;
} else {
output.pause_frames = output.next_sample_rate * output.dop_delay / 2000;
output.dop_delay_active = false; // second delay - process track start
}
output.state = OUTPUT_PAUSE_FRAMES;
}
}
output.dop = output.next_dop;
if (output.dop != output.next_dop) {
delay = output.dop_delay;
}
)
frames -= size;
// add silence delay in two halves, before and after track start and rate or pcm-dop change
if (delay) {
output.state = OUTPUT_PAUSE_FRAMES;
if (!output.delay_active) {
output.pause_frames = output.current_sample_rate * delay / 2000;
output.delay_active = true; // first delay - don't process track start
break;
} else {
output.pause_frames = output.next_sample_rate * delay / 2000;
output.delay_active = false; // second delay - process track start
}
}
LOG_INFO("track start sample rate: %u replay_gain: %u", output.next_sample_rate, output.next_replay_gain);
output.frames_played = 0;
output.track_started = true;
output.current_sample_rate = output.next_sample_rate;
IF_DSD(
output.dop = output.next_dop;
)
if (!output.fade == FADE_ACTIVE || !output.fade_mode == FADE_CROSSFADE) {
output.current_replay_gain = output.next_replay_gain;
}
Expand Down Expand Up @@ -413,7 +420,7 @@ void output_flush(void) {
if (output.error_opening) {
output.current_sample_rate = output.default_sample_rate;
}
IF_DSD( output.dop_delay_active = false; )
IF_DSD( output.delay_active = false; )
}
output.frames_played = 0;
UNLOCK;
Expand Down
4 changes: 3 additions & 1 deletion output_alsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,8 @@ static void *output_thread(void *arg) {

static pthread_t thread;

void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rt_priority) {
void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[],
unsigned rate_delay, unsigned rt_priority) {

unsigned alsa_buffer = ALSA_BUFFER_TIME;
unsigned alsa_period = ALSA_PERIOD_COUNT;
Expand Down Expand Up @@ -646,6 +647,7 @@ void output_init_alsa(log_level level, const char *device, unsigned output_buf_s
output.period = alsa_period;
output.start_frames = 0;
output.write_cb = &_write_frames;
output.rate_delay = rate_delay;

if (alsa_sample_fmt) {
if (!strcmp(alsa_sample_fmt, "32")) alsa.format = SND_PCM_FORMAT_S32_LE;
Expand Down
3 changes: 2 additions & 1 deletion output_pa.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ static int pa_callback(const void *pa_input, void *pa_output, unsigned long pa_f
return ret;
}

void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[]) {
void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay) {
PaError err;
unsigned latency = 0;
int osx_playnice = -1;
Expand All @@ -391,6 +391,7 @@ void output_init_pa(log_level level, const char *device, unsigned output_buf_siz
output.format = 0;
output.start_frames = 0;
output.write_cb = &_write_frames;
output.rate_delay = rate_delay;
pa.stream = NULL;

LOG_INFO("requested latency: %u", output.latency);
Expand Down
3 changes: 2 additions & 1 deletion output_stdout.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ static void *output_thread() {

static thread_type thread;

void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[]) {
void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay) {
loglevel = level;

LOG_INFO("init output stdout");
Expand All @@ -136,6 +136,7 @@ void output_init_stdout(log_level level, unsigned output_buf_size, char *params,
output.format = S32_LE;
output.start_frames = FRAME_BLOCK * 2;
output.write_cb = &_stdout_write_frames;
output.rate_delay = rate_delay;

if (params) {
if (!strcmp(params, "32")) output.format = S32_LE;
Expand Down
10 changes: 6 additions & 4 deletions squeezelite.h
Original file line number Diff line number Diff line change
Expand Up @@ -524,12 +524,13 @@ struct outputstate {
fade_dir fade_dir;
fade_mode fade_mode; // set by slimproto
unsigned fade_secs; // set by slimproto
unsigned rate_delay;
bool delay_active;
#if DSD
bool next_dop; // set in decode thread
bool dop;
bool has_dop; // set in dop_init - output device supports dop
unsigned dop_delay; // set in dop_init - delay in ms switching to/from dop
bool dop_delay_active;
#endif
};

Expand All @@ -544,21 +545,22 @@ void _checkfade(bool);
#if ALSA
void list_devices(void);
bool test_open(const char *device, unsigned rates[]);
void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rt_priority);
void output_init_alsa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[],
unsigned rate_delay, unsigned rt_priority);
void output_close_alsa(void);
#endif

// output_pa.c
#if PORTAUDIO
void list_devices(void);
bool test_open(const char *device, unsigned rates[]);
void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[]);
void output_init_pa(log_level level, const char *device, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay);
void output_close_pa(void);
void _pa_open(void);
#endif

// output_stdout.c
void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[]);
void output_init_stdout(log_level level, unsigned output_buf_size, char *params, unsigned rates[], unsigned rate_delay);
void output_close_stdout(void);

// output_pack.c
Expand Down

0 comments on commit d8ad375

Please sign in to comment.