static LIST_HEAD(slaves);
static pthread_mutex_t slaves_mutex = PTHREAD_MUTEX_INITIALIZER;
+char *slaves_mutex_holder;
+
+#define _S(x) #x
+#define S(x) _S(x)
+
+#if 1
+#define Pthread_mutex_lock(mutex) pthread_mutex_lock(mutex)
+#define Pthread_mutex_unlock(mutex) pthread_mutex_unlock(mutex)
+#else
+#define Pthread_mutex_lock(mutex) \
+do { \
+ int err = pthread_mutex_trylock(mutex); \
+ if (err == EBUSY) { \
+ fprintf(stderr, "lock " #mutex " is busy (%s): waiting in " __FUNCTION__ "\n", *(mutex##_holder)); \
+ pthread_mutex_lock(mutex); \
+ fprintf(stderr, "... got\n"); \
+ } \
+ *(mutex##_holder) = __FUNCTION__; \
+} while (0)
+
+#define Pthread_mutex_unlock(mutex) \
+do { \
+ *(mutex##_holder) = 0; \
+ pthread_mutex_unlock(mutex); \
+} while (0)
+#endif
typedef struct {
struct list_head clients;
struct list_head list;
snd_pcm_t *pcm;
+ int sformat;
+ int srate;
size_t channels_count;
size_t open_count;
size_t setup_count;
size_t prepared_count;
size_t running_count;
size_t safety_threshold;
+ size_t silence_frames;
+ size_t hw_ptr;
+ int poll[2];
+ int polling;
pthread_t thread;
pthread_mutex_t mutex;
+ char *mutex_holder;
+ pthread_cond_t poll_cond;
} snd_pcm_share_slave_t;
typedef struct {
int *slave_channels;
int xfer_mode;
int xrun_mode;
+ size_t avail_min;
int async_sig;
pid_t async_pid;
+ int drain_silenced;
struct timeval trigger_time;
int state;
size_t hw_ptr;
void *stopped_data;
} snd_pcm_share_t;
-
static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state);
+static size_t snd_pcm_share_slave_avail(snd_pcm_share_slave_t *slave)
+{
+ ssize_t avail;
+ snd_pcm_t *pcm = slave->pcm;
+ avail = slave->hw_ptr - *pcm->appl_ptr;
+ if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
+ avail += pcm->setup.buffer_size;
+ if (avail < 0)
+ avail += pcm->setup.boundary;
+ return avail;
+}
-static void _snd_pcm_update_poll(snd_pcm_t *pcm)
+/* Warning: take the mutex before to call this */
+/* Return number of frames to mmap_forward the slave */
+static size_t _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave)
+{
+ struct list_head *i;
+ size_t buffer_size, boundary;
+ size_t slave_appl_ptr;
+ ssize_t frames, safety_frames;
+ ssize_t min_frames, max_frames;
+ size_t avail, slave_avail;
+ size_t slave_hw_avail;
+ slave_avail = snd_pcm_share_slave_avail(slave);
+ boundary = slave->pcm->setup.boundary;
+ buffer_size = slave->pcm->setup.buffer_size;
+ min_frames = slave_avail;
+ max_frames = 0;
+ slave_appl_ptr = *slave->pcm->appl_ptr;
+ for (i = slave->clients.next; i != &slave->clients; i = i->next) {
+ snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
+ snd_pcm_t *pcm = share->pcm;
+ switch (share->state) {
+ case SND_PCM_STATE_RUNNING:
+ break;
+ case SND_PCM_STATE_DRAINING:
+ if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
+ continue;
+ break;
+ default:
+ continue;
+ }
+ avail = snd_pcm_mmap_avail(pcm);
+ frames = slave_avail - avail;
+ if (frames > max_frames)
+ max_frames = frames;
+ if (share->state != SND_PCM_STATE_RUNNING)
+ continue;
+ if (frames < min_frames)
+ min_frames = frames;
+ }
+ if (max_frames == 0)
+ return 0;
+ frames = min_frames;
+ /* Slave xrun prevention */
+ slave_hw_avail = buffer_size - slave_avail;
+ safety_frames = slave->safety_threshold - slave_hw_avail;
+ if (safety_frames > 0 &&
+ frames < safety_frames) {
+ /* Avoid to pass over the last */
+ if (max_frames < safety_frames)
+ frames = max_frames;
+ else
+ frames = safety_frames;
+ }
+ if (frames < 0)
+ return 0;
+ return frames;
+}
+
+
+/*
+ - stop PCM on xrun
+ - update poll status
+ - draining silencing
+ - return distance in frames to next event
+*/
+static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
- size_t avail;
- int ready;
+ snd_pcm_t *spcm = slave->pcm;
+ size_t buffer_size = spcm->setup.buffer_size;
+ int ready = 1, running = 0;
+ size_t avail = 0, slave_avail;
+ ssize_t hw_avail;
+ size_t missing = INT_MAX;
+ ssize_t ready_missing;
+ // printf("state=%d hw_ptr=%d appl_ptr=%d slave appl_ptr=%d safety=%d silence=%d\n", share->state, slave->hw_ptr, share->appl_ptr, *slave->pcm->appl_ptr, slave->safety_threshold, slave->silence_frames);
switch (share->state) {
+ case SND_PCM_STATE_RUNNING:
+ break;
case SND_PCM_STATE_DRAINING:
- if (pcm->stream == SND_PCM_STREAM_CAPTURE)
- ready = 1;
- else {
- share->hw_ptr = *slave->pcm->hw_ptr;
- avail = snd_pcm_mmap_avail(pcm);
- if (avail >= pcm->setup.buffer_size) {
+ if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
+ break;
+ /* Fall through */
+ default:
+ return INT_MAX;
+ }
+ if (slave_xrun && pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
+ _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
+ goto update_poll;
+ }
+ share->hw_ptr = slave->hw_ptr;
+ avail = snd_pcm_mmap_avail(pcm);
+ hw_avail = buffer_size - avail;
+ slave_avail = snd_pcm_share_slave_avail(slave);
+ if (avail < slave_avail) {
+ /* Some frames need still to be transferred */
+ ssize_t slave_hw_avail = buffer_size - slave_avail;
+ ssize_t safety_missing = slave_hw_avail - slave->safety_threshold;
+ if (safety_missing < 0) {
+ ssize_t err;
+ ssize_t frames = slave_avail - avail;
+ if (-safety_missing <= frames) {
+ frames = -safety_missing;
+ missing = 1;
+ }
+ err = snd_pcm_mmap_forward(spcm, frames);
+ assert(err == frames);
+ slave_avail -= frames;
+ } else {
+ if (safety_missing == 0)
+ missing = 1;
+ else
+ missing = safety_missing;
+ }
+ }
+ switch (share->state) {
+ case SND_PCM_STATE_DRAINING:
+ if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+ if (hw_avail <= 0) {
_snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
- ready = 1;
- } else
- ready = 0;
+ break;
+ }
+ if ((size_t)hw_avail < missing)
+ missing = hw_avail;
+ running = 1;
+ ready = 0;
}
break;
case SND_PCM_STATE_RUNNING:
- share->hw_ptr = *slave->pcm->hw_ptr;
- avail = snd_pcm_mmap_avail(pcm);
- if (avail >= pcm->setup.buffer_size &&
- pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
- _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
- ready = 1;
- } else
- ready = (avail >= pcm->setup.avail_min);
+ if (pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
+ if (hw_avail <= 0) {
+ _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
+ break;
+ }
+ if ((size_t)hw_avail < missing)
+ missing = hw_avail;
+ }
+ ready_missing = share->avail_min - avail;
+ if (ready_missing > 0) {
+ ready = 0;
+ if ((size_t)ready_missing < missing)
+ missing = ready_missing;
+ }
+ running = 1;
break;
- default:
- ready = 1;
}
+
+ update_poll:
if (ready != share->ready) {
char buf[1];
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
}
share->ready = ready;
}
+ if (!running)
+ return INT_MAX;
+ if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
+ share->state == SND_PCM_STATE_DRAINING &&
+ !share->drain_silenced) {
+ /* drain silencing */
+ if (avail >= slave->silence_frames) {
+ size_t offset = share->appl_ptr % buffer_size;
+ size_t xfer = 0;
+ size_t size = slave->silence_frames;
+ while (xfer < size) {
+ size_t frames = size - xfer;
+ size_t cont = buffer_size - offset;
+ if (cont < frames)
+ frames = cont;
+ snd_pcm_areas_silence(pcm->running_areas, offset, pcm->setup.format.channels, frames, pcm->setup.format.sfmt);
+ offset += frames;
+ if (offset >= buffer_size)
+ offset = 0;
+ xfer += frames;
+ }
+ share->drain_silenced = 1;
+ } else {
+ size_t silence_missing;
+ silence_missing = slave->silence_frames - avail;
+ if (silence_missing < missing)
+ missing = silence_missing;
+ }
+ }
+ // printf("missing=%d\n", missing);
+ return missing;
}
-static void snd_pcm_share_interrupt(snd_pcm_share_slave_t *slave)
+static size_t _snd_pcm_share_slave_missing(snd_pcm_share_slave_t *slave)
{
+ size_t missing = INT_MAX;
struct list_head *i;
- pthread_mutex_lock(&slave->mutex);
- snd_pcm_avail_update(slave->pcm);
- /* Update poll status */
+ ssize_t avail = snd_pcm_avail_update(slave->pcm);
+ int slave_xrun = (avail == -EPIPE);
+ slave->hw_ptr = *slave->pcm->hw_ptr;
for (i = slave->clients.next; i != &slave->clients; i = i->next) {
snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
snd_pcm_t *pcm = share->pcm;
- switch (share->state) {
- case SND_PCM_STATE_DRAINING:
- if (pcm->stream == SND_PCM_STREAM_CAPTURE)
- break;
- /* Fall through */
- case SND_PCM_STATE_RUNNING:
- if (pcm->mode & SND_PCM_ASYNC)
- kill(share->async_pid, share->async_sig);
- _snd_pcm_update_poll(pcm);
- break;
- default:
- break;
-
- }
+ size_t m = _snd_pcm_share_missing(pcm, slave_xrun);
+ if (m < missing)
+ missing = m;
}
- pthread_mutex_unlock(&slave->mutex);
-}
-
-
-void sigio_handler(int sig ATTRIBUTE_UNUSED)
-{
+ return missing;
}
void *snd_pcm_share_slave_thread(void *data)
{
snd_pcm_share_slave_t *slave = data;
+ snd_pcm_t *spcm = slave->pcm;
+ struct pollfd pfd[2];
int err;
- struct sigaction act;
- err = snd_pcm_async(slave->pcm, SIGIO, 0);
- assert(err == 0);
- act.sa_handler = sigio_handler;
- sigemptyset(&act.sa_mask);
- sigaddset(&act.sa_mask, SIGIO);
- act.sa_flags = 0;
- err = sigaction(SIGIO, &act, NULL);
- assert(err == 0);
- while (1) {
- int sig;
- sigwait(&act.sa_mask, &sig);
- snd_pcm_share_interrupt(slave);
+ pfd[0].fd = slave->poll[0];
+ pfd[0].events = POLLIN;
+ pfd[1].fd = snd_pcm_poll_descriptor(spcm);
+ pfd[1].events = POLLIN | POLLOUT;
+ Pthread_mutex_lock(&slave->mutex);
+ err = pipe(slave->poll);
+ assert(err >= 0);
+ while (slave->open_count > 0) {
+ size_t missing;
+ // printf("begin min_missing\n");
+ missing = _snd_pcm_share_slave_missing(slave);
+ // printf("min_missing=%d\n", missing);
+ if (missing < INT_MAX) {
+ size_t hw_ptr;
+ ssize_t avail_min;
+ hw_ptr = slave->hw_ptr + missing;
+ hw_ptr += spcm->setup.frag_size - 1;
+ if (hw_ptr >= spcm->setup.boundary)
+ hw_ptr -= spcm->setup.boundary;
+ hw_ptr -= hw_ptr % spcm->setup.frag_size;
+ avail_min = hw_ptr - *spcm->appl_ptr;
+ if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
+ avail_min += spcm->setup.buffer_size;
+ if (avail_min < 0)
+ avail_min += spcm->setup.boundary;
+ // printf("avail_min=%d\n", avail_min);
+ if ((size_t)avail_min != spcm->setup.avail_min)
+ snd_pcm_set_avail_min(spcm, avail_min);
+ slave->polling = 1;
+ Pthread_mutex_unlock(&slave->mutex);
+ err = poll(pfd, 2, -1);
+ Pthread_mutex_lock(&slave->mutex);
+ if (pfd[0].revents & POLLIN) {
+ char buf[1];
+ read(pfd[0].fd, buf, 1);
+ }
+ } else {
+ slave->polling = 0;
+ pthread_cond_wait(&slave->poll_cond, &slave->mutex);
+ }
}
+ Pthread_mutex_unlock(&slave->mutex);
return NULL;
}
-/* Warning: take the mutex before to call this */
-static void _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave)
+static void _snd_pcm_share_update(snd_pcm_t *pcm)
{
- struct list_head *i;
- size_t buffer_size, boundary;
- size_t slave_appl_ptr;
- ssize_t frames, safety_frames;
- size_t min_frames, max_frames;
- ssize_t avail;
- int err;
-#if 0
- avail = snd_pcm_avail_update(slave->pcm);
- if (avail <= 0)
- return;
- assert(avail > 0);
-#else
- avail = snd_pcm_mmap_avail(slave->pcm);
-#endif
- boundary = slave->pcm->setup.boundary;
- buffer_size = slave->pcm->setup.buffer_size;
- min_frames = buffer_size;
- max_frames = 0;
- slave_appl_ptr = *slave->pcm->appl_ptr;
- for (i = slave->clients.next; i != &slave->clients; i = i->next) {
- snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
- snd_pcm_t *pcm = share->pcm;
- switch (share->state) {
- case SND_PCM_STATE_RUNNING:
- // share->hw_ptr = *slave->pcm->hw_ptr;
- break;
- case SND_PCM_STATE_DRAINING:
- {
- if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
- continue;
- // share->hw_ptr = *slave->pcm->hw_ptr;
- break;
- }
- default:
- continue;
- }
- frames = share->appl_ptr - slave_appl_ptr;
- if (frames > (ssize_t)buffer_size)
- frames -= pcm->setup.boundary;
- else if (frames < -(ssize_t)pcm->setup.buffer_size)
- frames += pcm->setup.boundary;
- if (frames < 0) {
- continue;
- }
- if ((size_t)frames < min_frames)
- min_frames = frames;
- if ((size_t)frames > max_frames)
- max_frames = frames;
- }
- if (max_frames == 0)
+ snd_pcm_share_t *share = pcm->private;
+ snd_pcm_share_slave_t *slave = share->slave;
+ snd_pcm_t *spcm = slave->pcm;
+ size_t missing;
+ ssize_t avail = snd_pcm_avail_update(spcm);
+ slave->hw_ptr = *slave->pcm->hw_ptr;
+ missing = _snd_pcm_share_missing(pcm, avail == -EPIPE);
+ if (!slave->polling) {
+ pthread_cond_signal(&slave->poll_cond);
return;
- frames = min_frames;
- if (frames > avail)
- frames = avail;
- /* Slave xrun prevention */
- safety_frames = slave->safety_threshold - snd_pcm_mmap_hw_avail(slave->pcm);
- if (safety_frames > 0 &&
- frames < (ssize_t)safety_frames) {
- /* Avoid to pass over the last */
- if (max_frames < (size_t)safety_frames)
- frames = max_frames;
- else
- frames = safety_frames;
}
- if (frames > 0) {
- err = snd_pcm_mmap_forward(slave->pcm, frames);
- assert(err == frames);
+ if (missing < INT_MAX) {
+ size_t hw_ptr;
+ ssize_t avail_min;
+ hw_ptr = slave->hw_ptr + missing;
+ hw_ptr += spcm->setup.frag_size - 1;
+ if (hw_ptr >= spcm->setup.boundary)
+ hw_ptr -= spcm->setup.boundary;
+ hw_ptr -= hw_ptr % spcm->setup.frag_size;
+ avail_min = hw_ptr - *spcm->appl_ptr;
+ if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
+ avail_min += spcm->setup.buffer_size;
+ if (avail_min < 0)
+ avail_min += spcm->setup.boundary;
+ if ((size_t)avail_min < spcm->setup.avail_min)
+ snd_pcm_set_avail_min(spcm, avail_min);
}
}
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
+ if (slave->sformat >= 0) {
+ if ((req_mask & SND_PCM_PARAMS_SFMT) &&
+ info->req.format.sfmt != slave->sformat) {
+ info->req.fail_mask |= SND_PCM_PARAMS_SFMT;
+ info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+ return -EINVAL;
+ }
+ info->req.format.sfmt = slave->sformat;
+ info->req_mask |= SND_PCM_PARAMS_SFMT;
+ }
+ if (slave->srate >= 0) {
+ info->req.format.rate = slave->srate;
+ info->req_mask |= SND_PCM_PARAMS_RATE;
+ }
+
info->req_mask |= SND_PCM_PARAMS_CHANNELS;
info->req.format.channels = slave->channels_count;
err = snd_pcm_params_info(slave->pcm, info);
info->req.format.channels = channels;
info->req_mask = req_mask;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
if (slave->setup_count > 1 ||
(slave->setup_count == 1 && !pcm->valid_setup)) {
snd_pcm_setup_t *s = &slave->pcm->setup;
info->flags |= SND_PCM_INFO_COMPLEX;
}
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
snd_pcm_mmap_info_t *i;
size_t count;
int err = 0;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
if (slave->mmap_count == 0) {
err = snd_pcm_mmap(slave->pcm);
if (err < 0)
err = -ENOMEM;
goto _end;
}
- i->type = SND_PCM_MMAP_USER;
- i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size);
- i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666);
- if (i->u.user.shmid < 0) {
- SYSERR("shmget failed");
- free(i);
- err = -errno;
- goto _end;
- }
- i->addr = shmat(i->u.user.shmid, 0, 0);
- if (i->addr == (void*) -1) {
- SYSERR("shmat failed");
+ err = snd_pcm_alloc_user_mmap(pcm, i);
+ if (err < 0) {
free(i);
- err = -errno;
- goto _end;
+ return err;
}
share->stopped_data = i->addr;
memcpy(i + 1, slave->pcm->mmap_info, count * sizeof(*pcm->mmap_info));
pcm->mmap_info_count = count + 1;
pcm->mmap_info = i;
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return 0;
}
snd_pcm_share_slave_t *slave = share->slave;
snd_pcm_mmap_info_t *i = pcm->mmap_info;
int err = 0;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
slave->mmap_count--;
if (slave->mmap_count == 0) {
err = snd_pcm_munmap(slave->pcm);
if (err < 0)
goto _end;
}
- if (shmdt(i->addr) < 0) {
- SYSERR("shmdt failed");
- err = -errno;
- goto _end;
- }
- if (shmctl(i->u.user.shmid, IPC_RMID, 0) < 0) {
- SYSERR("shmctl IPC_RMID failed");
- err =-errno;
+ err = snd_pcm_free_mmap(pcm, i);
+ if (err < 0)
goto _end;
- }
free(i);
pcm->mmap_info_count = 0;
pcm->mmap_info = 0;
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
}
share->xfer_mode = params->xfer_mode;
share->xrun_mode = params->xrun_mode;
- pthread_mutex_lock(&slave->mutex);
+ share->avail_min = params->avail_min;
+ Pthread_mutex_lock(&slave->mutex);
if (slave->setup_count > 1 ||
(slave->setup_count == 1 && !pcm->valid_setup)) {
snd_pcm_setup_t *s = &slave->pcm->setup;
if (params->format.sfmt != s->format.sfmt) {
- printf("%d %d\n", params->format.sfmt, s->format.sfmt);
ERR("slave is already running with different format");
params->fail_mask |= SND_PCM_PARAMS_SFMT;
}
}
} else {
snd_pcm_params_t sp = *params;
+ snd_pcm_setup_t *ss;
+ if (slave->sformat >= 0 &&
+ params->format.sfmt != slave->sformat) {
+ params->fail_mask = SND_PCM_PARAMS_SFMT;
+ params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
+ ERR("format requested (%d) differs from configuration (%d)", params->format.sfmt, slave->sformat);
+ err = -EINVAL;
+ goto _end;
+ }
+ if (slave->srate >= 0)
+ sp.format.rate = slave->srate;
sp.xfer_mode = SND_PCM_XFER_UNSPECIFIED;
sp.xrun_mode = SND_PCM_XRUN_NONE;
sp.format.channels = slave->channels_count;
- err = snd_pcm_params(slave->pcm, &sp);
+ err = snd_pcm_params_mmap(slave->pcm, &sp);
if (err < 0)
goto _end;
+ ss = &slave->pcm->setup;
+ /* >= 30 ms */
+ slave->safety_threshold = ss->format.rate * 30 / 1000;
+ slave->safety_threshold += ss->frag_size - 1;
+ slave->safety_threshold -= slave->safety_threshold % ss->frag_size;
+ slave->silence_frames = slave->safety_threshold;
}
share->state = SND_PCM_STATE_SETUP;
slave->setup_count++;
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
return err;
setup->xrun_mode = share->xrun_mode;
setup->format.channels = share->channels_count;
+ if (share->avail_min > setup->buffer_size)
+ share->avail_min = setup->buffer_size;
+ setup->avail_min = share->avail_min;
if (share->xfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
ssize_t sd = 0, d = 0;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
status->avail = snd_pcm_mmap_playback_avail(pcm);
if (share->state != SND_PCM_STATE_RUNNING &&
status->state = share->state;
status->trigger_time = share->trigger_time;
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
err = _snd_pcm_share_delay(pcm, delayp);
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
- ssize_t ret = 0;
- pthread_mutex_lock(&slave->mutex);
- ret = snd_pcm_avail_update(slave->pcm);
- if (share->state == SND_PCM_STATE_RUNNING)
- share->hw_ptr = *slave->pcm->hw_ptr;
- if (ret >= 0) {
- ret = snd_pcm_mmap_avail(pcm);
- if ((size_t)ret > pcm->setup.buffer_size) {
- if (share->state == SND_PCM_STATE_RUNNING &&
- pcm->setup.xrun_mode != SND_PCM_XRUN_NONE)
- _snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
- return -EPIPE;
+ ssize_t avail;
+ Pthread_mutex_lock(&slave->mutex);
+ if (share->state == SND_PCM_STATE_RUNNING) {
+ avail = snd_pcm_avail_update(slave->pcm);
+ if (avail < 0) {
+ Pthread_mutex_unlock(&slave->mutex);
+ return avail;
}
+ share->hw_ptr = *slave->pcm->hw_ptr;
}
- pthread_mutex_unlock(&slave->mutex);
- return ret;
+ Pthread_mutex_unlock(&slave->mutex);
+ avail = snd_pcm_mmap_avail(pcm);
+ if ((size_t)avail > pcm->setup.buffer_size)
+ return -EPIPE;
+ return avail;
}
/* Call it with mutex held */
}
}
snd_pcm_mmap_appl_forward(pcm, size);
- if (share->state == SND_PCM_STATE_RUNNING)
- _snd_pcm_share_slave_forward(share->slave);
- _snd_pcm_update_poll(pcm);
+ if (share->state == SND_PCM_STATE_RUNNING) {
+ ssize_t frames = _snd_pcm_share_slave_forward(slave);
+ if (frames > 0) {
+ ssize_t err;
+ err = snd_pcm_mmap_forward(slave->pcm, frames);
+ assert(err == frames);
+ }
+ _snd_pcm_share_update(pcm);
+ }
return size;
}
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
ssize_t ret;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
ret = _snd_pcm_share_mmap_forward(pcm, size);
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return ret;
}
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
if (slave->prepared_count == 0) {
err = snd_pcm_prepare(slave->pcm);
if (err < 0)
share->appl_ptr = 0;
share->state = SND_PCM_STATE_PREPARED;
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
int err = 0;
if (share->state != SND_PCM_STATE_PREPARED)
return -EBADFD;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
share->state = SND_PCM_STATE_RUNNING;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
size_t hw_avail = snd_pcm_mmap_playback_hw_avail(pcm);
pcm->setup.format.sfmt);
xfer += frames;
}
- _snd_pcm_share_mmap_forward(pcm, hw_avail);
+ snd_pcm_mmap_appl_forward(pcm, hw_avail);
+ if (slave->running_count == 0)
+ snd_pcm_mmap_forward(slave->pcm, hw_avail);
}
if (slave->running_count == 0) {
err = snd_pcm_start(slave->pcm);
goto _end;
}
slave->running_count++;
+ _snd_pcm_share_update(pcm);
gettimeofday(&share->trigger_time, 0);
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
n += ret;
}
snd_pcm_mmap_appl_backward(pcm, n);
- _snd_pcm_update_poll(pcm);
+ _snd_pcm_share_update(pcm);
return n;
}
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
ssize_t ret;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
ret = _snd_pcm_share_rewind(pcm, frames);
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return ret;
}
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
pcm->setup.avail_min = frames;
- _snd_pcm_update_poll(pcm);
- pthread_mutex_unlock(&slave->mutex);
+ share->avail_min = frames;
+ _snd_pcm_share_update(pcm);
+ Pthread_mutex_unlock(&slave->mutex);
return 0;
}
err = snd_pcm_delay(slave->pcm, &delay);
if (err >= 0 && delay > 0)
snd_pcm_rewind(slave->pcm, delay);
- _snd_pcm_share_slave_forward(slave);
+ share->drain_silenced = 0;
}
share->state = state;
slave->prepared_count--;
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
switch (share->state) {
case SND_PCM_STATE_OPEN:
err = -EBADFD;
goto _end;
case SND_PCM_STATE_PREPARED:
share->state = SND_PCM_STATE_SETUP;
- break;
+ goto _end;
case SND_PCM_STATE_SETUP:
goto _end;
- case SND_PCM_STATE_DRAINING:
- break;
- case SND_PCM_STATE_XRUN:
- if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+ }
+ if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+ switch (share->state) {
+ case SND_PCM_STATE_XRUN:
share->state = SND_PCM_STATE_SETUP;
goto _end;
+ case SND_PCM_STATE_DRAINING:
+ case SND_PCM_STATE_RUNNING:
+ share->state = SND_PCM_STATE_DRAINING;
+ _snd_pcm_share_update(pcm);
+ Pthread_mutex_unlock(&slave->mutex);
+ if (!(pcm->mode & SND_PCM_NONBLOCK))
+ snd_pcm_wait(pcm, -1);
+ return 0;
}
- /* Fall through */
- case SND_PCM_STATE_RUNNING:
- if (snd_pcm_mmap_avail(pcm) <= 0) {
- share->state = SND_PCM_STATE_SETUP;
- goto _end;
+ } else {
+ switch (share->state) {
+ case SND_PCM_STATE_RUNNING:
+ _snd_pcm_share_stop(pcm, SND_PCM_STATE_DRAINING);
+ _snd_pcm_share_update(pcm);
+ /* Fall through */
+ case SND_PCM_STATE_XRUN:
+ case SND_PCM_STATE_DRAINING:
+ if (snd_pcm_mmap_capture_avail(pcm) <= 0)
+ share->state = SND_PCM_STATE_SETUP;
+ else
+ share->state = SND_PCM_STATE_DRAINING;
+ break;
}
- share->state = SND_PCM_STATE_DRAINING;
- break;
- }
- if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
- _snd_pcm_update_poll(pcm);
- if (!(pcm->mode & SND_PCM_NONBLOCK))
- snd_pcm_wait(pcm, -1);
}
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slave->mutex);
switch (share->state) {
case SND_PCM_STATE_OPEN:
err = -EBADFD;
/* Fall through */
case SND_PCM_STATE_RUNNING:
_snd_pcm_share_stop(pcm, SND_PCM_STATE_SETUP);
- _snd_pcm_update_poll(pcm);
+ _snd_pcm_share_update(pcm);
break;
case SND_PCM_STATE_PREPARED:
case SND_PCM_STATE_XRUN:
share->appl_ptr = share->hw_ptr = 0;
_end:
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
return err;
}
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
- pthread_mutex_lock(&slaves_mutex);
- pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_lock(&slaves_mutex);
+ Pthread_mutex_lock(&slave->mutex);
if (pcm->valid_setup)
slave->setup_count--;
slave->open_count--;
if (slave->open_count == 0) {
- err = pthread_cancel(slave->thread);
- assert(err == 0);
+ err = snd_pcm_close(slave->pcm);
+ pthread_cond_signal(&slave->poll_cond);
+ Pthread_mutex_unlock(&slave->mutex);
err = pthread_join(slave->thread, 0);
assert(err == 0);
- err = snd_pcm_close(slave->pcm);
- pthread_mutex_unlock(&slave->mutex);
pthread_mutex_destroy(&slave->mutex);
+ pthread_cond_destroy(&slave->poll_cond);
list_del(&slave->list);
free(slave);
list_del(&share->list);
} else {
list_del(&share->list);
- pthread_mutex_unlock(&slave->mutex);
+ Pthread_mutex_unlock(&slave->mutex);
}
- pthread_mutex_unlock(&slaves_mutex);
+ Pthread_mutex_unlock(&slaves_mutex);
close(share->client_socket);
close(share->slave_socket);
free(share->slave_channels);
};
int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
+ int sformat, int srate,
size_t schannels_count,
size_t channels_count, int *channels_map,
int stream, int mode)
return -ENOMEM;
}
err = socketpair(AF_LOCAL, SOCK_STREAM, 0, sd);
- if (err >= 0 && stream == SND_PCM_STREAM_PLAYBACK) {
+ if (err < 0) {
+ free(pcm);
+ free(share->slave_channels);
+ free(share);
+ return -errno;
+ }
+
+ if (stream == SND_PCM_STREAM_PLAYBACK) {
int bufsize = 1;
err = setsockopt(sd[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
if (err >= 0) {
}
if (err < 0) {
err = -errno;
+ close(sd[0]);
+ close(sd[1]);
free(pcm);
free(share->slave_channels);
free(share);
return err;
}
- pthread_mutex_lock(&slaves_mutex);
+ Pthread_mutex_lock(&slaves_mutex);
for (i = slaves.next; i != &slaves; i = i->next) {
snd_pcm_share_slave_t *s = list_entry(i, snd_pcm_share_slave_t, list);
if (s->pcm->name && strcmp(s->pcm->name, sname) == 0) {
snd_pcm_t *spcm;
err = snd_pcm_open(&spcm, sname, stream, mode);
if (err < 0) {
- pthread_mutex_unlock(&slaves_mutex);
+ Pthread_mutex_unlock(&slaves_mutex);
close(sd[0]);
close(sd[1]);
free(pcm);
}
slave = calloc(1, sizeof(*slave));
if (!slave) {
- pthread_mutex_unlock(&slaves_mutex);
+ Pthread_mutex_unlock(&slaves_mutex);
snd_pcm_close(spcm);
close(sd[0]);
close(sd[1]);
INIT_LIST_HEAD(&slave->clients);
slave->pcm = spcm;
slave->channels_count = schannels_count;
+ slave->sformat = sformat;
+ slave->srate = srate;
pthread_mutex_init(&slave->mutex, NULL);
+ pthread_cond_init(&slave->poll_cond, NULL);
list_add_tail(&slave->list, &slaves);
+ Pthread_mutex_lock(&slave->mutex);
err = pthread_create(&slave->thread, NULL, snd_pcm_share_slave_thread, slave);
assert(err == 0);
+ Pthread_mutex_unlock(&slaves_mutex);
+ } else {
+ Pthread_mutex_lock(&slave->mutex);
+ Pthread_mutex_unlock(&slaves_mutex);
+ for (i = slave->clients.next; i != &slave->clients; i = i->next) {
+ snd_pcm_share_t *sh = list_entry(i, snd_pcm_share_t, list);
+ unsigned int k;
+ for (k = 0; k < sh->channels_count; ++k) {
+ if (slave_map[sh->slave_channels[k]]) {
+ ERR("Slave channel %d is already in use", sh->slave_channels[k]);
+ Pthread_mutex_unlock(&slave->mutex);
+ close(sd[0]);
+ close(sd[1]);
+ free(pcm);
+ free(share->slave_channels);
+ free(share);
+ return -EBUSY;
+ }
+ }
+ }
}
- pthread_mutex_lock(&slave->mutex);
- pthread_mutex_unlock(&slaves_mutex);
- slave->open_count++;
- list_add_tail(&share->list, &slave->clients);
- pthread_mutex_unlock(&slave->mutex);
share->slave = slave;
share->pcm = pcm;
pcm->poll_fd = share->client_socket;
pcm->hw_ptr = &share->hw_ptr;
pcm->appl_ptr = &share->appl_ptr;
+
+ slave->open_count++;
+ list_add_tail(&share->list, &slave->clients);
+
+ Pthread_mutex_unlock(&slave->mutex);
+
*pcmp = pcm;
return 0;
}
size_t channels_count = 0;
long schannels_count = -1;
size_t schannel_max = 0;
+ int sformat = -1;
+ long srate = -1;
+
snd_config_foreach(i, conf) {
snd_config_t *n = snd_config_entry(i);
if (strcmp(n->id, "comment") == 0)
}
continue;
}
+ if (strcmp(n->id, "sformat") == 0) {
+ char *f;
+ err = snd_config_string_get(n, &f);
+ if (err < 0) {
+ ERR("Invalid type for sformat");
+ return -EINVAL;
+ }
+ sformat = snd_pcm_format_value(f);
+ if (sformat < 0) {
+ ERR("Unknown format %s", f);
+ return -EINVAL;
+ }
+ continue;
+ }
if (strcmp(n->id, "schannels") == 0) {
err = snd_config_integer_get(n, &schannels_count);
if (err < 0) {
}
continue;
}
+ if (strcmp(n->id, "srate") == 0) {
+ err = snd_config_integer_get(n, &srate);
+ if (err < 0) {
+ ERR("Invalid type for srate");
+ return -EINVAL;
+ }
+ continue;
+ }
if (strcmp(n->id, "binding") == 0) {
if (snd_config_type(n) != SND_CONFIG_TYPE_COMPOUND) {
ERR("Invalid type for binding");
}
if (schannels_count <= 0)
schannels_count = schannel_max + 1;
- err = snd_pcm_share_open(pcmp, name, sname, schannels_count,
- channels_count, channels_map, stream, mode);
+ err = snd_pcm_share_open(pcmp, name, sname, sformat, srate,
+ schannels_count,
+ channels_count, channels_map, stream, mode);
_free:
free(channels_map);
return err;