|
Packit |
229ac0 |
/*
|
|
Packit |
229ac0 |
* A simple PCM loopback utility
|
|
Packit |
229ac0 |
* Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
|
|
Packit |
229ac0 |
*
|
|
Packit |
229ac0 |
* Author: Jaroslav Kysela <perex@perex.cz>
|
|
Packit |
229ac0 |
*
|
|
Packit |
229ac0 |
*
|
|
Packit |
229ac0 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
229ac0 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
229ac0 |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
229ac0 |
* (at your option) any later version.
|
|
Packit |
229ac0 |
*
|
|
Packit |
229ac0 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
229ac0 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
229ac0 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
229ac0 |
* GNU General Public License for more details.
|
|
Packit |
229ac0 |
*
|
|
Packit |
229ac0 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
229ac0 |
* along with this program; if not, write to the Free Software
|
|
Packit |
229ac0 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
229ac0 |
*
|
|
Packit |
229ac0 |
*/
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#include <stdio.h>
|
|
Packit |
229ac0 |
#include <stdlib.h>
|
|
Packit |
229ac0 |
#include <string.h>
|
|
Packit |
229ac0 |
#include <sched.h>
|
|
Packit |
229ac0 |
#include <errno.h>
|
|
Packit |
229ac0 |
#include <getopt.h>
|
|
Packit |
229ac0 |
#include <alsa/asoundlib.h>
|
|
Packit |
229ac0 |
#include <sys/time.h>
|
|
Packit |
229ac0 |
#include <math.h>
|
|
Packit |
229ac0 |
#include <syslog.h>
|
|
Packit |
229ac0 |
#include <pthread.h>
|
|
Packit |
229ac0 |
#include "alsaloop.h"
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#define XRUN_PROFILE_UNKNOWN (-10000000)
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int set_rate_shift(struct loopback_handle *lhandle, double pitch);
|
|
Packit |
229ac0 |
static int get_rate(struct loopback_handle *lhandle);
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#define SYNCTYPE(v) [SYNC_TYPE_##v] = #v
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static const char *sync_types[] = {
|
|
Packit |
229ac0 |
SYNCTYPE(NONE),
|
|
Packit |
229ac0 |
SYNCTYPE(SIMPLE),
|
|
Packit |
229ac0 |
SYNCTYPE(CAPTRATESHIFT),
|
|
Packit |
229ac0 |
SYNCTYPE(PLAYRATESHIFT),
|
|
Packit |
229ac0 |
SYNCTYPE(SAMPLERATE),
|
|
Packit |
229ac0 |
SYNCTYPE(AUTO)
|
|
Packit |
229ac0 |
};
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#define SRCTYPE(v) [SRC_##v] = "SRC_" #v
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
static const char *src_types[] = {
|
|
Packit |
229ac0 |
SRCTYPE(SINC_BEST_QUALITY),
|
|
Packit |
229ac0 |
SRCTYPE(SINC_MEDIUM_QUALITY),
|
|
Packit |
229ac0 |
SRCTYPE(SINC_FASTEST),
|
|
Packit |
229ac0 |
SRCTYPE(ZERO_ORDER_HOLD),
|
|
Packit |
229ac0 |
SRCTYPE(LINEAR)
|
|
Packit |
229ac0 |
};
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static pthread_once_t pcm_open_mutex_once = PTHREAD_ONCE_INIT;
|
|
Packit |
229ac0 |
static pthread_mutex_t pcm_open_mutex;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void pcm_open_init_mutex(void)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
pthread_mutexattr_t attr;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
pthread_mutexattr_init(&attr);
|
|
Packit |
229ac0 |
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
|
Packit |
229ac0 |
pthread_mutex_init(&pcm_open_mutex, &attr);
|
|
Packit |
229ac0 |
pthread_mutexattr_destroy(&attr);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline void pcm_open_lock(void)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
pthread_once(&pcm_open_mutex_once, pcm_open_init_mutex);
|
|
Packit |
229ac0 |
if (workarounds & WORKAROUND_SERIALOPEN)
|
|
Packit |
229ac0 |
pthread_mutex_lock(&pcm_open_mutex);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline void pcm_open_unlock(void)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
if (workarounds & WORKAROUND_SERIALOPEN)
|
|
Packit |
229ac0 |
pthread_mutex_unlock(&pcm_open_mutex);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline snd_pcm_uframes_t get_whole_latency(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
return loop->latency;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline unsigned long long
|
|
Packit |
229ac0 |
frames_to_time(unsigned int rate,
|
|
Packit |
229ac0 |
snd_pcm_uframes_t frames)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
return (frames * 1000000ULL) / rate;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline snd_pcm_uframes_t time_to_frames(unsigned int rate,
|
|
Packit |
229ac0 |
unsigned long long time)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
return (time * rate) / 1000000ULL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int setparams_stream(struct loopback_handle *lhandle,
|
|
Packit |
229ac0 |
snd_pcm_hw_params_t *params)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_t *handle = lhandle->handle;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
unsigned int rrate;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_any(handle, params);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Broken configuration for %s PCM: no configurations available: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_set_rate_resample(handle, params, lhandle->resample);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Resample setup failed for %s (val %i): %s\n", lhandle->id, lhandle->resample, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_set_access(handle, params, lhandle->access);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Access type not available for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_set_format(handle, params, lhandle->format);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Sample format not available for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_set_channels(handle, params, lhandle->channels);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Channels count (%i) not available for %s: %s\n", lhandle->channels, lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
rrate = lhandle->rate_req;
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Rate %iHz not available for %s: %s\n", lhandle->rate_req, lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
rrate = 0;
|
|
Packit |
229ac0 |
snd_pcm_hw_params_get_rate(params, &rrate, 0);
|
|
Packit |
229ac0 |
lhandle->rate = rrate;
|
|
Packit |
229ac0 |
if (
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
!lhandle->loopback->src_enable &&
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
(int)rrate != lhandle->rate) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Rate does not match (requested %iHz, got %iHz, resample %i)\n", lhandle->rate, rrate, lhandle->resample);
|
|
Packit |
229ac0 |
return -EINVAL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
lhandle->pitch = (double)lhandle->rate_req / (double)lhandle->rate;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int setparams_bufsize(struct loopback_handle *lhandle,
|
|
Packit |
229ac0 |
snd_pcm_hw_params_t *params,
|
|
Packit |
229ac0 |
snd_pcm_hw_params_t *tparams,
|
|
Packit |
229ac0 |
snd_pcm_uframes_t bufsize)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_t *handle = lhandle->handle;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t periodsize;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t buffersize;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t last_bufsize = 0;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->buffer_size_req > 0) {
|
|
Packit |
229ac0 |
bufsize = lhandle->buffer_size_req;
|
|
Packit |
229ac0 |
last_bufsize = bufsize;
|
|
Packit |
229ac0 |
goto __set_it;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
__again:
|
|
Packit |
229ac0 |
if (lhandle->buffer_size_req > 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set buffer size %li for %s\n", (long)lhandle->buffer_size, lhandle->id);
|
|
Packit |
229ac0 |
return -EIO;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (last_bufsize == bufsize)
|
|
Packit |
229ac0 |
bufsize += 4;
|
|
Packit |
229ac0 |
last_bufsize = bufsize;
|
|
Packit |
229ac0 |
if (bufsize > 10*1024*1024) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Buffer size too big\n");
|
|
Packit |
229ac0 |
return -EIO;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
__set_it:
|
|
Packit |
229ac0 |
snd_pcm_hw_params_copy(params, tparams);
|
|
Packit |
229ac0 |
periodsize = bufsize * 8;
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_set_buffer_size_near(handle, params, &periodsize);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set buffer size %li for %s: %s\n", periodsize, lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
snd_pcm_hw_params_get_buffer_size(params, &periodsize);
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(lhandle->loopback->output, "%s: buffer_size=%li\n", lhandle->id, periodsize);
|
|
Packit |
229ac0 |
if (lhandle->period_size_req > 0)
|
|
Packit |
229ac0 |
periodsize = lhandle->period_size_req;
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
periodsize /= 8;
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params_set_period_size_near(handle, params, &periodsize, 0);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set period size %li for %s: %s\n", periodsize, lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
snd_pcm_hw_params_get_period_size(params, &periodsize, NULL);
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(lhandle->loopback->output, "%s: period_size=%li\n", lhandle->id, periodsize);
|
|
Packit |
229ac0 |
if (periodsize != bufsize)
|
|
Packit |
229ac0 |
bufsize = periodsize;
|
|
Packit |
229ac0 |
snd_pcm_hw_params_get_buffer_size(params, &buffersize);
|
|
Packit |
229ac0 |
if (periodsize * 2 > buffersize)
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
lhandle->period_size = periodsize;
|
|
Packit |
229ac0 |
lhandle->buffer_size = buffersize;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int setparams_set(struct loopback_handle *lhandle,
|
|
Packit |
229ac0 |
snd_pcm_hw_params_t *params,
|
|
Packit |
229ac0 |
snd_pcm_sw_params_t *swparams,
|
|
Packit |
229ac0 |
snd_pcm_uframes_t bufsize)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_t *handle = lhandle->handle;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t val, period_size, buffer_size;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
err = snd_pcm_hw_params(handle, params);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set hw params for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_sw_params_current(handle, swparams);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to determine current swparams for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0x7fffffff);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set start threshold mode for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
snd_pcm_hw_params_get_period_size(params, &period_size, NULL);
|
|
Packit |
229ac0 |
snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
|
|
Packit |
229ac0 |
if (lhandle->nblock) {
|
|
Packit |
229ac0 |
if (lhandle == lhandle->loopback->play) {
|
|
Packit |
229ac0 |
val = buffer_size - (2 * period_size - 4);
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
val = 4;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(lhandle->loopback->output, "%s: avail_min1=%li\n", lhandle->id, val);
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
if (lhandle == lhandle->loopback->play) {
|
|
Packit |
229ac0 |
val = bufsize + bufsize / 2;
|
|
Packit |
229ac0 |
if (val > (buffer_size * 3) / 4)
|
|
Packit |
229ac0 |
val = (buffer_size * 3) / 4;
|
|
Packit |
229ac0 |
val = buffer_size - val;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
val = bufsize / 2;
|
|
Packit |
229ac0 |
if (val > buffer_size / 4)
|
|
Packit |
229ac0 |
val = buffer_size / 4;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(lhandle->loopback->output, "%s: avail_min2=%li\n", lhandle->id, val);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_sw_params_set_avail_min(handle, swparams, val);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set avail min for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
snd_pcm_sw_params_get_avail_min(swparams, &lhandle->avail_min);
|
|
Packit |
229ac0 |
err = snd_pcm_sw_params(handle, swparams);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set sw params for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int increase_playback_avail_min(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_t *handle = lhandle->handle;
|
|
Packit |
229ac0 |
snd_pcm_sw_params_t *swparams;
|
|
Packit |
229ac0 |
struct timespec ts;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->avail_min + (lhandle->period_size / 2) > lhandle->buffer_size) {
|
|
Packit |
229ac0 |
/* avoid 100% CPU usage for broken plugins */
|
|
Packit |
229ac0 |
ts.tv_sec = 0;
|
|
Packit |
229ac0 |
ts.tv_nsec = 10000;
|
|
Packit |
229ac0 |
nanosleep(&ts, NULL);
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
snd_pcm_sw_params_alloca(&swparams);
|
|
Packit |
229ac0 |
err = snd_pcm_sw_params_current(handle, swparams);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to determine current swparams for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_pcm_sw_params_set_avail_min(handle, swparams, lhandle->avail_min + 4);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set avail min for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
snd_pcm_sw_params_get_avail_min(swparams, &lhandle->avail_min);
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(lhandle->loopback->output, "%s: change avail_min=%li\n", lhandle->id, lhandle->avail_min);
|
|
Packit |
229ac0 |
err = snd_pcm_sw_params(handle, swparams);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set sw params for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int setparams(struct loopback *loop, snd_pcm_uframes_t bufsize)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
snd_pcm_hw_params_t *pt_params, *ct_params; /* templates with rate, format and channels */
|
|
Packit |
229ac0 |
snd_pcm_hw_params_t *p_params, *c_params;
|
|
Packit |
229ac0 |
snd_pcm_sw_params_t *p_swparams, *c_swparams;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
snd_pcm_hw_params_alloca(&p_params);
|
|
Packit |
229ac0 |
snd_pcm_hw_params_alloca(&c_params);
|
|
Packit |
229ac0 |
snd_pcm_hw_params_alloca(&pt_params);
|
|
Packit |
229ac0 |
snd_pcm_hw_params_alloca(&ct_params);
|
|
Packit |
229ac0 |
snd_pcm_sw_params_alloca(&p_swparams);
|
|
Packit |
229ac0 |
snd_pcm_sw_params_alloca(&c_swparams);
|
|
Packit |
229ac0 |
if ((err = setparams_stream(loop->play, pt_params)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set parameters for %s stream: %s\n", loop->play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = setparams_stream(loop->capt, ct_params)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if ((err = setparams_bufsize(loop->play, p_params, pt_params, bufsize / loop->play->pitch)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = setparams_bufsize(loop->capt, c_params, ct_params, bufsize / loop->capt->pitch)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set buffer parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if ((err = setparams_set(loop->play, p_params, p_swparams, bufsize / loop->play->pitch)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set sw parameters for %s stream: %s\n", loop->play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = setparams_set(loop->capt, c_params, c_swparams, bufsize / loop->capt->pitch)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Unable to set sw parameters for %s stream: %s\n", loop->capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#if 0
|
|
Packit |
229ac0 |
if (!loop->linked)
|
|
Packit |
229ac0 |
if (snd_pcm_link(loop->capt->handle, loop->play->handle) >= 0)
|
|
Packit |
229ac0 |
loop->linked = 1;
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
if ((err = snd_pcm_prepare(loop->play->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Prepare %s error: %s\n", loop->play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (!loop->linked && (err = snd_pcm_prepare(loop->capt->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Prepare %s error: %s\n", loop->capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (verbose) {
|
|
Packit |
229ac0 |
snd_pcm_dump(loop->play->handle, loop->output);
|
|
Packit |
229ac0 |
snd_pcm_dump(loop->capt->handle, loop->output);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void showlatency(snd_output_t *out, size_t latency, unsigned int rate,
|
|
Packit |
229ac0 |
char *prefix)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
double d;
|
|
Packit |
229ac0 |
d = (double)latency / (double)rate;
|
|
Packit |
229ac0 |
snd_output_printf(out, "%s %li frames, %.3fus, %.6fms (%.4fHz)\n", prefix, (long)latency, d * 1000000, d * 1000, (double)1 / d);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static long timediff(snd_timestamp_t t1, snd_timestamp_t t2)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
signed long l;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
t1.tv_sec -= t2.tv_sec;
|
|
Packit |
229ac0 |
if (t1.tv_usec < t2.tv_usec) {
|
|
Packit |
229ac0 |
l = ((t1.tv_usec + 1000000) - t2.tv_usec) % 1000000;
|
|
Packit |
229ac0 |
t1.tv_sec--;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
l = t1.tv_usec - t2.tv_usec;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return (t1.tv_sec * 1000000) + l;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int getcurtimestamp(snd_timestamp_t *ts)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct timeval tv;
|
|
Packit |
229ac0 |
gettimeofday(&tv, NULL);
|
|
Packit |
229ac0 |
ts->tv_sec = tv.tv_sec;
|
|
Packit |
229ac0 |
ts->tv_usec = tv.tv_usec;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void xrun_profile0(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_sframes_t pdelay, cdelay;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (snd_pcm_delay(loop->play->handle, &pdelay) >= 0 &&
|
|
Packit |
229ac0 |
snd_pcm_delay(loop->capt->handle, &cdelay) >= 0) {
|
|
Packit |
229ac0 |
getcurtimestamp(&loop->xrun_last_update);
|
|
Packit |
229ac0 |
loop->xrun_last_pdelay = pdelay;
|
|
Packit |
229ac0 |
loop->xrun_last_cdelay = cdelay;
|
|
Packit |
229ac0 |
loop->xrun_buf_pcount = loop->play->buf_count;
|
|
Packit |
229ac0 |
loop->xrun_buf_ccount = loop->capt->buf_count;
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
loop->xrun_out_frames = loop->src_out_frames;
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline void xrun_profile(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
if (loop->xrun)
|
|
Packit |
229ac0 |
xrun_profile0(loop);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void xrun_stats0(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_timestamp_t t;
|
|
Packit |
229ac0 |
double expected, last, wake, check, queued = -1, proc, missing = -1;
|
|
Packit |
229ac0 |
double maxbuf, pfilled, cfilled, cqueued = -1, avail_min;
|
|
Packit |
229ac0 |
double sincejob;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
expected = ((double)loop->latency /
|
|
Packit |
229ac0 |
(double)loop->play->rate_req) * 1000;
|
|
Packit |
229ac0 |
getcurtimestamp(&t);
|
|
Packit |
229ac0 |
last = (double)timediff(t, loop->xrun_last_update) / 1000;
|
|
Packit |
229ac0 |
wake = (double)timediff(t, loop->xrun_last_wake) / 1000;
|
|
Packit |
229ac0 |
check = (double)timediff(t, loop->xrun_last_check) / 1000;
|
|
Packit |
229ac0 |
sincejob = (double)timediff(t, loop->tstamp_start) / 1000;
|
|
Packit |
229ac0 |
if (loop->xrun_last_pdelay != XRUN_PROFILE_UNKNOWN)
|
|
Packit |
229ac0 |
queued = ((double)loop->xrun_last_pdelay /
|
|
Packit |
229ac0 |
(double)loop->play->rate) * 1000;
|
|
Packit |
229ac0 |
if (loop->xrun_last_cdelay != XRUN_PROFILE_UNKNOWN)
|
|
Packit |
229ac0 |
cqueued = ((double)loop->xrun_last_cdelay /
|
|
Packit |
229ac0 |
(double)loop->capt->rate) * 1000;
|
|
Packit |
229ac0 |
maxbuf = ((double)loop->play->buffer_size /
|
|
Packit |
229ac0 |
(double)loop->play->rate) * 1000;
|
|
Packit |
229ac0 |
proc = (double)loop->xrun_max_proctime / 1000;
|
|
Packit |
229ac0 |
pfilled = ((double)(loop->xrun_buf_pcount + loop->xrun_out_frames) /
|
|
Packit |
229ac0 |
(double)loop->play->rate) * 1000;
|
|
Packit |
229ac0 |
cfilled = ((double)loop->xrun_buf_ccount /
|
|
Packit |
229ac0 |
(double)loop->capt->rate) * 1000;
|
|
Packit |
229ac0 |
avail_min = (((double)loop->play->buffer_size -
|
|
Packit |
229ac0 |
(double)loop->play->avail_min ) /
|
|
Packit |
229ac0 |
(double)loop->play->rate) * 1000;
|
|
Packit |
229ac0 |
avail_min = expected - avail_min;
|
|
Packit |
229ac0 |
if (queued >= 0)
|
|
Packit |
229ac0 |
missing = last - queued;
|
|
Packit |
229ac0 |
if (missing >= 0 && loop->xrun_max_missing < missing)
|
|
Packit |
229ac0 |
loop->xrun_max_missing = missing;
|
|
Packit |
229ac0 |
loop->xrun_max_proctime = 0;
|
|
Packit |
229ac0 |
getcurtimestamp(&t);
|
|
Packit |
229ac0 |
logit(LOG_INFO, " last write before %.4fms, queued %.4fms/%.4fms -> missing %.4fms\n", last, queued, cqueued, missing);
|
|
Packit |
229ac0 |
logit(LOG_INFO, " expected %.4fms, processing %.4fms, max missing %.4fms\n", expected, proc, loop->xrun_max_missing);
|
|
Packit |
229ac0 |
logit(LOG_INFO, " last wake %.4fms, last check %.4fms, avail_min %.4fms\n", wake, check, avail_min);
|
|
Packit |
229ac0 |
logit(LOG_INFO, " max buf %.4fms, pfilled %.4fms, cfilled %.4fms\n", maxbuf, pfilled, cfilled);
|
|
Packit |
229ac0 |
logit(LOG_INFO, " job started before %.4fms\n", sincejob);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline void xrun_stats(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
if (loop->xrun)
|
|
Packit |
229ac0 |
xrun_stats0(loop);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static inline snd_pcm_uframes_t buf_avail(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
return lhandle->buf_size - lhandle->buf_count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void buf_remove(struct loopback *loop, snd_pcm_uframes_t count)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
/* remove samples from the capture buffer */
|
|
Packit |
229ac0 |
if (count <= 0)
|
|
Packit |
229ac0 |
return;
|
|
Packit |
229ac0 |
if (loop->play->buf == loop->capt->buf) {
|
|
Packit |
229ac0 |
if (count < loop->capt->buf_count)
|
|
Packit |
229ac0 |
loop->capt->buf_count -= count;
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
loop->capt->buf_count = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#if 0
|
|
Packit |
229ac0 |
static void buf_add_copy(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct loopback_handle *capt = loop->capt;
|
|
Packit |
229ac0 |
struct loopback_handle *play = loop->play;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t count, count1, cpos, ppos;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
count = capt->buf_count;
|
|
Packit |
229ac0 |
cpos = capt->buf_pos - count;
|
|
Packit |
229ac0 |
if (cpos > capt->buf_size)
|
|
Packit |
229ac0 |
cpos += capt->buf_size;
|
|
Packit |
229ac0 |
ppos = (play->buf_pos + play->buf_count) % play->buf_size;
|
|
Packit |
229ac0 |
while (count > 0) {
|
|
Packit |
229ac0 |
count1 = count;
|
|
Packit |
229ac0 |
if (count1 + cpos > capt->buf_size)
|
|
Packit |
229ac0 |
count1 = capt->buf_size - cpos;
|
|
Packit |
229ac0 |
if (count1 > buf_avail(play))
|
|
Packit |
229ac0 |
count1 = buf_avail(play);
|
|
Packit |
229ac0 |
if (count1 + ppos > play->buf_size)
|
|
Packit |
229ac0 |
count1 = play->buf_size - ppos;
|
|
Packit |
229ac0 |
if (count1 == 0)
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
memcpy(play->buf + ppos * play->frame_size,
|
|
Packit |
229ac0 |
capt->buf + cpos * capt->frame_size,
|
|
Packit |
229ac0 |
count1 * capt->frame_size);
|
|
Packit |
229ac0 |
play->buf_count += count1;
|
|
Packit |
229ac0 |
capt->buf_count -= count1;
|
|
Packit |
229ac0 |
ppos += count1;
|
|
Packit |
229ac0 |
ppos %= play->buf_size;
|
|
Packit |
229ac0 |
cpos += count1;
|
|
Packit |
229ac0 |
cpos %= capt->buf_size;
|
|
Packit |
229ac0 |
count -= count1;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
static void buf_add_src(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct loopback_handle *capt = loop->capt;
|
|
Packit |
229ac0 |
struct loopback_handle *play = loop->play;
|
|
Packit |
229ac0 |
float *old_data_out;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t count, pos, count1, pos1;
|
|
Packit |
229ac0 |
count = capt->buf_count;
|
|
Packit |
229ac0 |
pos = 0;
|
|
Packit |
229ac0 |
pos1 = capt->buf_pos - count;
|
|
Packit |
229ac0 |
if (pos1 > capt->buf_size)
|
|
Packit |
229ac0 |
pos1 += capt->buf_size;
|
|
Packit |
229ac0 |
while (count > 0) {
|
|
Packit |
229ac0 |
count1 = count;
|
|
Packit |
229ac0 |
if (count1 + pos1 > capt->buf_size)
|
|
Packit |
229ac0 |
count1 = capt->buf_size - pos1;
|
|
Packit |
229ac0 |
if (capt->format == SND_PCM_FORMAT_S32)
|
|
Packit |
229ac0 |
src_int_to_float_array((int *)(capt->buf +
|
|
Packit |
229ac0 |
pos1 * capt->frame_size),
|
|
Packit |
229ac0 |
(void *)loop->src_data.data_in +
|
|
Packit |
229ac0 |
pos * capt->channels,
|
|
Packit |
229ac0 |
count1 * capt->channels);
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
src_short_to_float_array((short *)(capt->buf +
|
|
Packit |
229ac0 |
pos1 * capt->frame_size),
|
|
Packit |
229ac0 |
(void *)loop->src_data.data_in +
|
|
Packit |
229ac0 |
pos * capt->channels,
|
|
Packit |
229ac0 |
count1 * capt->channels);
|
|
Packit |
229ac0 |
count -= count1;
|
|
Packit |
229ac0 |
pos += count1;
|
|
Packit |
229ac0 |
pos1 += count1;
|
|
Packit |
229ac0 |
pos1 %= capt->buf_size;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->src_data.input_frames = pos;
|
|
Packit |
229ac0 |
loop->src_data.output_frames = play->buf_size -
|
|
Packit |
229ac0 |
loop->src_out_frames;
|
|
Packit |
229ac0 |
loop->src_data.end_of_input = 0;
|
|
Packit |
229ac0 |
old_data_out = loop->src_data.data_out;
|
|
Packit |
229ac0 |
loop->src_data.data_out = old_data_out + loop->src_out_frames;
|
|
Packit |
229ac0 |
src_process(loop->src_state, &loop->src_data);
|
|
Packit |
229ac0 |
loop->src_data.data_out = old_data_out;
|
|
Packit |
229ac0 |
capt->buf_count -= loop->src_data.input_frames_used;
|
|
Packit |
229ac0 |
count = loop->src_data.output_frames_gen +
|
|
Packit |
229ac0 |
loop->src_out_frames;
|
|
Packit |
229ac0 |
pos = 0;
|
|
Packit |
229ac0 |
pos1 = (play->buf_pos + play->buf_count) % play->buf_size;
|
|
Packit |
229ac0 |
while (count > 0) {
|
|
Packit |
229ac0 |
count1 = count;
|
|
Packit |
229ac0 |
if (count1 + pos1 > play->buf_size)
|
|
Packit |
229ac0 |
count1 = play->buf_size - pos1;
|
|
Packit |
229ac0 |
if (count1 > buf_avail(play))
|
|
Packit |
229ac0 |
count1 = buf_avail(play);
|
|
Packit |
229ac0 |
if (count1 == 0)
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
if (capt->format == SND_PCM_FORMAT_S32)
|
|
Packit |
229ac0 |
src_float_to_int_array(loop->src_data.data_out +
|
|
Packit |
229ac0 |
pos * play->channels,
|
|
Packit |
229ac0 |
(int *)(play->buf +
|
|
Packit |
229ac0 |
pos1 * play->frame_size),
|
|
Packit |
229ac0 |
count1 * play->channels);
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
src_float_to_short_array(loop->src_data.data_out +
|
|
Packit |
229ac0 |
pos * play->channels,
|
|
Packit |
229ac0 |
(short *)(play->buf +
|
|
Packit |
229ac0 |
pos1 * play->frame_size),
|
|
Packit |
229ac0 |
count1 * play->channels);
|
|
Packit |
229ac0 |
play->buf_count += count1;
|
|
Packit |
229ac0 |
count -= count1;
|
|
Packit |
229ac0 |
pos += count1;
|
|
Packit |
229ac0 |
pos1 += count1;
|
|
Packit |
229ac0 |
pos1 %= play->buf_size;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#if 0
|
|
Packit |
229ac0 |
printf("src: pos = %li, gen = %li, out = %li, count = %li\n",
|
|
Packit |
229ac0 |
(long)pos, (long)loop->src_data.output_frames_gen,
|
|
Packit |
229ac0 |
(long)loop->src_out_frames, play->buf_count);
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
loop->src_out_frames = (loop->src_data.output_frames_gen +
|
|
Packit |
229ac0 |
loop->src_out_frames) - pos;
|
|
Packit |
229ac0 |
if (loop->src_out_frames > 0) {
|
|
Packit |
229ac0 |
memmove(loop->src_data.data_out,
|
|
Packit |
229ac0 |
loop->src_data.data_out + pos * play->channels,
|
|
Packit |
229ac0 |
loop->src_out_frames * play->channels * sizeof(float));
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#else
|
|
Packit |
229ac0 |
static void buf_add_src(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void buf_add(struct loopback *loop, snd_pcm_uframes_t count)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
/* copy samples from capture to playback buffer */
|
|
Packit |
229ac0 |
if (count <= 0)
|
|
Packit |
229ac0 |
return;
|
|
Packit |
229ac0 |
if (loop->play->buf == loop->capt->buf) {
|
|
Packit |
229ac0 |
loop->play->buf_count += count;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
buf_add_src(loop);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int xrun(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle == lhandle->loopback->play) {
|
|
Packit |
229ac0 |
logit(LOG_DEBUG, "underrun for %s\n", lhandle->id);
|
|
Packit |
229ac0 |
xrun_stats(lhandle->loopback);
|
|
Packit |
229ac0 |
if ((err = snd_pcm_prepare(lhandle->handle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
lhandle->xrun_pending = 1;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
logit(LOG_DEBUG, "overrun for %s\n", lhandle->id);
|
|
Packit |
229ac0 |
xrun_stats(lhandle->loopback);
|
|
Packit |
229ac0 |
if ((err = snd_pcm_prepare(lhandle->handle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
lhandle->xrun_pending = 1;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int suspend(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
while ((err = snd_pcm_resume(lhandle->handle)) == -EAGAIN)
|
|
Packit |
229ac0 |
usleep(1);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return xrun(lhandle);
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int readit(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_sframes_t r, res = 0;
|
|
Packit |
229ac0 |
snd_pcm_sframes_t avail;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
avail = snd_pcm_avail_update(lhandle->handle);
|
|
Packit |
229ac0 |
if (avail == -EPIPE) {
|
|
Packit |
229ac0 |
return xrun(lhandle);
|
|
Packit |
229ac0 |
} else if (avail == -ESTRPIPE) {
|
|
Packit |
229ac0 |
if ((err = suspend(lhandle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (avail > buf_avail(lhandle)) {
|
|
Packit |
229ac0 |
lhandle->buf_over += avail - buf_avail(lhandle);
|
|
Packit |
229ac0 |
avail = buf_avail(lhandle);
|
|
Packit |
229ac0 |
} else if (avail == 0) {
|
|
Packit |
229ac0 |
if (snd_pcm_state(lhandle->handle) == SND_PCM_STATE_DRAINING) {
|
|
Packit |
229ac0 |
lhandle->loopback->reinit = 1;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
while (avail > 0) {
|
|
Packit |
229ac0 |
r = buf_avail(lhandle);
|
|
Packit |
229ac0 |
if (r + lhandle->buf_pos > lhandle->buf_size)
|
|
Packit |
229ac0 |
r = lhandle->buf_size - lhandle->buf_pos;
|
|
Packit |
229ac0 |
if (r > avail)
|
|
Packit |
229ac0 |
r = avail;
|
|
Packit |
229ac0 |
r = snd_pcm_readi(lhandle->handle,
|
|
Packit |
229ac0 |
lhandle->buf +
|
|
Packit |
229ac0 |
lhandle->buf_pos *
|
|
Packit |
229ac0 |
lhandle->frame_size, r);
|
|
Packit |
229ac0 |
if (r == 0)
|
|
Packit |
229ac0 |
return res;
|
|
Packit |
229ac0 |
if (r < 0) {
|
|
Packit |
229ac0 |
if (r == -EPIPE) {
|
|
Packit |
229ac0 |
err = xrun(lhandle);
|
|
Packit |
229ac0 |
return res > 0 ? res : err;
|
|
Packit |
229ac0 |
} else if (r == -ESTRPIPE) {
|
|
Packit |
229ac0 |
if ((err = suspend(lhandle)) < 0)
|
|
Packit |
229ac0 |
return res > 0 ? res : err;
|
|
Packit |
229ac0 |
r = 0;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
return res > 0 ? res : r;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#ifdef FILE_CWRITE
|
|
Packit |
229ac0 |
if (lhandle->loopback->cfile)
|
|
Packit |
229ac0 |
fwrite(lhandle->buf + lhandle->buf_pos * lhandle->frame_size,
|
|
Packit |
229ac0 |
r, lhandle->frame_size, lhandle->loopback->cfile);
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
res += r;
|
|
Packit |
229ac0 |
if (lhandle->max < res)
|
|
Packit |
229ac0 |
lhandle->max = res;
|
|
Packit |
229ac0 |
lhandle->counter += r;
|
|
Packit |
229ac0 |
lhandle->buf_count += r;
|
|
Packit |
229ac0 |
lhandle->buf_pos += r;
|
|
Packit |
229ac0 |
lhandle->buf_pos %= lhandle->buf_size;
|
|
Packit |
229ac0 |
avail -= r;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return res;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int writeit(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_sframes_t avail;
|
|
Packit |
229ac0 |
snd_pcm_sframes_t r, res = 0;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
__again:
|
|
Packit |
229ac0 |
avail = snd_pcm_avail_update(lhandle->handle);
|
|
Packit |
229ac0 |
if (avail == -EPIPE) {
|
|
Packit |
229ac0 |
if ((err = xrun(lhandle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
return res;
|
|
Packit |
229ac0 |
} else if (avail == -ESTRPIPE) {
|
|
Packit |
229ac0 |
if ((err = suspend(lhandle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
while (avail > 0 && lhandle->buf_count > 0) {
|
|
Packit |
229ac0 |
r = lhandle->buf_count;
|
|
Packit |
229ac0 |
if (r + lhandle->buf_pos > lhandle->buf_size)
|
|
Packit |
229ac0 |
r = lhandle->buf_size - lhandle->buf_pos;
|
|
Packit |
229ac0 |
if (r > avail)
|
|
Packit |
229ac0 |
r = avail;
|
|
Packit |
229ac0 |
r = snd_pcm_writei(lhandle->handle,
|
|
Packit |
229ac0 |
lhandle->buf +
|
|
Packit |
229ac0 |
lhandle->buf_pos *
|
|
Packit |
229ac0 |
lhandle->frame_size, r);
|
|
Packit |
229ac0 |
if (r <= 0) {
|
|
Packit |
229ac0 |
if (r == -EPIPE) {
|
|
Packit |
229ac0 |
if ((err = xrun(lhandle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
return res;
|
|
Packit |
229ac0 |
} else if (r == -ESTRPIPE) {
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return res > 0 ? res : r;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#ifdef FILE_PWRITE
|
|
Packit |
229ac0 |
if (lhandle->loopback->pfile)
|
|
Packit |
229ac0 |
fwrite(lhandle->buf + lhandle->buf_pos * lhandle->frame_size,
|
|
Packit |
229ac0 |
r, lhandle->frame_size, lhandle->loopback->pfile);
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
res += r;
|
|
Packit |
229ac0 |
lhandle->counter += r;
|
|
Packit |
229ac0 |
lhandle->buf_count -= r;
|
|
Packit |
229ac0 |
lhandle->buf_pos += r;
|
|
Packit |
229ac0 |
lhandle->buf_pos %= lhandle->buf_size;
|
|
Packit |
229ac0 |
xrun_profile(lhandle->loopback);
|
|
Packit |
229ac0 |
if (lhandle->loopback->stop_pending) {
|
|
Packit |
229ac0 |
lhandle->loopback->stop_count += r;
|
|
Packit |
229ac0 |
if (lhandle->loopback->stop_count * lhandle->pitch >
|
|
Packit |
229ac0 |
lhandle->loopback->latency * 3) {
|
|
Packit |
229ac0 |
lhandle->loopback->stop_pending = 0;
|
|
Packit |
229ac0 |
lhandle->loopback->reinit = 1;
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return res;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static snd_pcm_sframes_t remove_samples(struct loopback *loop,
|
|
Packit |
229ac0 |
int capture_preferred,
|
|
Packit |
229ac0 |
snd_pcm_sframes_t count)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct loopback_handle *play = loop->play;
|
|
Packit |
229ac0 |
struct loopback_handle *capt = loop->capt;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (loop->play->buf == loop->capt->buf) {
|
|
Packit |
229ac0 |
if (count > loop->play->buf_count)
|
|
Packit |
229ac0 |
count = loop->play->buf_count;
|
|
Packit |
229ac0 |
if (count > loop->capt->buf_count)
|
|
Packit |
229ac0 |
count = loop->capt->buf_count;
|
|
Packit |
229ac0 |
capt->buf_count -= count;
|
|
Packit |
229ac0 |
play->buf_pos += count;
|
|
Packit |
229ac0 |
play->buf_pos %= play->buf_size;
|
|
Packit |
229ac0 |
play->buf_count -= count;
|
|
Packit |
229ac0 |
return count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (capture_preferred) {
|
|
Packit |
229ac0 |
if (count > capt->buf_count)
|
|
Packit |
229ac0 |
count = capt->buf_count;
|
|
Packit |
229ac0 |
capt->buf_count -= count;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
if (count > play->buf_count)
|
|
Packit |
229ac0 |
count = play->buf_count;
|
|
Packit |
229ac0 |
play->buf_count -= count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int xrun_sync(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct loopback_handle *play = loop->play;
|
|
Packit |
229ac0 |
struct loopback_handle *capt = loop->capt;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t fill = get_whole_latency(loop);
|
|
Packit |
229ac0 |
snd_pcm_sframes_t pdelay, cdelay, delay1, pdelay1, cdelay1, diff;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
__again:
|
|
Packit |
229ac0 |
if (verbose > 5)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: xrun sync %i %i\n", loop->id, capt->xrun_pending, play->xrun_pending);
|
|
Packit |
229ac0 |
if (capt->xrun_pending) {
|
|
Packit |
229ac0 |
__pagain:
|
|
Packit |
229ac0 |
capt->xrun_pending = 0;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_prepare(capt->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s prepare failed: %s\n", capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = snd_pcm_start(capt->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s start failed: %s\n", capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
diff = readit(capt);
|
|
Packit |
229ac0 |
buf_add(loop, diff);
|
|
Packit |
229ac0 |
if (capt->xrun_pending)
|
|
Packit |
229ac0 |
goto __pagain;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
/* skip additional playback samples */
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(capt->handle, &cdelay)) < 0) {
|
|
Packit |
229ac0 |
if (err == -EPIPE) {
|
|
Packit |
229ac0 |
capt->xrun_pending = 1;
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (err == -ESTRPIPE) {
|
|
Packit |
229ac0 |
err = suspend(capt);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s capture delay failed: %s\n", capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(play->handle, &pdelay)) < 0) {
|
|
Packit |
229ac0 |
if (err == -EPIPE) {
|
|
Packit |
229ac0 |
pdelay = 0;
|
|
Packit |
229ac0 |
play->xrun_pending = 1;
|
|
Packit |
229ac0 |
} else if (err == -ESTRPIPE) {
|
|
Packit |
229ac0 |
err = suspend(play);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s playback delay failed: %s\n", play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
capt->counter = cdelay;
|
|
Packit |
229ac0 |
play->counter = pdelay;
|
|
Packit |
229ac0 |
if (play->buf != capt->buf)
|
|
Packit |
229ac0 |
cdelay += capt->buf_count;
|
|
Packit |
229ac0 |
pdelay += play->buf_count;
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
pdelay += loop->src_out_frames;
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
cdelay1 = cdelay * capt->pitch;
|
|
Packit |
229ac0 |
pdelay1 = pdelay * play->pitch;
|
|
Packit |
229ac0 |
delay1 = cdelay1 + pdelay1;
|
|
Packit |
229ac0 |
capt->total_queued = 0;
|
|
Packit |
229ac0 |
play->total_queued = 0;
|
|
Packit |
229ac0 |
loop->total_queued_count = 0;
|
|
Packit |
229ac0 |
loop->pitch_diff = loop->pitch_diff_min = loop->pitch_diff_max = 0;
|
|
Packit |
229ac0 |
if (verbose > 6) {
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: cdelay=%li(%li), pdelay=%li(%li), fill=%li (delay=%li)"
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
", src_out=%li"
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
"\n",
|
|
Packit |
229ac0 |
(long)cdelay, (long)cdelay1, (long)pdelay, (long)pdelay1,
|
|
Packit |
229ac0 |
(long)fill, (long)delay1
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
, (long)loop->src_out_frames
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
);
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: cbufcount=%li, pbufcount=%li\n",
|
|
Packit |
229ac0 |
(long)capt->buf_count, (long)play->buf_count);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (delay1 > fill && capt->counter > 0) {
|
|
Packit |
229ac0 |
if ((err = snd_pcm_drop(capt->handle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_prepare(capt->handle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_start(capt->handle)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
diff = remove_samples(loop, 1, (delay1 - fill) / capt->pitch);
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: capt stop removed %li samples\n", (long)diff);
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (delay1 > fill) {
|
|
Packit |
229ac0 |
diff = (delay1 - fill) / play->pitch;
|
|
Packit |
229ac0 |
if (diff > play->buf_count)
|
|
Packit |
229ac0 |
diff = play->buf_count;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: removing %li playback samples, delay1=%li\n", (long)diff, (long)delay1);
|
|
Packit |
229ac0 |
diff = remove_samples(loop, 0, diff);
|
|
Packit |
229ac0 |
pdelay -= diff;
|
|
Packit |
229ac0 |
pdelay1 = pdelay * play->pitch;
|
|
Packit |
229ac0 |
delay1 = cdelay1 + pdelay1;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: removed %li playback samples, delay1=%li\n", (long)diff, (long)delay1);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (delay1 > fill) {
|
|
Packit |
229ac0 |
diff = (delay1 - fill) / capt->pitch;
|
|
Packit |
229ac0 |
if (diff > capt->buf_count)
|
|
Packit |
229ac0 |
diff = capt->buf_count;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: removing %li captured samples, delay1=%li\n", (long)diff, (long)delay1);
|
|
Packit |
229ac0 |
diff -= remove_samples(loop, 1, diff);
|
|
Packit |
229ac0 |
cdelay -= diff;
|
|
Packit |
229ac0 |
cdelay1 = cdelay * capt->pitch;
|
|
Packit |
229ac0 |
delay1 = cdelay1 + pdelay1;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: removed %li captured samples, delay1=%li\n", (long)diff, (long)delay1);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (play->xrun_pending) {
|
|
Packit |
229ac0 |
play->xrun_pending = 0;
|
|
Packit |
229ac0 |
diff = (fill - delay1) / play->pitch;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: xrun_pending, silence filling %li / buf_count=%li\n", (long)diff, play->buf_count);
|
|
Packit |
229ac0 |
if (fill > delay1 && play->buf_count < diff) {
|
|
Packit |
229ac0 |
diff = diff - play->buf_count;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: playback silence added %li samples\n", (long)diff);
|
|
Packit |
229ac0 |
play->buf_pos -= diff;
|
|
Packit |
229ac0 |
play->buf_pos %= play->buf_size;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_format_set_silence(play->format, play->buf + play->buf_pos * play->channels, diff)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
play->buf_count += diff;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = snd_pcm_prepare(play->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s prepare failed: %s\n", play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
delay1 = writeit(play);
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: playback wrote %li samples\n", (long)delay1);
|
|
Packit |
229ac0 |
if (delay1 > diff) {
|
|
Packit |
229ac0 |
buf_remove(loop, delay1 - diff);
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: playback buf_remove %li samples\n", (long)(delay1 - diff));
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = snd_pcm_start(play->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s start failed: %s\n", play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
} else if (delay1 < fill) {
|
|
Packit |
229ac0 |
diff = (fill - delay1) / play->pitch;
|
|
Packit |
229ac0 |
while (diff > 0) {
|
|
Packit |
229ac0 |
delay1 = play->buf_size - play->buf_pos;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output,
|
|
Packit |
229ac0 |
"sync: playback short, silence filling %li / buf_count=%li\n", (long)delay1, play->buf_count);
|
|
Packit |
229ac0 |
if (delay1 > diff)
|
|
Packit |
229ac0 |
delay1 = diff;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_format_set_silence(play->format, play->buf + play->buf_pos * play->channels, delay1)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
play->buf_pos += delay1;
|
|
Packit |
229ac0 |
play->buf_pos %= play->buf_size;
|
|
Packit |
229ac0 |
play->buf_count += delay1;
|
|
Packit |
229ac0 |
diff -= delay1;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
writeit(play);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (verbose > 5) {
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: xrun sync ok\n", loop->id);
|
|
Packit |
229ac0 |
if (verbose > 6) {
|
|
Packit |
229ac0 |
if (snd_pcm_delay(capt->handle, &cdelay) < 0)
|
|
Packit |
229ac0 |
cdelay = -1;
|
|
Packit |
229ac0 |
if (snd_pcm_delay(play->handle, &pdelay) < 0)
|
|
Packit |
229ac0 |
pdelay = -1;
|
|
Packit |
229ac0 |
if (play->buf != capt->buf)
|
|
Packit |
229ac0 |
cdelay += capt->buf_count;
|
|
Packit |
229ac0 |
pdelay += play->buf_count;
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
pdelay += loop->src_out_frames;
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
cdelay1 = cdelay * capt->pitch;
|
|
Packit |
229ac0 |
pdelay1 = pdelay * play->pitch;
|
|
Packit |
229ac0 |
delay1 = cdelay1 + pdelay1;
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: sync verify: %li\n", loop->id, delay1);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->xrun_max_proctime = 0;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int set_notify(struct loopback_handle *lhandle, int enable)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->ctl_notify == NULL)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
snd_ctl_elem_value_set_boolean(lhandle->ctl_notify, 0, enable);
|
|
Packit |
229ac0 |
err = snd_ctl_elem_write(lhandle->ctl, lhandle->ctl_notify);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Cannot set PCM Notify element for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_notify);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Cannot get PCM Notify element for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int set_rate_shift(struct loopback_handle *lhandle, double pitch)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->ctl_rate_shift == NULL)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
snd_ctl_elem_value_set_integer(lhandle->ctl_rate_shift, 0, pitch * 100000);
|
|
Packit |
229ac0 |
err = snd_ctl_elem_write(lhandle->ctl, lhandle->ctl_rate_shift);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Cannot set PCM Rate Shift element for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
void update_pitch(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
double pitch = loop->pitch;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_SAMPLERATE) {
|
|
Packit |
229ac0 |
loop->src_data.src_ratio = (double)1.0 / (pitch *
|
|
Packit |
229ac0 |
loop->play->pitch * loop->capt->pitch);
|
|
Packit |
229ac0 |
if (verbose > 2)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: Samplerate src_ratio update1: %.8f\n", loop->id, loop->src_data.src_ratio);
|
|
Packit |
229ac0 |
} else
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_CAPTRATESHIFT) {
|
|
Packit |
229ac0 |
set_rate_shift(loop->capt, pitch);
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
if (loop->use_samplerate) {
|
|
Packit |
229ac0 |
loop->src_data.src_ratio =
|
|
Packit |
229ac0 |
(double)1.0 /
|
|
Packit |
229ac0 |
(loop->play->pitch * loop->capt->pitch);
|
|
Packit |
229ac0 |
if (verbose > 2)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: Samplerate src_ratio update2: %.8f\n", loop->id, loop->src_data.src_ratio);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
else if (loop->sync == SYNC_TYPE_PLAYRATESHIFT) {
|
|
Packit |
229ac0 |
set_rate_shift(loop->play, pitch);
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
if (loop->use_samplerate) {
|
|
Packit |
229ac0 |
loop->src_data.src_ratio =
|
|
Packit |
229ac0 |
(double)1.0 /
|
|
Packit |
229ac0 |
(loop->play->pitch * loop->capt->pitch);
|
|
Packit |
229ac0 |
if (verbose > 2)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: Samplerate src_ratio update3: %.8f\n", loop->id, loop->src_data.src_ratio);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (verbose)
|
|
Packit |
229ac0 |
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);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int get_active(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->ctl_active == NULL)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_active);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Cannot get PCM Slave Active element for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return snd_ctl_elem_value_get_boolean(lhandle->ctl_active, 0);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int get_format(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->ctl_format == NULL)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_format);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Cannot get PCM Format element for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return snd_ctl_elem_value_get_integer(lhandle->ctl_format, 0);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int get_rate(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->ctl_rate == NULL)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_rate);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Cannot get PCM Rate element for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return snd_ctl_elem_value_get_integer(lhandle->ctl_rate, 0);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int get_channels(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (lhandle->ctl_channels == NULL)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
err = snd_ctl_elem_read(lhandle->ctl, lhandle->ctl_channels);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "Cannot get PCM Channels element for %s: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return snd_ctl_elem_value_get_integer(lhandle->ctl_channels, 0);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void openctl_elem(struct loopback_handle *lhandle,
|
|
Packit |
229ac0 |
int device, int subdevice,
|
|
Packit |
229ac0 |
const char *name,
|
|
Packit |
229ac0 |
snd_ctl_elem_value_t **elem)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (snd_ctl_elem_value_malloc(elem) < 0) {
|
|
Packit |
229ac0 |
*elem = NULL;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
snd_ctl_elem_value_set_interface(*elem,
|
|
Packit |
229ac0 |
SND_CTL_ELEM_IFACE_PCM);
|
|
Packit |
229ac0 |
snd_ctl_elem_value_set_device(*elem, device);
|
|
Packit |
229ac0 |
snd_ctl_elem_value_set_subdevice(*elem, subdevice);
|
|
Packit |
229ac0 |
snd_ctl_elem_value_set_name(*elem, name);
|
|
Packit |
229ac0 |
err = snd_ctl_elem_read(lhandle->ctl, *elem);
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
snd_ctl_elem_value_free(*elem);
|
|
Packit |
229ac0 |
*elem = NULL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int openctl(struct loopback_handle *lhandle, int device, int subdevice)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
lhandle->ctl_rate_shift = NULL;
|
|
Packit |
229ac0 |
if (lhandle->loopback->play == lhandle) {
|
|
Packit |
229ac0 |
if (lhandle->loopback->controls)
|
|
Packit |
229ac0 |
goto __events;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
openctl_elem(lhandle, device, subdevice, "PCM Notify",
|
|
Packit |
229ac0 |
&lhandle->ctl_notify);
|
|
Packit |
229ac0 |
openctl_elem(lhandle, device, subdevice, "PCM Rate Shift 100000",
|
|
Packit |
229ac0 |
&lhandle->ctl_rate_shift);
|
|
Packit |
229ac0 |
set_rate_shift(lhandle, 1);
|
|
Packit |
229ac0 |
openctl_elem(lhandle, device, subdevice, "PCM Slave Active",
|
|
Packit |
229ac0 |
&lhandle->ctl_active);
|
|
Packit |
229ac0 |
openctl_elem(lhandle, device, subdevice, "PCM Slave Format",
|
|
Packit |
229ac0 |
&lhandle->ctl_format);
|
|
Packit |
229ac0 |
openctl_elem(lhandle, device, subdevice, "PCM Slave Rate",
|
|
Packit |
229ac0 |
&lhandle->ctl_rate);
|
|
Packit |
229ac0 |
openctl_elem(lhandle, device, subdevice, "PCM Slave Channels",
|
|
Packit |
229ac0 |
&lhandle->ctl_channels);
|
|
Packit |
229ac0 |
if ((lhandle->ctl_active &&
|
|
Packit |
229ac0 |
lhandle->ctl_format &&
|
|
Packit |
229ac0 |
lhandle->ctl_rate &&
|
|
Packit |
229ac0 |
lhandle->ctl_channels) ||
|
|
Packit |
229ac0 |
lhandle->loopback->controls) {
|
|
Packit |
229ac0 |
__events:
|
|
Packit |
229ac0 |
if ((err = snd_ctl_poll_descriptors_count(lhandle->ctl)) < 0)
|
|
Packit |
229ac0 |
lhandle->ctl_pollfd_count = 0;
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
lhandle->ctl_pollfd_count = err;
|
|
Packit |
229ac0 |
if (snd_ctl_subscribe_events(lhandle->ctl, 1) < 0)
|
|
Packit |
229ac0 |
lhandle->ctl_pollfd_count = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int openit(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_info_t *info;
|
|
Packit |
229ac0 |
int stream = lhandle == lhandle->loopback->play ?
|
|
Packit |
229ac0 |
SND_PCM_STREAM_PLAYBACK :
|
|
Packit |
229ac0 |
SND_PCM_STREAM_CAPTURE;
|
|
Packit |
229ac0 |
int err, card, device, subdevice;
|
|
Packit |
229ac0 |
pcm_open_lock();
|
|
Packit |
229ac0 |
err = snd_pcm_open(&lhandle->handle, lhandle->device, stream, SND_PCM_NONBLOCK);
|
|
Packit |
229ac0 |
pcm_open_unlock();
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s open error: %s\n", lhandle->id, snd_strerror(err));
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = snd_pcm_info_malloc(&info)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_info(lhandle->handle, info)) < 0) {
|
|
Packit |
229ac0 |
snd_pcm_info_free(info);
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
card = snd_pcm_info_get_card(info);
|
|
Packit |
229ac0 |
device = snd_pcm_info_get_device(info);
|
|
Packit |
229ac0 |
subdevice = snd_pcm_info_get_subdevice(info);
|
|
Packit |
229ac0 |
snd_pcm_info_free(info);
|
|
Packit |
229ac0 |
lhandle->card_number = card;
|
|
Packit |
229ac0 |
lhandle->ctl = NULL;
|
|
Packit |
229ac0 |
if (card >= 0 || lhandle->ctldev) {
|
|
Packit |
229ac0 |
char name[16], *dev = lhandle->ctldev;
|
|
Packit |
229ac0 |
if (dev == NULL) {
|
|
Packit |
229ac0 |
sprintf(name, "hw:%i", card);
|
|
Packit |
229ac0 |
dev = name;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
pcm_open_lock();
|
|
Packit |
229ac0 |
err = snd_ctl_open(&lhandle->ctl, dev, SND_CTL_NONBLOCK);
|
|
Packit |
229ac0 |
pcm_open_unlock();
|
|
Packit |
229ac0 |
if (err < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s [%s] ctl open error: %s\n", lhandle->id, dev, snd_strerror(err));
|
|
Packit |
229ac0 |
lhandle->ctl = NULL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (lhandle->ctl)
|
|
Packit |
229ac0 |
openctl(lhandle, device, subdevice);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int freeit(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
free(lhandle->buf);
|
|
Packit |
229ac0 |
lhandle->buf = NULL;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int closeit(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err = 0;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
set_rate_shift(lhandle, 1);
|
|
Packit |
229ac0 |
if (lhandle->ctl_rate_shift)
|
|
Packit |
229ac0 |
snd_ctl_elem_value_free(lhandle->ctl_rate_shift);
|
|
Packit |
229ac0 |
lhandle->ctl_rate_shift = NULL;
|
|
Packit |
229ac0 |
if (lhandle->ctl)
|
|
Packit |
229ac0 |
err = snd_ctl_close(lhandle->ctl);
|
|
Packit |
229ac0 |
lhandle->ctl = NULL;
|
|
Packit |
229ac0 |
if (lhandle->handle)
|
|
Packit |
229ac0 |
err = snd_pcm_close(lhandle->handle);
|
|
Packit |
229ac0 |
lhandle->handle = NULL;
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int init_handle(struct loopback_handle *lhandle, int alloc)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_uframes_t lat;
|
|
Packit |
229ac0 |
lhandle->frame_size = (snd_pcm_format_physical_width(lhandle->format)
|
|
Packit |
229ac0 |
/ 8) * lhandle->channels;
|
|
Packit |
229ac0 |
lhandle->sync_point = lhandle->rate * 15; /* every 15 seconds */
|
|
Packit |
229ac0 |
lat = lhandle->loopback->latency;
|
|
Packit |
229ac0 |
if (lhandle->buffer_size > lat)
|
|
Packit |
229ac0 |
lat = lhandle->buffer_size;
|
|
Packit |
229ac0 |
lhandle->buf_size = lat * 2;
|
|
Packit |
229ac0 |
if (alloc) {
|
|
Packit |
229ac0 |
lhandle->buf = calloc(1, lhandle->buf_size * lhandle->frame_size);
|
|
Packit |
229ac0 |
if (lhandle->buf == NULL)
|
|
Packit |
229ac0 |
return -ENOMEM;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
int pcmjob_init(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
char id[128];
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#ifdef FILE_CWRITE
|
|
Packit |
229ac0 |
loop->cfile = fopen(FILE_CWRITE, "w+");
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
#ifdef FILE_PWRITE
|
|
Packit |
229ac0 |
loop->pfile = fopen(FILE_PWRITE, "w+");
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
if ((err = openit(loop->play)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if ((err = openit(loop->capt)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
snprintf(id, sizeof(id), "%s/%s", loop->play->id, loop->capt->id);
|
|
Packit |
229ac0 |
id[sizeof(id)-1] = '\0';
|
|
Packit |
229ac0 |
loop->id = strdup(id);
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_AUTO && loop->capt->ctl_rate_shift)
|
|
Packit |
229ac0 |
loop->sync = SYNC_TYPE_CAPTRATESHIFT;
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_AUTO && loop->play->ctl_rate_shift)
|
|
Packit |
229ac0 |
loop->sync = SYNC_TYPE_PLAYRATESHIFT;
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_AUTO && loop->src_enable)
|
|
Packit |
229ac0 |
loop->sync = SYNC_TYPE_SAMPLERATE;
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_AUTO)
|
|
Packit |
229ac0 |
loop->sync = SYNC_TYPE_SIMPLE;
|
|
Packit |
229ac0 |
if (loop->slave == SLAVE_TYPE_AUTO &&
|
|
Packit |
229ac0 |
loop->capt->ctl_notify &&
|
|
Packit |
229ac0 |
loop->capt->ctl_active &&
|
|
Packit |
229ac0 |
loop->capt->ctl_format &&
|
|
Packit |
229ac0 |
loop->capt->ctl_rate &&
|
|
Packit |
229ac0 |
loop->capt->ctl_channels)
|
|
Packit |
229ac0 |
loop->slave = SLAVE_TYPE_ON;
|
|
Packit |
229ac0 |
if (loop->slave == SLAVE_TYPE_ON) {
|
|
Packit |
229ac0 |
err = set_notify(loop->capt, 1);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if (loop->capt->ctl_notify == NULL ||
|
|
Packit |
229ac0 |
snd_ctl_elem_value_get_boolean(loop->capt->ctl_notify, 0) == 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "unable to enable slave mode for %s\n", loop->id);
|
|
Packit |
229ac0 |
err = -EINVAL;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = control_init(loop);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
__error:
|
|
Packit |
229ac0 |
pcmjob_done(loop);
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void freeloop(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
if (loop->use_samplerate) {
|
|
Packit |
229ac0 |
if (loop->src_state)
|
|
Packit |
229ac0 |
src_delete(loop->src_state);
|
|
Packit |
229ac0 |
loop->src_state = NULL;
|
|
Packit |
229ac0 |
free((void *)loop->src_data.data_in);
|
|
Packit |
229ac0 |
loop->src_data.data_in = NULL;
|
|
Packit |
229ac0 |
free(loop->src_data.data_out);
|
|
Packit |
229ac0 |
loop->src_data.data_out = NULL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
if (loop->play->buf == loop->capt->buf)
|
|
Packit |
229ac0 |
loop->play->buf = NULL;
|
|
Packit |
229ac0 |
freeit(loop->play);
|
|
Packit |
229ac0 |
freeit(loop->capt);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
int pcmjob_done(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
control_done(loop);
|
|
Packit |
229ac0 |
closeit(loop->play);
|
|
Packit |
229ac0 |
closeit(loop->capt);
|
|
Packit |
229ac0 |
freeloop(loop);
|
|
Packit |
229ac0 |
free(loop->id);
|
|
Packit |
229ac0 |
loop->id = NULL;
|
|
Packit |
229ac0 |
#ifdef FILE_PWRITE
|
|
Packit |
229ac0 |
if (loop->pfile) {
|
|
Packit |
229ac0 |
fclose(loop->pfile);
|
|
Packit |
229ac0 |
loop->pfile = NULL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
#ifdef FILE_CWRITE
|
|
Packit |
229ac0 |
if (loop->cfile) {
|
|
Packit |
229ac0 |
fclose(loop->cfile);
|
|
Packit |
229ac0 |
loop->cfile = NULL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void lhandle_start(struct loopback_handle *lhandle)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
lhandle->buf_pos = 0;
|
|
Packit |
229ac0 |
lhandle->buf_count = 0;
|
|
Packit |
229ac0 |
lhandle->counter = 0;
|
|
Packit |
229ac0 |
lhandle->total_queued = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void fix_format(struct loopback *loop, int force)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_format_t format = loop->capt->format;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (!force && loop->sync != SYNC_TYPE_SAMPLERATE)
|
|
Packit |
229ac0 |
return;
|
|
Packit |
229ac0 |
if (format == SND_PCM_FORMAT_S16 ||
|
|
Packit |
229ac0 |
format == SND_PCM_FORMAT_S32)
|
|
Packit |
229ac0 |
return;
|
|
Packit |
229ac0 |
if (snd_pcm_format_width(format) > 16)
|
|
Packit |
229ac0 |
format = SND_PCM_FORMAT_S32;
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
format = SND_PCM_FORMAT_S16;
|
|
Packit |
229ac0 |
loop->capt->format = format;
|
|
Packit |
229ac0 |
loop->play->format = format;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
int pcmjob_start(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_uframes_t count;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
loop->pollfd_count = loop->play->ctl_pollfd_count +
|
|
Packit |
229ac0 |
loop->capt->ctl_pollfd_count;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_poll_descriptors_count(loop->play->handle)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
loop->play->pollfd_count = err;
|
|
Packit |
229ac0 |
loop->pollfd_count += err;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_poll_descriptors_count(loop->capt->handle)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
loop->capt->pollfd_count = err;
|
|
Packit |
229ac0 |
loop->pollfd_count += err;
|
|
Packit |
229ac0 |
if (loop->slave == SLAVE_TYPE_ON) {
|
|
Packit |
229ac0 |
err = get_active(loop->capt);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if (err == 0) /* stream is not active */
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
err = get_format(loop->capt);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
loop->play->format = loop->capt->format = err;
|
|
Packit |
229ac0 |
fix_format(loop, 0);
|
|
Packit |
229ac0 |
err = get_rate(loop->capt);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
loop->play->rate_req = loop->capt->rate_req = err;
|
|
Packit |
229ac0 |
err = get_channels(loop->capt);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
loop->play->channels = loop->capt->channels = err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->reinit = 0;
|
|
Packit |
229ac0 |
loop->use_samplerate = 0;
|
|
Packit |
229ac0 |
__again:
|
|
Packit |
229ac0 |
if (loop->latency_req) {
|
|
Packit |
229ac0 |
loop->latency_reqtime = frames_to_time(loop->play->rate_req,
|
|
Packit |
229ac0 |
loop->latency_req);
|
|
Packit |
229ac0 |
loop->latency_req = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->latency = time_to_frames(loop->play->rate_req, loop->latency_reqtime);
|
|
Packit |
229ac0 |
if ((err = setparams(loop, loop->latency/2)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if (verbose)
|
|
Packit |
229ac0 |
showlatency(loop->output, loop->latency, loop->play->rate_req, "Latency");
|
|
Packit |
229ac0 |
if (loop->play->access == loop->capt->access &&
|
|
Packit |
229ac0 |
loop->play->format == loop->capt->format &&
|
|
Packit |
229ac0 |
loop->play->rate == loop->capt->rate &&
|
|
Packit |
229ac0 |
loop->play->channels == loop->capt->channels &&
|
|
Packit |
229ac0 |
loop->sync != SYNC_TYPE_SAMPLERATE) {
|
|
Packit |
229ac0 |
if (verbose > 1)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "shared buffer!!!\n");
|
|
Packit |
229ac0 |
if ((err = init_handle(loop->play, 1)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if ((err = init_handle(loop->capt, 0)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if (loop->play->buf_size < loop->capt->buf_size) {
|
|
Packit |
229ac0 |
char *nbuf = realloc(loop->play->buf,
|
|
Packit |
229ac0 |
loop->capt->buf_size *
|
|
Packit |
229ac0 |
loop->capt->frame_size);
|
|
Packit |
229ac0 |
if (nbuf == NULL) {
|
|
Packit |
229ac0 |
err = -ENOMEM;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->play->buf = nbuf;
|
|
Packit |
229ac0 |
loop->play->buf_size = loop->capt->buf_size;
|
|
Packit |
229ac0 |
} else if (loop->capt->buf_size < loop->play->buf_size) {
|
|
Packit |
229ac0 |
char *nbuf = realloc(loop->capt->buf,
|
|
Packit |
229ac0 |
loop->play->buf_size *
|
|
Packit |
229ac0 |
loop->play->frame_size);
|
|
Packit |
229ac0 |
if (nbuf == NULL) {
|
|
Packit |
229ac0 |
err = -ENOMEM;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->capt->buf = nbuf;
|
|
Packit |
229ac0 |
loop->capt->buf_size = loop->play->buf_size;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->capt->buf = loop->play->buf;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
if ((err = init_handle(loop->play, 1)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if ((err = init_handle(loop->capt, 1)) < 0)
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
if (loop->play->rate_req != loop->play->rate ||
|
|
Packit |
229ac0 |
loop->capt->rate_req != loop->capt->rate) {
|
|
Packit |
229ac0 |
snd_pcm_format_t format1, format2;
|
|
Packit |
229ac0 |
loop->use_samplerate = 1;
|
|
Packit |
229ac0 |
format1 = loop->play->format;
|
|
Packit |
229ac0 |
format2 = loop->capt->format;
|
|
Packit |
229ac0 |
fix_format(loop, 1);
|
|
Packit |
229ac0 |
if (loop->play->format != format1 ||
|
|
Packit |
229ac0 |
loop->capt->format != format2) {
|
|
Packit |
229ac0 |
pcmjob_stop(loop);
|
|
Packit |
229ac0 |
goto __again;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_SAMPLERATE)
|
|
Packit |
229ac0 |
loop->use_samplerate = 1;
|
|
Packit |
229ac0 |
if (loop->use_samplerate && !loop->src_enable) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "samplerate conversion required but disabled\n");
|
|
Packit |
229ac0 |
loop->use_samplerate = 0;
|
|
Packit |
229ac0 |
err = -EIO;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (loop->use_samplerate) {
|
|
Packit |
229ac0 |
if ((loop->capt->format != SND_PCM_FORMAT_S16 ||
|
|
Packit |
229ac0 |
loop->play->format != SND_PCM_FORMAT_S16) &&
|
|
Packit |
229ac0 |
(loop->capt->format != SND_PCM_FORMAT_S32 ||
|
|
Packit |
229ac0 |
loop->play->format != SND_PCM_FORMAT_S32)) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "samplerate conversion supports only %s or %s formats (play=%s, capt=%s)\n", snd_pcm_format_name(SND_PCM_FORMAT_S16), snd_pcm_format_name(SND_PCM_FORMAT_S32), snd_pcm_format_name(loop->play->format), snd_pcm_format_name(loop->capt->format));
|
|
Packit |
229ac0 |
loop->use_samplerate = 0;
|
|
Packit |
229ac0 |
err = -EIO;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->src_state = src_new(loop->src_converter_type,
|
|
Packit |
229ac0 |
loop->play->channels, &err;;
|
|
Packit |
229ac0 |
loop->src_data.data_in = calloc(1, sizeof(float)*loop->capt->channels*loop->capt->buf_size);
|
|
Packit |
229ac0 |
if (loop->src_data.data_in == NULL) {
|
|
Packit |
229ac0 |
err = -ENOMEM;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->src_data.data_out = calloc(1, sizeof(float)*loop->play->channels*loop->play->buf_size);
|
|
Packit |
229ac0 |
if (loop->src_data.data_out == NULL) {
|
|
Packit |
229ac0 |
err = -ENOMEM;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->src_data.src_ratio = (double)loop->play->rate /
|
|
Packit |
229ac0 |
(double)loop->capt->rate;
|
|
Packit |
229ac0 |
loop->src_data.end_of_input = 0;
|
|
Packit |
229ac0 |
loop->src_out_frames = 0;
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
loop->src_state = NULL;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#else
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_SAMPLERATE || loop->use_samplerate) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "alsaloop is compiled without libsamplerate support\n");
|
|
Packit |
229ac0 |
err = -EIO;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
if (verbose) {
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s sync type: %s", loop->id, sync_types[loop->sync]);
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
if (loop->sync == SYNC_TYPE_SAMPLERATE)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, " (%s)", src_types[loop->src_converter_type]);
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "\n");
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
lhandle_start(loop->play);
|
|
Packit |
229ac0 |
lhandle_start(loop->capt);
|
|
Packit |
229ac0 |
if ((err = snd_pcm_format_set_silence(loop->play->format,
|
|
Packit |
229ac0 |
loop->play->buf,
|
|
Packit |
229ac0 |
loop->play->buf_size * loop->play->channels)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s: silence error\n", loop->id);
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (verbose > 4)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: capt->buffer_size = %li, play->buffer_size = %li\n", loop->id, loop->capt->buf_size, loop->play->buf_size);
|
|
Packit |
229ac0 |
loop->pitch = 1.0;
|
|
Packit |
229ac0 |
update_pitch(loop);
|
|
Packit |
229ac0 |
loop->pitch_delta = 1.0 / ((double)loop->capt->rate * 4);
|
|
Packit |
229ac0 |
loop->total_queued_count = 0;
|
|
Packit |
229ac0 |
loop->pitch_diff = 0;
|
|
Packit |
229ac0 |
count = get_whole_latency(loop) / loop->play->pitch;
|
|
Packit |
229ac0 |
loop->play->buf_count = count;
|
|
Packit |
229ac0 |
if (loop->play->buf == loop->capt->buf)
|
|
Packit |
229ac0 |
loop->capt->buf_pos = count;
|
|
Packit |
229ac0 |
err = writeit(loop->play);
|
|
Packit |
229ac0 |
if (verbose > 4)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: silence queued %i samples\n", loop->id, err);
|
|
Packit |
229ac0 |
if (count > loop->play->buffer_size)
|
|
Packit |
229ac0 |
count = loop->play->buffer_size;
|
|
Packit |
229ac0 |
if (err != count) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "%s: initial playback fill error (%i/%i/%i)\n", loop->id, err, (int)count, loop->play->buffer_size);
|
|
Packit |
229ac0 |
err = -EIO;
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->running = 1;
|
|
Packit |
229ac0 |
loop->stop_pending = 0;
|
|
Packit |
229ac0 |
if (loop->xrun) {
|
|
Packit |
229ac0 |
getcurtimestamp(&loop->xrun_last_update);
|
|
Packit |
229ac0 |
loop->xrun_last_pdelay = XRUN_PROFILE_UNKNOWN;
|
|
Packit |
229ac0 |
loop->xrun_last_cdelay = XRUN_PROFILE_UNKNOWN;
|
|
Packit |
229ac0 |
loop->xrun_max_proctime = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if ((err = snd_pcm_start(loop->capt->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "pcm start %s error: %s\n", loop->capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (!loop->linked) {
|
|
Packit |
229ac0 |
if ((err = snd_pcm_start(loop->play->handle)) < 0) {
|
|
Packit |
229ac0 |
logit(LOG_CRIT, "pcm start %s error: %s\n", loop->play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
goto __error;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
__error:
|
|
Packit |
229ac0 |
pcmjob_stop(loop);
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
int pcmjob_stop(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (loop->running) {
|
|
Packit |
229ac0 |
if ((err = snd_pcm_drop(loop->capt->handle)) < 0)
|
|
Packit |
229ac0 |
logit(LOG_WARNING, "pcm drop %s error: %s\n", loop->capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
if ((err = snd_pcm_drop(loop->play->handle)) < 0)
|
|
Packit |
229ac0 |
logit(LOG_WARNING, "pcm drop %s error: %s\n", loop->play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
if ((err = snd_pcm_hw_free(loop->capt->handle)) < 0)
|
|
Packit |
229ac0 |
logit(LOG_WARNING, "pcm hw_free %s error: %s\n", loop->capt->id, snd_strerror(err));
|
|
Packit |
229ac0 |
if ((err = snd_pcm_hw_free(loop->play->handle)) < 0)
|
|
Packit |
229ac0 |
logit(LOG_WARNING, "pcm hw_free %s error: %s\n", loop->play->id, snd_strerror(err));
|
|
Packit |
229ac0 |
loop->running = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
freeloop(loop);
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
int pcmjob_pollfds_init(struct loopback *loop, struct pollfd *fds)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
int err, idx = 0;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (loop->running) {
|
|
Packit |
229ac0 |
err = snd_pcm_poll_descriptors(loop->play->handle, fds + idx, loop->play->pollfd_count);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
idx += loop->play->pollfd_count;
|
|
Packit |
229ac0 |
err = snd_pcm_poll_descriptors(loop->capt->handle, fds + idx, loop->capt->pollfd_count);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
idx += loop->capt->pollfd_count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (loop->play->ctl_pollfd_count > 0 &&
|
|
Packit |
229ac0 |
(loop->slave == SLAVE_TYPE_ON || loop->controls)) {
|
|
Packit |
229ac0 |
err = snd_ctl_poll_descriptors(loop->play->ctl, fds + idx, loop->play->ctl_pollfd_count);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
idx += loop->play->ctl_pollfd_count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (loop->capt->ctl_pollfd_count > 0 &&
|
|
Packit |
229ac0 |
(loop->slave == SLAVE_TYPE_ON || loop->controls)) {
|
|
Packit |
229ac0 |
err = snd_ctl_poll_descriptors(loop->capt->ctl, fds + idx, loop->capt->ctl_pollfd_count);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
idx += loop->capt->ctl_pollfd_count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->active_pollfd_count = idx;
|
|
Packit |
229ac0 |
return idx;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static snd_pcm_sframes_t get_queued_playback_samples(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_sframes_t delay;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(loop->play->handle, &delay)) < 0)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
loop->play->last_delay = delay;
|
|
Packit |
229ac0 |
delay += loop->play->buf_count;
|
|
Packit |
229ac0 |
#ifdef USE_SAMPLERATE
|
|
Packit |
229ac0 |
delay += loop->src_out_frames;
|
|
Packit |
229ac0 |
#endif
|
|
Packit |
229ac0 |
return delay;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static snd_pcm_sframes_t get_queued_capture_samples(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_pcm_sframes_t delay;
|
|
Packit |
229ac0 |
int err;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(loop->capt->handle, &delay)) < 0)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
loop->capt->last_delay = delay;
|
|
Packit |
229ac0 |
delay += loop->capt->buf_count;
|
|
Packit |
229ac0 |
return delay;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int ctl_event_check(snd_ctl_elem_value_t *val, snd_ctl_event_t *ev)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
snd_ctl_elem_id_t *id1, *id2;
|
|
Packit |
229ac0 |
snd_ctl_elem_id_alloca(&id1);
|
|
Packit |
229ac0 |
snd_ctl_elem_id_alloca(&id2);
|
|
Packit |
229ac0 |
snd_ctl_elem_value_get_id(val, id1);
|
|
Packit |
229ac0 |
snd_ctl_event_elem_get_id(ev, id2);
|
|
Packit |
229ac0 |
if (snd_ctl_event_elem_get_mask(ev) == SND_CTL_EVENT_MASK_REMOVE)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
if ((snd_ctl_event_elem_get_mask(ev) & SND_CTL_EVENT_MASK_VALUE) == 0)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
return control_id_match(id1, id2);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static int handle_ctl_events(struct loopback_handle *lhandle,
|
|
Packit |
229ac0 |
unsigned short events)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct loopback *loop = lhandle->loopback;
|
|
Packit |
229ac0 |
snd_ctl_event_t *ev;
|
|
Packit |
229ac0 |
int err, restart = 0;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
snd_ctl_event_alloca(&ev;;
|
|
Packit |
229ac0 |
while ((err = snd_ctl_read(lhandle->ctl, ev)) != 0 && err != -EAGAIN) {
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
if (snd_ctl_event_get_type(ev) != SND_CTL_EVENT_ELEM)
|
|
Packit |
229ac0 |
continue;
|
|
Packit |
229ac0 |
if (lhandle == loop->play)
|
|
Packit |
229ac0 |
goto __ctl_check;
|
|
Packit |
229ac0 |
if (verbose > 6)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: ctl event!!!! %s\n", lhandle->id, snd_ctl_event_elem_get_name(ev));
|
|
Packit |
229ac0 |
if (ctl_event_check(lhandle->ctl_active, ev)) {
|
|
Packit |
229ac0 |
continue;
|
|
Packit |
229ac0 |
} else if (ctl_event_check(lhandle->ctl_format, ev)) {
|
|
Packit |
229ac0 |
err = get_format(lhandle);
|
|
Packit |
229ac0 |
if (lhandle->format != err)
|
|
Packit |
229ac0 |
restart = 1;
|
|
Packit |
229ac0 |
continue;
|
|
Packit |
229ac0 |
} else if (ctl_event_check(lhandle->ctl_rate, ev)) {
|
|
Packit |
229ac0 |
err = get_rate(lhandle);
|
|
Packit |
229ac0 |
if (lhandle->rate != err)
|
|
Packit |
229ac0 |
restart = 1;
|
|
Packit |
229ac0 |
continue;
|
|
Packit |
229ac0 |
} else if (ctl_event_check(lhandle->ctl_channels, ev)) {
|
|
Packit |
229ac0 |
err = get_channels(lhandle);
|
|
Packit |
229ac0 |
if (lhandle->channels != err)
|
|
Packit |
229ac0 |
restart = 1;
|
|
Packit |
229ac0 |
continue;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
__ctl_check:
|
|
Packit |
229ac0 |
control_event(lhandle, ev);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
err = get_active(lhandle);
|
|
Packit |
229ac0 |
if (verbose > 7)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: ctl event active %i\n", lhandle->id, err);
|
|
Packit |
229ac0 |
if (!err) {
|
|
Packit |
229ac0 |
if (lhandle->loopback->running) {
|
|
Packit |
229ac0 |
loop->stop_pending = 1;
|
|
Packit |
229ac0 |
loop->stop_count = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
loop->stop_pending = 0;
|
|
Packit |
229ac0 |
if (loop->running == 0)
|
|
Packit |
229ac0 |
restart = 1;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (restart) {
|
|
Packit |
229ac0 |
pcmjob_stop(loop);
|
|
Packit |
229ac0 |
err = pcmjob_start(loop);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 1;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
int pcmjob_pollfds_handle(struct loopback *loop, struct pollfd *fds)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct loopback_handle *play = loop->play;
|
|
Packit |
229ac0 |
struct loopback_handle *capt = loop->capt;
|
|
Packit |
229ac0 |
unsigned short prevents, crevents, events;
|
|
Packit |
229ac0 |
snd_pcm_uframes_t ccount, pcount;
|
|
Packit |
229ac0 |
int err, loopcount = 0, idx;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
if (verbose > 11)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: pollfds handle\n", loop->id);
|
|
Packit |
229ac0 |
if (verbose > 13 || loop->xrun)
|
|
Packit |
229ac0 |
getcurtimestamp(&loop->tstamp_start);
|
|
Packit |
229ac0 |
if (verbose > 12) {
|
|
Packit |
229ac0 |
snd_pcm_sframes_t pdelay, cdelay;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(play->handle, &pdelay)) < 0)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: delay error: %s / %li / %li\n", play->id, snd_strerror(err), play->buf_size, play->buf_count);
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: delay %li / %li / %li\n", play->id, pdelay, play->buf_size, play->buf_count);
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(capt->handle, &cdelay)) < 0)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: delay error: %s / %li / %li\n", capt->id, snd_strerror(err), capt->buf_size, capt->buf_count);
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: delay %li / %li / %li\n", capt->id, cdelay, capt->buf_size, capt->buf_count);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
idx = 0;
|
|
Packit |
229ac0 |
if (loop->running) {
|
|
Packit |
229ac0 |
err = snd_pcm_poll_descriptors_revents(play->handle, fds,
|
|
Packit |
229ac0 |
play->pollfd_count,
|
|
Packit |
229ac0 |
&prevents);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
idx += play->pollfd_count;
|
|
Packit |
229ac0 |
err = snd_pcm_poll_descriptors_revents(capt->handle, fds + idx,
|
|
Packit |
229ac0 |
capt->pollfd_count,
|
|
Packit |
229ac0 |
&crevents);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
idx += capt->pollfd_count;
|
|
Packit |
229ac0 |
if (loop->xrun) {
|
|
Packit |
229ac0 |
if (prevents || crevents) {
|
|
Packit |
229ac0 |
loop->xrun_last_wake = loop->xrun_last_wake0;
|
|
Packit |
229ac0 |
loop->xrun_last_wake0 = loop->tstamp_start;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->xrun_last_check = loop->xrun_last_check0;
|
|
Packit |
229ac0 |
loop->xrun_last_check0 = loop->tstamp_start;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
} else {
|
|
Packit |
229ac0 |
prevents = crevents = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (play->ctl_pollfd_count > 0 &&
|
|
Packit |
229ac0 |
(loop->slave == SLAVE_TYPE_ON || loop->controls)) {
|
|
Packit |
229ac0 |
err = snd_ctl_poll_descriptors_revents(play->ctl, fds + idx,
|
|
Packit |
229ac0 |
play->ctl_pollfd_count,
|
|
Packit |
229ac0 |
&events);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
if (events) {
|
|
Packit |
229ac0 |
err = handle_ctl_events(play, events);
|
|
Packit |
229ac0 |
if (err == 1)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
idx += play->ctl_pollfd_count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (capt->ctl_pollfd_count > 0 &&
|
|
Packit |
229ac0 |
(loop->slave == SLAVE_TYPE_ON || loop->controls)) {
|
|
Packit |
229ac0 |
err = snd_ctl_poll_descriptors_revents(capt->ctl, fds + idx,
|
|
Packit |
229ac0 |
capt->ctl_pollfd_count,
|
|
Packit |
229ac0 |
&events);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
if (events) {
|
|
Packit |
229ac0 |
err = handle_ctl_events(capt, events);
|
|
Packit |
229ac0 |
if (err == 1)
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
idx += capt->ctl_pollfd_count;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (verbose > 9)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: prevents = 0x%x, crevents = 0x%x\n", loop->id, prevents, crevents);
|
|
Packit |
229ac0 |
if (!loop->running)
|
|
Packit |
229ac0 |
goto __pcm_end;
|
|
Packit |
229ac0 |
do {
|
|
Packit |
229ac0 |
ccount = readit(capt);
|
|
Packit |
229ac0 |
if (prevents != 0 && crevents == 0 &&
|
|
Packit |
229ac0 |
ccount == 0 && loopcount == 0) {
|
|
Packit |
229ac0 |
if (play->stall > 20) {
|
|
Packit |
229ac0 |
play->stall = 0;
|
|
Packit |
229ac0 |
increase_playback_avail_min(play);
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
play->stall++;
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (ccount > 0)
|
|
Packit |
229ac0 |
play->stall = 0;
|
|
Packit |
229ac0 |
buf_add(loop, ccount);
|
|
Packit |
229ac0 |
if (capt->xrun_pending || loop->reinit)
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
/* we read new samples, if we have a room in the playback
|
|
Packit |
229ac0 |
buffer, feed them there */
|
|
Packit |
229ac0 |
pcount = writeit(play);
|
|
Packit |
229ac0 |
buf_remove(loop, pcount);
|
|
Packit |
229ac0 |
if (pcount > 0)
|
|
Packit |
229ac0 |
play->stall = 0;
|
|
Packit |
229ac0 |
if (play->xrun_pending || loop->reinit)
|
|
Packit |
229ac0 |
break;
|
|
Packit |
229ac0 |
loopcount++;
|
|
Packit |
229ac0 |
} while ((ccount > 0 || pcount > 0) && loopcount > 10);
|
|
Packit |
229ac0 |
if (play->xrun_pending || capt->xrun_pending) {
|
|
Packit |
229ac0 |
if ((err = xrun_sync(loop)) < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (loop->reinit) {
|
|
Packit |
229ac0 |
err = pcmjob_stop(loop);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
err = pcmjob_start(loop);
|
|
Packit |
229ac0 |
if (err < 0)
|
|
Packit |
229ac0 |
return err;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (loop->sync != SYNC_TYPE_NONE &&
|
|
Packit |
229ac0 |
play->counter >= play->sync_point &&
|
|
Packit |
229ac0 |
capt->counter >= play->sync_point) {
|
|
Packit |
229ac0 |
snd_pcm_sframes_t diff, lat = get_whole_latency(loop);
|
|
Packit |
229ac0 |
diff = ((double)(((double)play->total_queued * play->pitch) +
|
|
Packit |
229ac0 |
((double)capt->total_queued * capt->pitch)) /
|
|
Packit |
229ac0 |
(double)loop->total_queued_count) - lat;
|
|
Packit |
229ac0 |
/* FIXME: this algorithm may be slightly better */
|
|
Packit |
229ac0 |
if (verbose > 3)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: sync diff %li old diff %li\n", loop->id, diff, loop->pitch_diff);
|
|
Packit |
229ac0 |
if (diff > 0) {
|
|
Packit |
229ac0 |
if (diff == loop->pitch_diff)
|
|
Packit |
229ac0 |
loop->pitch += loop->pitch_delta;
|
|
Packit |
229ac0 |
else if (diff > loop->pitch_diff)
|
|
Packit |
229ac0 |
loop->pitch += loop->pitch_delta*2;
|
|
Packit |
229ac0 |
} else if (diff < 0) {
|
|
Packit |
229ac0 |
if (diff == loop->pitch_diff)
|
|
Packit |
229ac0 |
loop->pitch -= loop->pitch_delta;
|
|
Packit |
229ac0 |
else if (diff < loop->pitch_diff)
|
|
Packit |
229ac0 |
loop->pitch -= loop->pitch_delta*2;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
loop->pitch_diff = diff;
|
|
Packit |
229ac0 |
if (loop->pitch_diff_min > diff)
|
|
Packit |
229ac0 |
loop->pitch_diff_min = diff;
|
|
Packit |
229ac0 |
if (loop->pitch_diff_max < diff)
|
|
Packit |
229ac0 |
loop->pitch_diff_max = diff;
|
|
Packit |
229ac0 |
update_pitch(loop);
|
|
Packit |
229ac0 |
play->counter -= play->sync_point;
|
|
Packit |
229ac0 |
capt->counter -= play->sync_point;
|
|
Packit |
229ac0 |
play->total_queued = 0;
|
|
Packit |
229ac0 |
capt->total_queued = 0;
|
|
Packit |
229ac0 |
loop->total_queued_count = 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (loop->sync != SYNC_TYPE_NONE) {
|
|
Packit |
229ac0 |
snd_pcm_sframes_t pqueued, cqueued;
|
|
Packit |
229ac0 |
pqueued = get_queued_playback_samples(loop);
|
|
Packit |
229ac0 |
cqueued = get_queued_capture_samples(loop);
|
|
Packit |
229ac0 |
if (verbose > 4)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: queued %li/%li samples\n", loop->id, pqueued, cqueued);
|
|
Packit |
229ac0 |
if (pqueued > 0)
|
|
Packit |
229ac0 |
play->total_queued += pqueued;
|
|
Packit |
229ac0 |
if (cqueued > 0)
|
|
Packit |
229ac0 |
capt->total_queued += cqueued;
|
|
Packit |
229ac0 |
if (pqueued > 0 || cqueued > 0)
|
|
Packit |
229ac0 |
loop->total_queued_count += 1;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
if (verbose > 12) {
|
|
Packit |
229ac0 |
snd_pcm_sframes_t pdelay, cdelay;
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(play->handle, &pdelay)) < 0)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: end delay error: %s / %li / %li\n", play->id, snd_strerror(err), play->buf_size, play->buf_count);
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: end delay %li / %li / %li\n", play->id, pdelay, play->buf_size, play->buf_count);
|
|
Packit |
229ac0 |
if ((err = snd_pcm_delay(capt->handle, &cdelay)) < 0)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: end delay error: %s / %li / %li\n", capt->id, snd_strerror(err), capt->buf_size, capt->buf_count);
|
|
Packit |
229ac0 |
else
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: end delay %li / %li / %li\n", capt->id, cdelay, capt->buf_size, capt->buf_count);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
__pcm_end:
|
|
Packit |
229ac0 |
if (verbose > 13 || loop->xrun) {
|
|
Packit |
229ac0 |
long diff;
|
|
Packit |
229ac0 |
getcurtimestamp(&loop->tstamp_end);
|
|
Packit |
229ac0 |
diff = timediff(loop->tstamp_end, loop->tstamp_start);
|
|
Packit |
229ac0 |
if (verbose > 13)
|
|
Packit |
229ac0 |
snd_output_printf(loop->output, "%s: processing time %lius\n", loop->id, diff);
|
|
Packit |
229ac0 |
if (loop->xrun && loop->xrun_max_proctime < diff)
|
|
Packit |
229ac0 |
loop->xrun_max_proctime = diff;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
return 0;
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
#define OUT(args...) \
|
|
Packit |
229ac0 |
snd_output_printf(loop->state, ##args)
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static pthread_mutex_t state_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
static void show_handle(struct loopback_handle *lhandle, const char *id)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
struct loopback *loop = lhandle->loopback;
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
OUT(" %s: %s:\n", id, lhandle->id);
|
|
Packit |
229ac0 |
OUT(" device = '%s', ctldev '%s'\n", lhandle->device, lhandle->ctldev);
|
|
Packit |
229ac0 |
OUT(" card_number = %i\n", lhandle->card_number);
|
|
Packit |
229ac0 |
if (!loop->running)
|
|
Packit |
229ac0 |
return;
|
|
Packit |
229ac0 |
OUT(" access = %s, format = %s, rate = %u, channels = %u\n", snd_pcm_access_name(lhandle->access), snd_pcm_format_name(lhandle->format), lhandle->rate, lhandle->channels);
|
|
Packit |
229ac0 |
OUT(" buffer_size = %u, period_size = %u, avail_min = %li\n", lhandle->buffer_size, lhandle->period_size, lhandle->avail_min);
|
|
Packit |
229ac0 |
OUT(" xrun_pending = %i\n", lhandle->xrun_pending);
|
|
Packit |
229ac0 |
OUT(" buf_size = %li, buf_pos = %li, buf_count = %li, buf_over = %li\n", lhandle->buf_size, lhandle->buf_pos, lhandle->buf_count, lhandle->buf_over);
|
|
Packit |
229ac0 |
OUT(" pitch = %.8f\n", lhandle->pitch);
|
|
Packit |
229ac0 |
}
|
|
Packit |
229ac0 |
|
|
Packit |
229ac0 |
void pcmjob_state(struct loopback *loop)
|
|
Packit |
229ac0 |
{
|
|
Packit |
229ac0 |
pthread_t self = pthread_self();
|
|
Packit |
229ac0 |
pthread_mutex_lock(&state_mutex);
|
|
Packit |
229ac0 |
OUT("State dump for thread %p job %i: %s:\n", (void *)self, loop->thread, loop->id);
|
|
Packit |
229ac0 |
OUT(" running = %i\n", loop->running);
|
|
Packit |
229ac0 |
OUT(" sync = %i\n", loop->sync);
|
|
Packit |
229ac0 |
OUT(" slave = %i\n", loop->slave);
|
|
Packit |
229ac0 |
if (!loop->running)
|
|
Packit |
229ac0 |
goto __skip;
|
|
Packit |
229ac0 |
OUT(" pollfd_count = %i\n", loop->pollfd_count);
|
|
Packit |
229ac0 |
OUT(" pitch = %.8f, delta = %.8f, diff = %li, min = %li, max = %li\n", loop->pitch, loop->pitch_delta, loop->pitch_diff, loop->pitch_diff_min, loop->pitch_diff_max);
|
|
Packit |
229ac0 |
OUT(" use_samplerate = %i\n", loop->use_samplerate);
|
|
Packit |
229ac0 |
__skip:
|
|
Packit |
229ac0 |
show_handle(loop->play, "playback");
|
|
Packit |
229ac0 |
show_handle(loop->capt, "capture");
|
|
Packit |
229ac0 |
pthread_mutex_unlock(&state_mutex);
|
|
Packit |
229ac0 |
}
|