mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2026-01-02 11:08:39 -05:00
In alsa-lib, snd_pcm_hw_params() internally calls snd_pcm_prepare(), thus user space applications have no need to call snd_pcm_prepare() after calls of snd_pcm_hw_params(). An explicit calls of snd_pcm_prepare() is expected in a case to recover PCM substreams. Current implementation of PulseAudio modules for ALSA playbacking/capturing results in double calls of snd_pcm_prepare(). The second call for hw plugin of alsa-lib executes ioctl(2) with SNDRV_PCM_IOCTL_PREPARE command in state of SNDRV_PCM_STATE_PREPARED for the PCM substream. This has no effects to the PCM substream as long as corresponding drivers are implemented correctly. This commit removes the second call for the reason. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
251 lines
7.2 KiB
C
251 lines
7.2 KiB
C
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <inttypes.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
|
|
#include <alsa/asoundlib.h>
|
|
|
|
#define SAMPLE_RATE 44100
|
|
#define CHANNELS 2
|
|
|
|
static uint64_t timespec_us(const struct timespec *ts) {
|
|
return
|
|
ts->tv_sec * 1000000LLU +
|
|
ts->tv_nsec / 1000LLU;
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
const char *dev;
|
|
int r, cap, count = 0;
|
|
snd_pcm_hw_params_t *hwparams;
|
|
snd_pcm_sw_params_t *swparams;
|
|
snd_pcm_status_t *status;
|
|
snd_pcm_t *pcm;
|
|
unsigned rate = SAMPLE_RATE;
|
|
unsigned periods = 2;
|
|
snd_pcm_uframes_t boundary, buffer_size = SAMPLE_RATE/10; /* 100s */
|
|
int dir = 1;
|
|
int fillrate;
|
|
struct timespec start, last_timestamp = { 0, 0 };
|
|
uint64_t start_us, last_us = 0;
|
|
snd_pcm_sframes_t last_avail = 0, last_delay = 0;
|
|
struct pollfd *pollfds;
|
|
int n_pollfd;
|
|
int64_t sample_count = 0;
|
|
uint16_t *samples;
|
|
struct sched_param sp;
|
|
|
|
r = -1;
|
|
#ifdef _POSIX_PRIORITY_SCHEDULING
|
|
sp.sched_priority = 5;
|
|
r = pthread_setschedparam(pthread_self(), SCHED_RR, &sp);
|
|
#endif
|
|
if (r)
|
|
printf("Could not get RT prio. :(\n");
|
|
|
|
snd_pcm_hw_params_alloca(&hwparams);
|
|
snd_pcm_sw_params_alloca(&swparams);
|
|
snd_pcm_status_alloca(&status);
|
|
|
|
r = clock_gettime(CLOCK_MONOTONIC, &start);
|
|
assert(r == 0);
|
|
|
|
start_us = timespec_us(&start);
|
|
|
|
dev = argc > 1 ? argv[1] : "front:0";
|
|
cap = argc > 2 ? atoi(argv[2]) : 0;
|
|
fillrate = argc > 3 ? atoi(argv[3]) : 1;
|
|
assert(fillrate > 0);
|
|
|
|
samples = calloc(fillrate, CHANNELS*sizeof(uint16_t));
|
|
assert(samples);
|
|
|
|
if (cap == 0)
|
|
r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_PLAYBACK, 0);
|
|
else
|
|
r = snd_pcm_open(&pcm, dev, SND_PCM_STREAM_CAPTURE, 0);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_any(pcm, hwparams);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 0);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, NULL);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_channels(pcm, hwparams, CHANNELS);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_periods_integer(pcm, hwparams);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_periods_near(pcm, hwparams, &periods, &dir);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_set_buffer_size_near(pcm, hwparams, &buffer_size);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params(pcm, hwparams);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_current(pcm, hwparams);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_sw_params_current(pcm, swparams);
|
|
assert(r == 0);
|
|
|
|
if (cap == 0)
|
|
r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 1);
|
|
else
|
|
r = snd_pcm_sw_params_set_avail_min(pcm, swparams, 0);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_sw_params_set_period_event(pcm, swparams, 0);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
|
|
assert(r == 0);
|
|
r = snd_pcm_sw_params_set_start_threshold(pcm, swparams, buffer_size - (buffer_size % fillrate));
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_sw_params_get_boundary(swparams, &boundary);
|
|
assert(r == 0);
|
|
r = snd_pcm_sw_params_set_stop_threshold(pcm, swparams, boundary);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_sw_params_set_tstamp_mode(pcm, swparams, SND_PCM_TSTAMP_ENABLE);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_sw_params(pcm, swparams);
|
|
assert(r == 0);
|
|
|
|
r = snd_pcm_sw_params_current(pcm, swparams);
|
|
assert(r == 0);
|
|
|
|
/* assert(snd_pcm_hw_params_is_monotonic(hwparams) > 0); */
|
|
|
|
n_pollfd = snd_pcm_poll_descriptors_count(pcm);
|
|
assert(n_pollfd > 0);
|
|
|
|
pollfds = malloc(sizeof(struct pollfd) * n_pollfd);
|
|
assert(pollfds);
|
|
|
|
r = snd_pcm_poll_descriptors(pcm, pollfds, n_pollfd);
|
|
assert(r == n_pollfd);
|
|
|
|
printf("Starting. Buffer size is %u frames\n", (unsigned int) buffer_size);
|
|
|
|
if (cap) {
|
|
r = snd_pcm_start(pcm);
|
|
assert(r == 0);
|
|
}
|
|
|
|
for (;;) {
|
|
snd_pcm_sframes_t avail, delay;
|
|
struct timespec now, timestamp;
|
|
unsigned short revents;
|
|
int handled = 0;
|
|
uint64_t now_us, timestamp_us;
|
|
snd_pcm_state_t state;
|
|
unsigned long long pos;
|
|
|
|
r = poll(pollfds, n_pollfd, 0);
|
|
assert(r >= 0);
|
|
|
|
r = snd_pcm_poll_descriptors_revents(pcm, pollfds, n_pollfd, &revents);
|
|
assert(r == 0);
|
|
|
|
if (cap == 0)
|
|
assert((revents & ~POLLOUT) == 0);
|
|
else
|
|
assert((revents & ~POLLIN) == 0);
|
|
|
|
avail = snd_pcm_avail(pcm);
|
|
assert(avail >= 0);
|
|
|
|
r = snd_pcm_status(pcm, status);
|
|
assert(r == 0);
|
|
|
|
/* This assertion fails from time to time. ALSA seems to be broken */
|
|
/* assert(avail == (snd_pcm_sframes_t) snd_pcm_status_get_avail(status)); */
|
|
/* printf("%lu %lu\n", (unsigned long) avail, (unsigned long) snd_pcm_status_get_avail(status)); */
|
|
|
|
snd_pcm_status_get_htstamp(status, ×tamp);
|
|
delay = snd_pcm_status_get_delay(status);
|
|
state = snd_pcm_status_get_state(status);
|
|
|
|
r = clock_gettime(CLOCK_MONOTONIC, &now);
|
|
assert(r == 0);
|
|
|
|
assert(!revents || avail > 0);
|
|
|
|
if ((!cap && (avail >= fillrate)) || (cap && (unsigned)avail >= buffer_size)) {
|
|
snd_pcm_sframes_t sframes;
|
|
|
|
if (cap == 0)
|
|
sframes = snd_pcm_writei(pcm, samples, fillrate);
|
|
else
|
|
sframes = snd_pcm_readi(pcm, samples, fillrate);
|
|
assert(sframes == fillrate);
|
|
|
|
handled = fillrate;
|
|
sample_count += fillrate;
|
|
}
|
|
|
|
if (!handled &&
|
|
memcmp(×tamp, &last_timestamp, sizeof(timestamp)) == 0 &&
|
|
avail == last_avail &&
|
|
delay == last_delay) {
|
|
/* This is boring */
|
|
continue;
|
|
}
|
|
|
|
now_us = timespec_us(&now);
|
|
timestamp_us = timespec_us(×tamp);
|
|
|
|
if (cap == 0)
|
|
pos = (unsigned long long) ((sample_count - handled - delay) * 1000000LU / SAMPLE_RATE);
|
|
else
|
|
pos = (unsigned long long) ((sample_count - handled + delay) * 1000000LU / SAMPLE_RATE);
|
|
|
|
if (count++ % 50 == 0)
|
|
printf("Elapsed\tCPU\tALSA\tPos\tSamples\tavail\tdelay\trevents\thandled\tstate\n");
|
|
|
|
printf("%llu\t%llu\t%llu\t%llu\t%llu\t%li\t%li\t%i\t%i\t%i\n",
|
|
(unsigned long long) (now_us - last_us),
|
|
(unsigned long long) (now_us - start_us),
|
|
(unsigned long long) (timestamp_us ? timestamp_us - start_us : 0),
|
|
pos,
|
|
(unsigned long long) sample_count,
|
|
(signed long) avail,
|
|
(signed long) delay,
|
|
revents,
|
|
handled,
|
|
state);
|
|
|
|
if (cap == 0)
|
|
/** When this assert is hit, most likely something bad
|
|
* happened, i.e. the avail jumped suddenly. */
|
|
assert((unsigned) avail <= buffer_size);
|
|
|
|
last_avail = avail;
|
|
last_delay = delay;
|
|
last_timestamp = timestamp;
|
|
last_us = now_us;
|
|
}
|
|
|
|
return 0;
|
|
}
|