alsaloop: added resampling for unsupported soundcard rates

- improve also xrun synchronization

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2010-10-07 20:38:36 +02:00
parent f956c329aa
commit 058357f042
4 changed files with 224 additions and 82 deletions

View file

@ -79,7 +79,7 @@ static int create_loopback_handle(struct loopback_handle **_handle,
handle->id = strdup(idbuf); handle->id = strdup(idbuf);
handle->access = SND_PCM_ACCESS_RW_INTERLEAVED; handle->access = SND_PCM_ACCESS_RW_INTERLEAVED;
handle->format = SND_PCM_FORMAT_S16_LE; handle->format = SND_PCM_FORMAT_S16_LE;
handle->rate = 48000; handle->rate = handle->rate_req = 48000;
handle->channels = 2; handle->channels = 2;
handle->resample = 0; handle->resample = 0;
*_handle = handle; *_handle = handle;
@ -311,7 +311,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
int arg_nblock = 0; int arg_nblock = 0;
int arg_effect = 0; int arg_effect = 0;
int arg_resample = 0; int arg_resample = 0;
int arg_samplerate = 0; int arg_samplerate = SRC_SINC_FASTEST + 1;
int arg_sync = SYNC_TYPE_AUTO; int arg_sync = SYNC_TYPE_AUTO;
int arg_slave = SLAVE_TYPE_AUTO; int arg_slave = SLAVE_TYPE_AUTO;
int arg_thread = 0; int arg_thread = 0;
@ -474,7 +474,7 @@ static int parse_config(int argc, char *argv[], snd_output_t *output)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
play->format = capt->format = arg_format; play->format = capt->format = arg_format;
play->rate = capt->rate = arg_rate; play->rate = play->rate_req = capt->rate = capt->rate_req = arg_rate;
play->channels = capt->channels = arg_channels; play->channels = capt->channels = arg_channels;
play->buffer_size_req = capt->buffer_size_req = arg_buffer_size; play->buffer_size_req = capt->buffer_size_req = arg_buffer_size;
play->period_size_req = capt->period_size_req = arg_period_size; play->period_size_req = capt->period_size_req = arg_period_size;

View file

@ -79,6 +79,7 @@ struct loopback_handle {
snd_pcm_access_t access; snd_pcm_access_t access;
snd_pcm_format_t format; snd_pcm_format_t format;
unsigned int rate; unsigned int rate;
unsigned int rate_req;
unsigned int channels; unsigned int channels;
unsigned int buffer_size; unsigned int buffer_size;
unsigned int period_size; unsigned int period_size;
@ -100,6 +101,8 @@ struct loopback_handle {
unsigned long long counter; unsigned long long counter;
unsigned long sync_point; /* in samples */ unsigned long sync_point; /* in samples */
snd_pcm_sframes_t last_delay; snd_pcm_sframes_t last_delay;
double pitch;
snd_pcm_uframes_t total_queued;
/* control */ /* control */
snd_ctl_t *ctl; snd_ctl_t *ctl;
unsigned int ctl_pollfd_count; unsigned int ctl_pollfd_count;
@ -135,15 +138,15 @@ struct loopback {
snd_pcm_sframes_t pitch_diff; snd_pcm_sframes_t pitch_diff;
snd_pcm_sframes_t pitch_diff_min; snd_pcm_sframes_t pitch_diff_min;
snd_pcm_sframes_t pitch_diff_max; snd_pcm_sframes_t pitch_diff_max;
snd_pcm_uframes_t total_queued;
unsigned int total_queued_count; unsigned int total_queued_count;
snd_timestamp_t tstamp_start; snd_timestamp_t tstamp_start;
snd_timestamp_t tstamp_end; snd_timestamp_t tstamp_end;
/* control mixer */ /* control mixer */
struct loopback_mixer *controls; struct loopback_mixer *controls;
/* sample rate */ /* sample rate */
unsigned int use_samplerate:1;
#ifdef USE_SAMPLERATE #ifdef USE_SAMPLERATE
unsigned int src_enable: 1; unsigned int src_enable:1;
int src_converter_type; int src_converter_type;
SRC_STATE *src_state; SRC_STATE *src_state;
SRC_DATA src_data; SRC_DATA src_data;

View file

@ -99,18 +99,20 @@ static int setparams_stream(struct loopback_handle *lhandle,
logit(LOG_CRIT, "Channels count (%i) not available for %s: %s\n", lhandle->channels, lhandle->id, snd_strerror(err)); logit(LOG_CRIT, "Channels count (%i) not available for %s: %s\n", lhandle->channels, lhandle->id, snd_strerror(err));
return err; return err;
} }
rrate = lhandle->rate; rrate = lhandle->rate_req;
err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
if (err < 0) { if (err < 0) {
logit(LOG_CRIT, "Rate %iHz not available for %s: %s\n", lhandle->rate, lhandle->id, snd_strerror(err)); logit(LOG_CRIT, "Rate %iHz not available for %s: %s\n", lhandle->rate_req, lhandle->id, snd_strerror(err));
return err; return err;
} }
rrate = 0; rrate = 0;
snd_pcm_hw_params_get_rate(params, &rrate, 0); snd_pcm_hw_params_get_rate(params, &rrate, 0);
if ((int)rrate != lhandle->rate) { lhandle->rate = rrate;
if (!lhandle->loopback->src_enable && (int)rrate != lhandle->rate) {
logit(LOG_CRIT, "Rate does not match (requested %iHz, got %iHz, resample %i)\n", lhandle->rate, rrate, lhandle->resample); logit(LOG_CRIT, "Rate does not match (requested %iHz, got %iHz, resample %i)\n", lhandle->rate, rrate, lhandle->resample);
return -EINVAL; return -EINVAL;
} }
lhandle->pitch = (double)lhandle->rate_req / (double)lhandle->rate;
return 0; return 0;
} }
@ -245,11 +247,11 @@ static int setparams(struct loopback *loop, snd_pcm_uframes_t bufsize)
return err; return err;
} }
if ((err = setparams_bufsize(loop->play, p_params, pt_params, bufsize)) < 0) { if ((err = setparams_bufsize(loop->play, p_params, pt_params, bufsize / loop->play->pitch)) < 0) {
logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->play->id, snd_strerror(err)); logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->play->id, snd_strerror(err));
return err; return err;
} }
if ((err = setparams_bufsize(loop->capt, c_params, ct_params, bufsize)) < 0) { if ((err = setparams_bufsize(loop->capt, c_params, ct_params, bufsize / loop->capt->pitch)) < 0) {
logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err)); logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err));
return err; return err;
} }
@ -618,6 +620,7 @@ static snd_pcm_sframes_t remove_samples(struct loopback *loop,
} else { } else {
if (count > play->buf_count) if (count > play->buf_count)
count = play->buf_count; count = play->buf_count;
play->buf_count -= count;
} }
return count; return count;
} }
@ -627,7 +630,7 @@ static int xrun_sync(struct loopback *loop)
struct loopback_handle *play = loop->play; struct loopback_handle *play = loop->play;
struct loopback_handle *capt = loop->capt; struct loopback_handle *capt = loop->capt;
snd_pcm_uframes_t fill = get_whole_latency(loop); snd_pcm_uframes_t fill = get_whole_latency(loop);
snd_pcm_sframes_t delay, delayi, pdelay, cdelay, diff; snd_pcm_sframes_t pdelay, cdelay, delay1, pdelay1, cdelay1, diff;
int err; int err;
__again: __again:
@ -681,45 +684,82 @@ static int xrun_sync(struct loopback *loop)
} }
capt->counter = cdelay; capt->counter = cdelay;
play->counter = pdelay; play->counter = pdelay;
loop->total_queued = 0; if (play->buf != capt->buf)
cdelay += capt->buf_count;
pdelay += play->buf_count;
#ifdef USE_SAMPLERATE
pdelay += loop->src_out_frames;
#endif
cdelay1 = cdelay * capt->pitch;
pdelay1 = pdelay * play->pitch;
delay1 = cdelay1 + pdelay1;
capt->total_queued = 0;
play->total_queued = 0;
loop->total_queued_count = 0; loop->total_queued_count = 0;
loop->pitch_diff = loop->pitch_diff_min = loop->pitch_diff_max = 0; loop->pitch_diff = loop->pitch_diff_min = loop->pitch_diff_max = 0;
delay = delayi = pdelay + cdelay; if (verbose > 6) {
if (play->buf != capt->buf) snd_output_printf(loop->output,
delay += capt->buf_count; "sync: cdelay=%li(%li), pdelay=%li(%li), fill=%li (delay=%li), src_out=%li\n",
delay += play->buf_count; (long)cdelay, (long)cdelay1, (long)pdelay, (long)pdelay1,
#ifdef USE_SAMPLERATE (long)fill, (long)delay1, (long)loop->src_out_frames);
delay += loop->src_out_frames; snd_output_printf(loop->output,
delayi += loop->src_out_frames; "sync: cbufcount=%li, pbufcount=%li\n",
#endif (long)capt->buf_count, (long)play->buf_count);
#if 0 }
printf("s: cdelay = %li, pdelay = %li, delay = %li, delayi = %li, fill = %li, src_out = %li\n", if (delay1 > fill && capt->counter > 0) {
(long)cdelay, (long)pdelay, (long)delay,
(long)delayi, (long)fill, (long)loop->src_out_frames);
printf("s: cbufcount = %li, pbufcount = %li\n", (long)capt->buf_count, (long)play->buf_count);
#endif
if (delayi > fill) {
if ((err = snd_pcm_drop(capt->handle)) < 0) if ((err = snd_pcm_drop(capt->handle)) < 0)
return err; return err;
if ((err = snd_pcm_prepare(capt->handle)) < 0) if ((err = snd_pcm_prepare(capt->handle)) < 0)
return err; return err;
if ((err = snd_pcm_start(capt->handle)) < 0) if ((err = snd_pcm_start(capt->handle)) < 0)
return err; return err;
remove_samples(loop, 1, delayi - fill); diff = remove_samples(loop, 1, (delay1 - fill) / capt->pitch);
if (verbose > 6)
snd_output_printf(loop->output,
"sync: capt stop removed %li samples\n", (long)diff);
goto __again; goto __again;
} }
if (delay > fill) { if (delay1 > fill) {
diff = fill > delayi ? play->buf_count - (fill - delayi) : 0; diff = (delay1 - fill) / play->pitch;
delay -= remove_samples(loop, 0, diff); if (diff > play->buf_count)
diff = play->buf_count;
if (verbose > 6)
snd_output_printf(loop->output,
"sync: removing %li playback samples, delay1=%li\n", (long)diff, (long)delay1);
diff = remove_samples(loop, 0, diff);
pdelay -= diff;
pdelay1 = pdelay * play->pitch;
delay1 = cdelay1 + pdelay1;
if (verbose > 6)
snd_output_printf(loop->output,
"sync: removed %li playback samples, delay1=%li\n", (long)diff, (long)delay1);
} }
if (delay > fill) { if (delay1 > fill) {
diff = fill > delayi ? capt->buf_count - (fill - delayi) : 0; diff = (delay1 - fill) / capt->pitch;
delay -= remove_samples(loop, 1, diff); if (diff > capt->buf_count)
diff = capt->buf_count;
if (verbose > 6)
snd_output_printf(loop->output,
"sync: removing %li captured samples, delay1=%li\n", (long)diff, (long)delay1);
diff -= remove_samples(loop, 1, diff);
cdelay -= diff;
cdelay1 = cdelay * capt->pitch;
delay1 = cdelay1 + pdelay1;
if (verbose > 6)
snd_output_printf(loop->output,
"sync: removed %li captured samples, delay1=%li\n", (long)diff, (long)delay1);
} }
if (play->xrun_pending) { if (play->xrun_pending) {
play->xrun_pending = 0; play->xrun_pending = 0;
if (fill > delay && play->buf_count < fill - delay) { diff = (fill - delay1) / play->pitch;
diff = fill - delay - play->buf_count; if (verbose > 6)
snd_output_printf(loop->output,
"sync: xrun_pending, silence filling %li / buf_count=%li\n", (long)diff, play->buf_count);
if (fill > delay1 && play->buf_count < diff) {
diff = diff - play->buf_count;
if (verbose > 6)
snd_output_printf(loop->output,
"sync: playback silence added %li samples\n", (long)diff);
play->buf_pos -= diff; play->buf_pos -= diff;
play->buf_pos %= play->buf_size; play->buf_pos %= play->buf_size;
if ((err = snd_pcm_format_set_silence(play->format, play->buf + play->buf_pos * play->channels, diff)) < 0) if ((err = snd_pcm_format_set_silence(play->format, play->buf + play->buf_pos * play->channels, diff)) < 0)
@ -728,18 +768,43 @@ static int xrun_sync(struct loopback *loop)
} }
if ((err = snd_pcm_prepare(play->handle)) < 0) { if ((err = snd_pcm_prepare(play->handle)) < 0) {
logit(LOG_CRIT, "%s prepare failed: %s\n", play->id, snd_strerror(err)); logit(LOG_CRIT, "%s prepare failed: %s\n", play->id, snd_strerror(err));
return err; return err;
} }
delayi = writeit(play); delay1 = writeit(play);
if (delayi > diff) if (verbose > 6)
buf_remove(loop, delayi - diff); snd_output_printf(loop->output,
"sync: playback wrote %li samples\n", (long)delay1);
if (delay1 > diff) {
buf_remove(loop, delay1 - diff);
if (verbose > 6)
snd_output_printf(loop->output,
"sync: playback buf_remove %li samples\n", (long)(delay1 - diff));
}
if ((err = snd_pcm_start(play->handle)) < 0) { if ((err = snd_pcm_start(play->handle)) < 0) {
logit(LOG_CRIT, "%s start failed: %s\n", play->id, snd_strerror(err)); logit(LOG_CRIT, "%s start failed: %s\n", play->id, snd_strerror(err));
return err; return err;
} }
} }
if (verbose > 5) if (verbose > 5) {
snd_output_printf(loop->output, "%s: xrun sync ok\n", loop->id); snd_output_printf(loop->output, "%s: xrun sync ok\n", loop->id);
if (verbose > 6) {
if (snd_pcm_delay(capt->handle, &cdelay) < 0)
cdelay = -1;
if (snd_pcm_delay(play->handle, &pdelay) < 0)
pdelay = -1;
if (play->buf != capt->buf)
cdelay += capt->buf_count;
pdelay += play->buf_count;
#ifdef USE_SAMPLERATE
pdelay += loop->src_out_frames;
#endif
cdelay1 = cdelay * capt->pitch;
pdelay1 = pdelay * play->pitch;
delay1 = cdelay1 + pdelay1;
snd_output_printf(loop->output, "%s: sync verify: %li\n", loop->id, delay1);
}
}
return 0; return 0;
} }
@ -778,6 +843,46 @@ static int set_rate_shift(struct loopback_handle *lhandle, double pitch)
return 0; return 0;
} }
void update_pitch(struct loopback *loop)
{
double pitch = loop->pitch;
#ifdef USE_SAMPLERATE
if (loop->sync == SYNC_TYPE_SAMPLERATE) {
loop->src_data.src_ratio = (double)1.0 / (pitch *
loop->play->pitch * loop->capt->pitch);
if (verbose > 2)
snd_output_printf(loop->output, "%s: Samplerate src_ratio update1: %.8f\n", loop->id, loop->src_data.src_ratio);
} else
#endif
if (loop->sync == SYNC_TYPE_CAPTRATESHIFT) {
set_rate_shift(loop->capt, pitch);
#ifdef USE_SAMPLERATE
if (loop->use_samplerate) {
loop->src_data.src_ratio =
(double)1.0 /
(loop->play->pitch * loop->capt->pitch);
if (verbose > 2)
snd_output_printf(loop->output, "%s: Samplerate src_ratio update2: %.8f\n", loop->id, loop->src_data.src_ratio);
}
#endif
}
else if (loop->sync == SYNC_TYPE_PLAYRATESHIFT) {
set_rate_shift(loop->play, pitch);
#ifdef USE_SAMPLERATE
if (loop->use_samplerate) {
loop->src_data.src_ratio =
(double)1.0 /
(loop->play->pitch * loop->capt->pitch);
if (verbose > 2)
snd_output_printf(loop->output, "%s: Samplerate src_ratio update3: %.8f\n", loop->id, loop->src_data.src_ratio);
}
#endif
}
if (verbose)
snd_output_printf(loop->output, "New pitch for %s: %.8f (min/max samples = %li/%li)\n", loop->id, pitch, loop->pitch_diff_min, loop->pitch_diff_max);
}
static int get_active(struct loopback_handle *lhandle) static int get_active(struct loopback_handle *lhandle)
{ {
int err; int err;
@ -1002,6 +1107,8 @@ int pcmjob_init(struct loopback *loop)
#ifdef USE_SAMPLERATE #ifdef USE_SAMPLERATE
if (loop->sync == SYNC_TYPE_AUTO && loop->src_enable) if (loop->sync == SYNC_TYPE_AUTO && loop->src_enable)
loop->sync = SYNC_TYPE_SAMPLERATE; loop->sync = SYNC_TYPE_SAMPLERATE;
#else
loop->src_enable = 0;
#endif #endif
if (loop->sync == SYNC_TYPE_AUTO) if (loop->sync == SYNC_TYPE_AUTO)
loop->sync = SYNC_TYPE_SIMPLE; loop->sync = SYNC_TYPE_SIMPLE;
@ -1035,8 +1142,9 @@ int pcmjob_init(struct loopback *loop)
static void freeloop(struct loopback *loop) static void freeloop(struct loopback *loop)
{ {
#ifdef USE_SAMPLERATE #ifdef USE_SAMPLERATE
if (loop->src_enable) { if (loop->use_samplerate) {
src_delete(loop->src_state); if (loop->src_state)
src_delete(loop->src_state);
loop->src_state = NULL; loop->src_state = NULL;
free(loop->src_data.data_in); free(loop->src_data.data_in);
loop->src_data.data_in = NULL; loop->src_data.data_in = NULL;
@ -1078,6 +1186,7 @@ static void lhandle_start(struct loopback_handle *lhandle)
lhandle->buf_pos = 0; lhandle->buf_pos = 0;
lhandle->buf_count = 0; lhandle->buf_count = 0;
lhandle->counter = 0; lhandle->counter = 0;
lhandle->total_queued = 0;
} }
int pcmjob_start(struct loopback *loop) int pcmjob_start(struct loopback *loop)
@ -1098,7 +1207,7 @@ int pcmjob_start(struct loopback *loop)
err = get_rate(loop->capt); err = get_rate(loop->capt);
if (err < 0) if (err < 0)
goto __error; goto __error;
loop->play->rate = loop->capt->rate = err; loop->play->rate_req = loop->capt->rate_req = err;
err = get_channels(loop->capt); err = get_channels(loop->capt);
if (err < 0) if (err < 0)
goto __error; goto __error;
@ -1107,6 +1216,7 @@ int pcmjob_start(struct loopback *loop)
loop->pollfd_count = loop->play->ctl_pollfd_count + loop->pollfd_count = loop->play->ctl_pollfd_count +
loop->capt->ctl_pollfd_count; loop->capt->ctl_pollfd_count;
loop->reinit = 0; loop->reinit = 0;
loop->use_samplerate = 0;
loop->latency = loop->latency_req; loop->latency = loop->latency_req;
if (loop->latency == 0) if (loop->latency == 0)
loop->latency = time_to_frames(loop->play, loop->latency = time_to_frames(loop->play,
@ -1114,7 +1224,7 @@ int pcmjob_start(struct loopback *loop)
if ((err = setparams(loop, loop->latency/2)) < 0) if ((err = setparams(loop, loop->latency/2)) < 0)
goto __error; goto __error;
if (verbose) if (verbose)
showlatency(loop, loop->latency, loop->play->rate); showlatency(loop, loop->latency, loop->play->rate_req);
if (loop->play->access == loop->capt->access && if (loop->play->access == loop->capt->access &&
loop->play->format == loop->capt->format && loop->play->format == loop->capt->format &&
loop->play->rate == loop->capt->rate && loop->play->rate == loop->capt->rate &&
@ -1142,6 +1252,10 @@ int pcmjob_start(struct loopback *loop)
goto __error; goto __error;
if ((err = init_handle(loop->capt, 1)) < 0) if ((err = init_handle(loop->capt, 1)) < 0)
goto __error; goto __error;
if (loop->play->rate_req != loop->play->rate)
loop->use_samplerate = 1;
if (loop->capt->rate_req != loop->capt->rate)
loop->use_samplerate = 1;
} }
if ((err = snd_pcm_poll_descriptors_count(loop->play->handle)) < 0) if ((err = snd_pcm_poll_descriptors_count(loop->play->handle)) < 0)
goto __error; goto __error;
@ -1152,7 +1266,22 @@ int pcmjob_start(struct loopback *loop)
loop->capt->pollfd_count = err; loop->capt->pollfd_count = err;
loop->pollfd_count += err; loop->pollfd_count += err;
#ifdef USE_SAMPLERATE #ifdef USE_SAMPLERATE
if (loop->sync == SYNC_TYPE_SAMPLERATE) { if (loop->sync == SYNC_TYPE_SAMPLERATE)
loop->use_samplerate = 1;
if (loop->use_samplerate && !loop->src_enable) {
logit(LOG_CRIT, "samplerate conversion required but disabled\n");
loop->use_samplerate = 0;
err = -EIO;
goto __error;
}
if (loop->use_samplerate) {
if (loop->capt->format != SND_PCM_FORMAT_S16 ||
loop->play->format != SND_PCM_FORMAT_S16) {
logit(LOG_CRIT, "samplerate conversion supports only S16_LE format (%i, %i)\n", loop->play->format, loop->capt->format);
loop->use_samplerate = 0;
err = -EIO;
goto __error;
}
loop->src_state = src_new(loop->src_converter_type, loop->src_state = src_new(loop->src_converter_type,
loop->play->channels, &err); loop->play->channels, &err);
loop->src_data.data_in = calloc(1, sizeof(float)*loop->capt->channels*loop->capt->buf_size); loop->src_data.data_in = calloc(1, sizeof(float)*loop->capt->channels*loop->capt->buf_size);
@ -1173,7 +1302,7 @@ int pcmjob_start(struct loopback *loop)
loop->src_state = NULL; loop->src_state = NULL;
} }
#else #else
if (loop->sync == SYNC_TYPE_SAMPLERATE) { if (loop->sync == SYNC_TYPE_SAMPLERATE || loop->use_samplerate) {
logit(LOG_CRIT, "alsaloop is compiled without libsamplerate support\n"); logit(LOG_CRIT, "alsaloop is compiled without libsamplerate support\n");
err = -EIO; err = -EIO;
goto __error; goto __error;
@ -1195,12 +1324,12 @@ int pcmjob_start(struct loopback *loop)
logit(LOG_CRIT, "%s: silence error\n", loop->id); logit(LOG_CRIT, "%s: silence error\n", loop->id);
goto __error; goto __error;
} }
loop->pitch = 1; loop->pitch = 1.0;
update_pitch(loop);
loop->pitch_delta = 1.0 / ((double)loop->capt->rate * 4); loop->pitch_delta = 1.0 / ((double)loop->capt->rate * 4);
loop->total_queued = 0;
loop->total_queued_count = 0; loop->total_queued_count = 0;
loop->pitch_diff = 0; loop->pitch_diff = 0;
count = get_whole_latency(loop); count = get_whole_latency(loop) / loop->play->pitch;
loop->play->buf_count = count; loop->play->buf_count = count;
if (loop->play->buf == loop->capt->buf) if (loop->play->buf == loop->capt->buf)
loop->capt->buf_pos = count; loop->capt->buf_pos = count;
@ -1277,19 +1406,14 @@ int pcmjob_pollfds_init(struct loopback *loop, struct pollfd *fds)
return idx; return idx;
} }
static snd_pcm_sframes_t get_queued_samples(struct loopback *loop) static snd_pcm_sframes_t get_queued_playback_samples(struct loopback *loop)
{ {
snd_pcm_sframes_t pdelay, cdelay, delay; snd_pcm_sframes_t delay;
int err; int err;
if ((err = snd_pcm_delay(loop->play->handle, &pdelay)) < 0) if ((err = snd_pcm_delay(loop->play->handle, &delay)) < 0)
return 0; return 0;
if ((err = snd_pcm_delay(loop->capt->handle, &cdelay)) < 0) loop->play->last_delay = delay;
return 0;
loop->play->last_delay = pdelay;
loop->capt->last_delay = cdelay;
delay = pdelay + cdelay;
delay += loop->capt->buf_count;
delay += loop->play->buf_count; delay += loop->play->buf_count;
#ifdef USE_SAMPLERATE #ifdef USE_SAMPLERATE
delay += loop->src_out_frames; delay += loop->src_out_frames;
@ -1297,6 +1421,18 @@ static snd_pcm_sframes_t get_queued_samples(struct loopback *loop)
return delay; return delay;
} }
static snd_pcm_sframes_t get_queued_capture_samples(struct loopback *loop)
{
snd_pcm_sframes_t delay;
int err;
if ((err = snd_pcm_delay(loop->capt->handle, &delay)) < 0)
return 0;
loop->capt->last_delay = delay;
delay += loop->capt->buf_count;
return delay;
}
static int ctl_event_check(snd_ctl_elem_value_t *val, snd_ctl_event_t *ev) static int ctl_event_check(snd_ctl_elem_value_t *val, snd_ctl_event_t *ev)
{ {
snd_ctl_elem_id_t *id1, *id2; snd_ctl_elem_id_t *id1, *id2;
@ -1363,7 +1499,6 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
struct loopback_handle *capt = loop->capt; struct loopback_handle *capt = loop->capt;
unsigned short prevents, crevents, events; unsigned short prevents, crevents, events;
snd_pcm_uframes_t ccount, pcount; snd_pcm_uframes_t ccount, pcount;
snd_pcm_sframes_t queued;
int err, loopcount = 10, idx; int err, loopcount = 10, idx;
if (verbose > 11) if (verbose > 11)
@ -1463,8 +1598,8 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
play->counter >= play->sync_point && play->counter >= play->sync_point &&
capt->counter >= play->sync_point) { capt->counter >= play->sync_point) {
snd_pcm_sframes_t diff, lat = get_whole_latency(loop); snd_pcm_sframes_t diff, lat = get_whole_latency(loop);
double pitch; diff = ((double)(((double)play->total_queued * play->pitch) +
diff = ((double)loop->total_queued / ((double)capt->total_queued * capt->pitch)) /
(double)loop->total_queued_count) - lat; (double)loop->total_queued_count) - lat;
/* FIXME: this algorithm may be slightly better */ /* FIXME: this algorithm may be slightly better */
if (verbose > 3) if (verbose > 3)
@ -1472,12 +1607,12 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
if (diff > 0) { if (diff > 0) {
if (diff == loop->pitch_diff) if (diff == loop->pitch_diff)
loop->pitch += loop->pitch_delta; loop->pitch += loop->pitch_delta;
if (diff > loop->pitch_diff) else if (diff > loop->pitch_diff)
loop->pitch += loop->pitch_delta*2; loop->pitch += loop->pitch_delta*2;
} else if (diff < 0) { } else if (diff < 0) {
if (diff == loop->pitch_diff) if (diff == loop->pitch_diff)
loop->pitch -= loop->pitch_delta; loop->pitch -= loop->pitch_delta;
if (diff < loop->pitch_diff) else if (diff < loop->pitch_diff)
loop->pitch -= loop->pitch_delta*2; loop->pitch -= loop->pitch_delta*2;
} }
loop->pitch_diff = diff; loop->pitch_diff = diff;
@ -1485,31 +1620,25 @@ int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
loop->pitch_diff_min = diff; loop->pitch_diff_min = diff;
if (loop->pitch_diff_max < diff) if (loop->pitch_diff_max < diff)
loop->pitch_diff_max = diff; loop->pitch_diff_max = diff;
pitch = loop->pitch; update_pitch(loop);
#ifdef USE_SAMPLERATE
if (loop->sync == SYNC_TYPE_SAMPLERATE)
loop->src_data.src_ratio = (double)1.0 / pitch;
else
#endif
if (loop->sync == SYNC_TYPE_CAPTRATESHIFT)
set_rate_shift(capt, pitch);
if (loop->sync == SYNC_TYPE_PLAYRATESHIFT)
set_rate_shift(play, pitch);
if (verbose)
snd_output_printf(loop->output, "New pitch for %s: %.8f (min/max samples = %li/%li)\n", loop->id, pitch, loop->pitch_diff_min, loop->pitch_diff_max);
play->counter -= play->sync_point; play->counter -= play->sync_point;
capt->counter -= play->sync_point; capt->counter -= play->sync_point;
loop->total_queued = 0; play->total_queued = 0;
capt->total_queued = 0;
loop->total_queued_count = 0; loop->total_queued_count = 0;
} }
if (loop->sync != SYNC_TYPE_NONE) { if (loop->sync != SYNC_TYPE_NONE) {
queued = get_queued_samples(loop); snd_pcm_sframes_t pqueued, cqueued;
pqueued = get_queued_playback_samples(loop);
cqueued = get_queued_capture_samples(loop);
if (verbose > 4) if (verbose > 4)
snd_output_printf(loop->output, "%s: queued %li samples\n", loop->id, queued); snd_output_printf(loop->output, "%s: queued %li/%li samples\n", loop->id, pqueued, cqueued);
if (queued > 0) { if (pqueued > 0)
loop->total_queued += queued; loop->play->total_queued += pqueued;
if (cqueued > 0)
loop->capt->total_queued += cqueued;
if (pqueued > 0 || cqueued > 0)
loop->total_queued_count += 1; loop->total_queued_count += 1;
}
} }
if (verbose > 12) { if (verbose > 12) {
snd_pcm_sframes_t pdelay, cdelay; snd_pcm_sframes_t pdelay, cdelay;

View file

@ -6,7 +6,7 @@ CFGFILE="/tmp/alsaloop.test.cfg"
test1() { test1() {
echo "TEST1" echo "TEST1"
$DBG ./alsaloop -C hw:1,0 -P hw:0,0 \ $DBG ./alsaloop -C hw:1,0 -P plughw:0,0 \
--tlatency 50000 \ --tlatency 50000 \
--mixer "name='Master Playback Volume'@name='Master Playback Volume'" \ --mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
--mixer "name='Master Playback Switch'@name='Master Playback Switch'" \ --mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
@ -45,9 +45,19 @@ EOF
$DBG ./alsaloop --config $CFGFILE $DBG ./alsaloop --config $CFGFILE
} }
test4() {
echo "TEST4"
$DBG ./alsaloop -C hw:1,0 -P plughw:0,0 -a off -r 11025 \
--tlatency 50000 \
--mixer "name='Master Playback Volume'@name='Master Playback Volume'" \
--mixer "name='Master Playback Switch'@name='Master Playback Switch'" \
--mixer "name='PCM Playback Volume'"
}
case "$1" in case "$1" in
test1) test1 ;; test1) test1 ;;
test2) test2 ;; test2) test2 ;;
test3) test3 ;; test3) test3 ;;
test4) test4 ;;
*) test1 ;; *) test1 ;;
esac esac