size_t channels_count;
size_t open_count;
size_t setup_count;
+ size_t mmap_count;
size_t prepared_count;
size_t running_count;
size_t safety_threshold;
pid_t async_pid;
struct timeval trigger_time;
size_t draining_silence;
- snd_pcm_mmap_control_t mmap_control;
- snd_pcm_mmap_status_t mmap_status;
+ int state;
+ size_t hw_ptr;
+ size_t appl_ptr;
int ready;
int client_socket;
int slave_socket;
snd_pcm_share_t *share = list_entry(i, snd_pcm_share_t, list);
snd_pcm_t *pcm = share->pcm;
int ready;
- switch (share->mmap_status.state) {
+ switch (share->state) {
case SND_PCM_STATE_DRAINING:
if (pcm->stream == SND_PCM_STREAM_CAPTURE)
ready = 1;
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
- share->mmap_status.state = state;
+ share->state = state;
gettimeofday(&share->trigger_time, 0);
slave->prepared_count--;
slave->running_count--;
- if (slave->running_count == 0) {
- int err = snd_pcm_drop(slave->pcm);
- assert(err >= 0);
- }
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
+ snd_pcm_avail_update(slave->pcm);
snd_pcm_areas_copy(pcm->running_areas, 0,
pcm->stopped_areas, 0,
pcm->setup.format.channels, pcm->setup.buffer_size,
pcm->setup.format.sfmt);
}
- share->mmap_status.hw_ptr = slave->pcm->mmap_status->hw_ptr;
- pcm->mmap_status = &share->mmap_status;
+ if (slave->running_count == 0) {
+ int err = snd_pcm_drop(slave->pcm);
+ assert(err >= 0);
+ }
}
/* Warning: take the mutex before to call this */
buffer_size = slave->pcm->setup.buffer_size;
min_frames = buffer_size;
max_frames = 0;
- slave_appl_ptr = slave->pcm->mmap_control->appl_ptr;
+ 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->mmap_status.state) {
+ switch (share->state) {
case SND_PCM_STATE_RUNNING:
break;
case SND_PCM_STATE_DRAINING:
default:
continue;
}
- frames = share->mmap_control.appl_ptr - slave_appl_ptr;
+ 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)
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
- if (share->mmap_status.state == SND_PCM_STATE_RUNNING) {
+ if (share->state == SND_PCM_STATE_RUNNING) {
if (pcm->mode & SND_PCM_NONBLOCK)
snd_pcm_drop(pcm);
else
pthread_kill(slave->thread, SIGTERM);
err = snd_pcm_close(slave->pcm);
list_del(&slave->list);
+ pthread_mutex_unlock(&slave->mutex);
pthread_mutex_destroy(&slave->mutex);
free(slave);
+ list_del(&share->list);
+ } else {
+ list_del(&share->list);
+ pthread_mutex_unlock(&slave->mutex);
}
- list_del(&share->list);
- pthread_mutex_unlock(&slave->mutex);
close(share->client_socket);
close(share->slave_socket);
free(share->slave_channels);
return err;
}
+static int snd_pcm_share_mmap(snd_pcm_t *pcm)
+{
+ snd_pcm_share_t *share = pcm->private;
+ snd_pcm_share_slave_t *slave = share->slave;
+ snd_pcm_mmap_info_t *i;
+ int err;
+ pthread_mutex_lock(&slave->mutex);
+ if (slave->mmap_count == 0) {
+ err = snd_pcm_mmap(slave->pcm);
+ if (err < 0) {
+ pthread_mutex_unlock(&slave->mutex);
+ return err;
+ }
+ if (slave->pcm->stream == SND_PCM_STREAM_PLAYBACK)
+ snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->setup.format.channels, slave->pcm->setup.buffer_size, slave->pcm->setup.format.sfmt);
+ slave->mmap_count++;
+ }
+ pthread_mutex_unlock(&slave->mutex);
+ pcm->mmap_info_count = slave->pcm->mmap_info_count + 1;
+ pcm->mmap_info = malloc(pcm->mmap_info_count * sizeof(*pcm->mmap_info));
+ if (!pcm->mmap_info)
+ return -ENOMEM;
+ memcpy(pcm->mmap_info, slave->pcm->mmap_info, slave->pcm->mmap_info_count * sizeof(*pcm->mmap_info));
+ i = &pcm->mmap_info[slave->pcm->mmap_info_count];
+ i->type = SND_PCM_MMAP_USER;
+ i->size = snd_pcm_frames_to_bytes(pcm, pcm->setup.buffer_size);
+ share->stopped_data = malloc(i->size);
+ if (share->stopped_data == 0) {
+ free(pcm->mmap_info);
+ pcm->mmap_info = 0;
+ return -ENOMEM;
+ }
+ i->addr = share->stopped_data;
+ return 0;
+}
+
+static int snd_pcm_share_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
+{
+ snd_pcm_share_t *share = pcm->private;
+ snd_pcm_share_slave_t *slave = share->slave;
+ int err;
+ pthread_mutex_lock(&slave->mutex);
+ slave->mmap_count--;
+ if (slave->mmap_count == 0) {
+ err = snd_pcm_munmap(slave->pcm);
+ if (err < 0) {
+ pthread_mutex_unlock(&slave->mutex);
+ return err;
+ }
+ }
+ pthread_mutex_unlock(&slave->mutex);
+ free(pcm->mmap_info);
+ pcm->mmap_info_count = 0;
+ pcm->mmap_info = 0;
+ free(share->stopped_data);
+ return 0;
+}
+
static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
{
snd_pcm_share_t *share = pcm->private;
if (channels != share->channels_count) {
params->fail_mask = SND_PCM_PARAMS_CHANNELS;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
- ERR("channels requested (%d) differs from configuration (%d)", channels, share->channels_count);
+ ERR("channels requested (%d) differs from configuration (%ld)", channels, (long)share->channels_count);
return -EINVAL;
}
share->xfer_mode = params->xfer_mode;
}
} else {
snd_pcm_params_t sp = *params;
- if (slave->pcm->mmap_data) {
- err = snd_pcm_munmap_data(slave->pcm);
- if (err < 0)
- goto _end;
- }
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);
- snd_pcm_mmap_data(slave->pcm, NULL);
if (err < 0)
goto _end;
- if (slave->pcm->stream == SND_PCM_STREAM_PLAYBACK)
- snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->setup.format.channels, slave->pcm->setup.buffer_size, slave->pcm->setup.format.sfmt);
}
- share->mmap_status.state = SND_PCM_STATE_SETUP;
+ share->state = SND_PCM_STATE_SETUP;
slave->setup_count++;
_end:
pthread_mutex_unlock(&slave->mutex);
pthread_mutex_lock(&slave->mutex);
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
status->avail = snd_pcm_mmap_playback_avail(pcm);
- if (share->mmap_status.state != SND_PCM_STATE_RUNNING &&
- share->mmap_status.state != SND_PCM_STATE_DRAINING)
+ if (share->state != SND_PCM_STATE_RUNNING &&
+ share->state != SND_PCM_STATE_DRAINING)
goto _notrunning;
d = pcm->setup.buffer_size - status->avail;
} else {
status->avail = snd_pcm_mmap_capture_avail(pcm);
- if (share->mmap_status.state != SND_PCM_STATE_RUNNING)
+ if (share->state != SND_PCM_STATE_RUNNING)
goto _notrunning;
d = status->avail;
}
goto _end;
_notrunning:
status->delay = sd + d;
- status->state = share->mmap_status.state;
+ status->state = share->state;
status->trigger_time = share->trigger_time;
_end:
pthread_mutex_unlock(&slave->mutex);
static int snd_pcm_share_state(snd_pcm_t *pcm)
{
snd_pcm_share_t *share = pcm->private;
- return share->mmap_status.state;
+ return share->state;
}
static int snd_pcm_share_delay(snd_pcm_t *pcm, ssize_t *delayp)
int err = 0;
ssize_t sd;
pthread_mutex_lock(&slave->mutex);
- switch (share->mmap_status.state) {
+ switch (share->state) {
case SND_PCM_STATE_XRUN:
err = -EPIPE;
goto _end;
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 == -EPIPE) {
struct list_head *i;
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;
- if (share->mmap_status.state == SND_PCM_STATE_RUNNING &&
+ if (share->state == SND_PCM_STATE_RUNNING &&
pcm->setup.xrun_mode != SND_PCM_XRUN_NONE)
snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
}
pthread_mutex_unlock(&slave->mutex);
if (ret >= 0) {
ret = snd_pcm_mmap_avail(pcm);
- if ((size_t)ret > pcm->setup.buffer_size)
+ 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;
+ }
}
return ret;
}
-static ssize_t snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size)
+/* Call it with mutex held */
+static ssize_t _snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
ssize_t ret = 0;
ssize_t frames;
- pthread_mutex_lock(&slave->mutex);
if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
- share->mmap_status.state == SND_PCM_STATE_RUNNING) {
- frames = slave->pcm->mmap_control->appl_ptr - share->mmap_control.appl_ptr;
+ share->state == SND_PCM_STATE_RUNNING) {
+ frames = *slave->pcm->appl_ptr - share->appl_ptr;
if (frames > (ssize_t)pcm->setup.buffer_size)
frames -= pcm->setup.boundary;
else if (frames < -(ssize_t)pcm->setup.buffer_size)
if (frames > 0) {
/* Latecomer PCM */
ret = snd_pcm_rewind(slave->pcm, frames);
- if (ret < 0) {
- pthread_mutex_unlock(&slave->mutex);
+ if (ret < 0)
return ret;
- }
size += ret;
}
}
snd_pcm_mmap_appl_forward(pcm, size);
snd_pcm_share_slave_forward(share->slave);
- pthread_mutex_unlock(&slave->mutex);
return size;
}
+static ssize_t snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size)
+{
+ snd_pcm_share_t *share = pcm->private;
+ snd_pcm_share_slave_t *slave = share->slave;
+ ssize_t ret;
+ pthread_mutex_lock(&slave->mutex);
+ ret = _snd_pcm_share_mmap_forward(pcm, size);
+ pthread_mutex_unlock(&slave->mutex);
+ return ret;
+}
+
static int snd_pcm_share_prepare(snd_pcm_t *pcm)
{
snd_pcm_share_t *share = pcm->private;
goto _end;
}
slave->prepared_count++;
- share->mmap_status.hw_ptr = 0;
- share->mmap_control.appl_ptr = 0;
- share->mmap_status.state = SND_PCM_STATE_PREPARED;
+ share->hw_ptr = 0;
+ share->appl_ptr = 0;
+ share->state = SND_PCM_STATE_PREPARED;
_end:
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;
- if (share->mmap_status.state != SND_PCM_STATE_PREPARED)
+ if (share->state != SND_PCM_STATE_PREPARED)
return -EBADFD;
pthread_mutex_lock(&slave->mutex);
- share->mmap_status.state = SND_PCM_STATE_RUNNING;
+ share->state = SND_PCM_STATE_RUNNING;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
size_t hw_avail = snd_pcm_mmap_playback_hw_avail(pcm);
if (hw_avail == 0) {
if (err < 0)
goto _end;
}
- assert(share->mmap_status.hw_ptr == 0);
- /* Use the same mmap_status of slave */
- pcm->mmap_status = slave->pcm->mmap_status;
- share->mmap_control.appl_ptr = slave->pcm->mmap_control->appl_ptr;
+ assert(share->hw_ptr == 0);
+ share->hw_ptr = *slave->pcm->hw_ptr;
+ share->appl_ptr = *slave->pcm->appl_ptr;
snd_pcm_areas_copy(pcm->stopped_areas, 0,
pcm->running_areas, snd_pcm_mmap_offset(pcm),
pcm->setup.format.channels, hw_avail,
pcm->setup.format.sfmt);
- snd_pcm_mmap_forward(pcm, pcm->setup.buffer_size);
+ _snd_pcm_share_mmap_forward(pcm, pcm->setup.buffer_size);
}
if (slave->running_count == 0) {
err = snd_pcm_start(slave->pcm);
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
pthread_mutex_lock(&slave->mutex);
- switch (share->mmap_status.state) {
+ switch (share->state) {
case SND_PCM_STATE_OPEN:
err = -EBADFD;
goto _end;
goto _end;
case SND_PCM_STATE_DRAINING:
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
- share->mmap_status.state = SND_PCM_STATE_SETUP;
+ share->state = SND_PCM_STATE_SETUP;
break;
}
/* Fall through */
break;
case SND_PCM_STATE_PREPARED:
case SND_PCM_STATE_XRUN:
- share->mmap_status.state = SND_PCM_STATE_SETUP;
+ share->state = SND_PCM_STATE_SETUP;
break;
}
pcm->setup.buffer_size, pcm->setup.format.sfmt);
snd_pcm_mmap_forward(pcm, pcm->setup.buffer_size);
}
- share->mmap_control.appl_ptr = share->mmap_status.hw_ptr = 0;
+ share->appl_ptr = share->hw_ptr = 0;
_end:
pthread_mutex_unlock(&slave->mutex);
return err;
snd_pcm_share_slave_t *slave = share->slave;
int err = 0;
pthread_mutex_lock(&slave->mutex);
- switch (share->mmap_status.state) {
+ switch (share->state) {
case SND_PCM_STATE_OPEN:
err = -EBADFD;
goto _end;
case SND_PCM_STATE_PREPARED:
- share->mmap_status.state = SND_PCM_STATE_SETUP;
+ share->state = SND_PCM_STATE_SETUP;
break;
case SND_PCM_STATE_SETUP:
case SND_PCM_STATE_DRAINING:
goto _end;
case SND_PCM_STATE_XRUN:
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
- share->mmap_status.state = SND_PCM_STATE_SETUP;
+ share->state = SND_PCM_STATE_SETUP;
break;
}
/* Fall through */
case SND_PCM_STATE_RUNNING:
if (snd_pcm_mmap_avail(pcm) <= 0) {
- share->mmap_status.state = SND_PCM_STATE_SETUP;
+ share->state = SND_PCM_STATE_SETUP;
break;
}
share->draining_silence = 0;
- share->mmap_status.state = SND_PCM_STATE_DRAINING;
+ share->state = SND_PCM_STATE_DRAINING;
break;
}
_end:
setup->channel = c;
err = snd_pcm_channel_setup(slave->pcm, setup);
setup->channel = channel;
- return err;
+ if (err < 0)
+ return err;
+ if (!pcm->mmap_info)
+ return 0;
+ switch (pcm->setup.mmap_shape) {
+ case SND_PCM_MMAP_INTERLEAVED:
+ case SND_PCM_MMAP_COMPLEX:
+ setup->stopped_area.addr = share->stopped_data;
+ setup->stopped_area.first = channel * pcm->bits_per_sample;
+ setup->stopped_area.step = pcm->bits_per_frame;
+ break;
+ case SND_PCM_MMAP_NONINTERLEAVED:
+ setup->stopped_area.addr = share->stopped_data + c * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
+ setup->stopped_area.first = 0;
+ setup->stopped_area.step = pcm->bits_per_sample;
+ break;
+ default:
+ assert(0);
+ }
+ return 0;
}
static ssize_t snd_pcm_share_rewind(snd_pcm_t *pcm, size_t frames)
snd_pcm_share_slave_t *slave = share->slave;
int ret = -EBADFD;
ssize_t n;
- switch (share->mmap_status.state) {
+ switch (share->state) {
case SND_PCM_STATE_RUNNING:
break;
case SND_PCM_STATE_PREPARED:
n = frames;
frames -= n;
}
- if (share->mmap_status.state == SND_PCM_STATE_RUNNING &&
+ if (share->state == SND_PCM_STATE_RUNNING &&
frames > 0) {
pthread_mutex_lock(&slave->mutex);
ret = snd_pcm_rewind(slave->pcm, frames);
return ret;
}
-static int snd_pcm_share_mmap_status(snd_pcm_t *pcm)
-{
- snd_pcm_share_t *share = pcm->private;
- pcm->mmap_status = &share->mmap_status;
- return 0;
-}
-
-static int snd_pcm_share_mmap_control(snd_pcm_t *pcm)
-{
- snd_pcm_share_t *share = pcm->private;
- pcm->mmap_control = &share->mmap_control;
- return 0;
-}
-
-static int snd_pcm_share_mmap_data(snd_pcm_t *pcm)
-{
- snd_pcm_share_t *share = pcm->private;
- snd_pcm_share_slave_t *slave = share->slave;
- pcm->mmap_data = slave->pcm->mmap_data;
- return 0;
-}
-
-static int snd_pcm_share_munmap_status(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-
-static int snd_pcm_share_munmap_control(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-
-static int snd_pcm_share_munmap_data(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
-{
- return 0;
-}
-
static int snd_pcm_share_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_dump(slave->pcm, fp);
}
-struct snd_pcm_ops snd_pcm_share_ops = {
+snd_pcm_ops_t snd_pcm_share_ops = {
close: snd_pcm_share_close,
info: snd_pcm_share_info,
params_info: snd_pcm_share_params_info,
dump: snd_pcm_share_dump,
nonblock: snd_pcm_share_nonblock,
async: snd_pcm_share_async,
- mmap_status: snd_pcm_share_mmap_status,
- mmap_control: snd_pcm_share_mmap_control,
- mmap_data: snd_pcm_share_mmap_data,
- munmap_status: snd_pcm_share_munmap_status,
- munmap_control: snd_pcm_share_munmap_control,
- munmap_data: snd_pcm_share_munmap_data,
+ mmap: snd_pcm_share_mmap,
+ munmap: snd_pcm_share_munmap,
};
-struct snd_pcm_fast_ops snd_pcm_share_fast_ops = {
+snd_pcm_fast_ops_t snd_pcm_share_fast_ops = {
status: snd_pcm_share_status,
state: snd_pcm_share_state,
delay: snd_pcm_share_delay,
readi: snd_pcm_mmap_readi,
readn: snd_pcm_mmap_readn,
rewind: snd_pcm_share_rewind,
- poll_descriptor: snd_pcm_share_poll_descriptor,
channels_mask: snd_pcm_share_channels_mask,
avail_update: snd_pcm_share_avail_update,
mmap_forward: snd_pcm_share_mmap_forward,
};
-int snd_pcm_share_open(snd_pcm_t **handlep, char *name, char *sname,
+int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
size_t schannels_count,
size_t channels_count, int *channels_map,
int stream, int mode)
{
- snd_pcm_t *handle;
+ snd_pcm_t *pcm;
snd_pcm_share_t *share;
int err;
struct list_head *i;
snd_pcm_share_slave_t *slave = NULL;
int sd[2];
- assert(handlep);
+ assert(pcmp);
assert(channels_count > 0 && sname && channels_map);
for (k = 0; k < channels_count; ++k) {
}
memcpy(share->slave_channels, channels_map, channels_count * sizeof(*share->slave_channels));
- handle = calloc(1, sizeof(snd_pcm_t));
- if (!handle) {
+ pcm = calloc(1, sizeof(snd_pcm_t));
+ if (!pcm) {
free(share->slave_channels);
free(share);
return -ENOMEM;
}
if (err < 0) {
err = -errno;
- free(handle);
+ free(pcm);
free(share->slave_channels);
free(share);
return err;
pthread_mutex_unlock(&slaves_mutex);
close(sd[0]);
close(sd[1]);
- free(handle);
+ free(pcm);
free(share->slave_channels);
free(share);
return err;
snd_pcm_close(spcm);
close(sd[0]);
close(sd[1]);
- free(handle);
+ free(pcm);
free(share->slave_channels);
free(share);
return err;
pthread_mutex_unlock(&slave->mutex);
share->slave = slave;
- share->pcm = handle;
+ share->pcm = pcm;
share->client_socket = sd[0];
share->slave_socket = sd[1];
share->async_sig = SIGIO;
share->async_pid = getpid();
if (name)
- handle->name = strdup(name);
- handle->type = SND_PCM_TYPE_SHARE;
- handle->stream = stream;
- handle->mode = mode;
- handle->ops = &snd_pcm_share_ops;
- handle->op_arg = handle;
- handle->fast_ops = &snd_pcm_share_fast_ops;
- handle->fast_op_arg = handle;
- handle->private = share;
- err = snd_pcm_init(handle);
- if (err < 0) {
- snd_pcm_close(handle);
- return err;
- }
- *handlep = handle;
+ pcm->name = strdup(name);
+ pcm->type = SND_PCM_TYPE_SHARE;
+ pcm->stream = stream;
+ pcm->mode = mode;
+ pcm->mmap_auto = 1;
+ pcm->ops = &snd_pcm_share_ops;
+ pcm->op_arg = pcm;
+ pcm->fast_ops = &snd_pcm_share_fast_ops;
+ pcm->fast_op_arg = pcm;
+ pcm->private = share;
+ pcm->poll_fd = share->client_socket;
+ pcm->hw_ptr = &share->hw_ptr;
+ pcm->appl_ptr = &share->appl_ptr;
+ *pcmp = pcm;
return 0;
}
return -EINVAL;
}
if (!sname) {
- ERR("Missing sname");
+ ERR("sname is not defined");
return -EINVAL;
}
if (!binding) {
- ERR("Missing binding");
+ ERR("binding is not defined");
return -EINVAL;
}
snd_config_foreach(i, binding) {