Merged pcmfinal branch.

This commit is contained in:
Jaroslav Kysela 2000-11-20 20:10:46 +00:00
parent 3cc2b957fb
commit 41bb7068f2
57 changed files with 5189 additions and 3088 deletions

File diff suppressed because it is too large Load diff

View file

@ -69,8 +69,6 @@ typedef struct {
int getput_idx;
adpcm_f func;
int sformat;
int cformat;
int cxfer_mode, cmmap_shape;
adpcm_state_t *states;
} snd_pcm_adpcm_t;
@ -331,102 +329,82 @@ static int snd_pcm_adpcm_close(snd_pcm_t *pcm)
return 0;
}
static int snd_pcm_adpcm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_adpcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_adpcm_t *adpcm = pcm->private;
unsigned int req_mask = info->req_mask;
unsigned int sfmt = info->req.format.sfmt;
unsigned int format_mask, access_mask;
int err;
if (req_mask & SND_PCM_PARAMS_SFMT) {
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ?
!snd_pcm_format_linear(sfmt) :
sfmt != SND_PCM_SFMT_IMA_ADPCM) {
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
access_mask = info->access_mask;
if (access_mask == 0)
return -EINVAL;
if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM)
info->format_mask &= SND_PCM_FMTBIT_LINEAR;
else
info->format_mask &= SND_PCM_FMTBIT_IMA_ADPCM;
format_mask = info->format_mask;
if (format_mask == 0)
return -EINVAL;
info->format_mask = 1U << adpcm->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(adpcm->plug.slave, info);
if (info->format_mask)
info->format_mask = format_mask;
if (info->access_mask) {
adpcm->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
info->req_mask |= SND_PCM_PARAMS_SFMT;
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
SND_PCM_PARAMS_XFER_MODE);
info->req.format.sfmt = adpcm->sformat;
err = snd_pcm_params_info(adpcm->plug.slave, info);
info->req_mask = req_mask;
info->req.format.sfmt = sfmt;
if (err < 0)
return err;
if (req_mask & SND_PCM_PARAMS_SFMT)
info->formats = 1 << sfmt;
else
info->formats = adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ?
SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_IMA_ADPCM;
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info);
return 0;
}
static int snd_pcm_adpcm_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_adpcm_t *adpcm = pcm->private;
snd_pcm_t *slave = adpcm->plug.slave;
unsigned int format, access;
int err;
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM ?
!snd_pcm_format_linear(params->format.sfmt) :
params->format.sfmt != SND_PCM_SFMT_IMA_ADPCM) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
adpcm->cformat = params->format.sfmt;
adpcm->cxfer_mode = params->xfer_mode;
adpcm->cmmap_shape = params->mmap_shape;
params->format.sfmt = adpcm->sformat;
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
err = snd_pcm_params_mmap(slave, params);
params->format.sfmt = adpcm->cformat;
params->xfer_mode = adpcm->cxfer_mode;
params->mmap_shape = adpcm->cmmap_shape;
return err;
}
static int snd_pcm_adpcm_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
{
snd_pcm_adpcm_t *adpcm = pcm->private;
int err = snd_pcm_setup(adpcm->plug.slave, setup);
format = params->format;
access = params->access;
params->format = adpcm->sformat;
if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0)
return err;
assert(adpcm->sformat == setup->format.sfmt);
if (adpcm->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
setup->xfer_mode = adpcm->cxfer_mode;
if (adpcm->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
else
setup->mmap_shape = adpcm->cmmap_shape;
setup->format.sfmt = adpcm->cformat;
setup->mmap_bytes = 0;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) {
adpcm->getput_idx = get_index(adpcm->cformat, SND_PCM_SFMT_S16);
if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
adpcm->getput_idx = get_index(format, SND_PCM_FORMAT_S16);
adpcm->func = adpcm_encode;
} else {
adpcm->getput_idx = put_index(SND_PCM_SFMT_S16, adpcm->sformat);
adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, adpcm->sformat);
adpcm->func = adpcm_decode;
}
} else {
if (adpcm->sformat == SND_PCM_SFMT_IMA_ADPCM) {
adpcm->getput_idx = put_index(SND_PCM_SFMT_S16, adpcm->cformat);
if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, format);
adpcm->func = adpcm_decode;
} else {
adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_SFMT_S16);
adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_FORMAT_S16);
adpcm->func = adpcm_encode;
}
}
if (adpcm->states)
free(adpcm->states);
adpcm->states = malloc(setup->format.channels * sizeof(*adpcm->states));
adpcm->states = malloc(params->channels * sizeof(*adpcm->states));
return 0;
}
@ -434,7 +412,7 @@ static int snd_pcm_adpcm_init(snd_pcm_t *pcm)
{
snd_pcm_adpcm_t *adpcm = pcm->private;
unsigned int k;
for (k = 0; k < pcm->setup.format.channels; ++k) {
for (k = 0; k < pcm->channels; ++k) {
adpcm->states[k].pred_val = 0;
adpcm->states[k].step_idx = 0;
}
@ -458,7 +436,7 @@ static ssize_t snd_pcm_adpcm_write_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
adpcm->func(areas, offset,
snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
frames, pcm->setup.format.channels,
frames, pcm->channels,
adpcm->getput_idx, adpcm->states);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
@ -493,7 +471,7 @@ static ssize_t snd_pcm_adpcm_read_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
adpcm->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
areas, offset,
frames, pcm->setup.format.channels,
frames, pcm->channels,
adpcm->getput_idx, adpcm->states);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
@ -516,7 +494,7 @@ static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_adpcm_t *adpcm = pcm->private;
fprintf(fp, "Ima-ADPCM conversion PCM (%s)\n",
snd_pcm_format_name(adpcm->sformat));
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -527,12 +505,12 @@ static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_adpcm_ops = {
close: snd_pcm_adpcm_close,
info: snd_pcm_plugin_info,
params_info: snd_pcm_adpcm_params_info,
params: snd_pcm_adpcm_params,
setup: snd_pcm_adpcm_setup,
hw_info: snd_pcm_adpcm_hw_info,
hw_params: snd_pcm_adpcm_hw_params,
sw_params: snd_pcm_plugin_sw_params,
dig_info: snd_pcm_plugin_dig_info,
dig_params: snd_pcm_plugin_dig_params,
channel_info: snd_pcm_plugin_channel_info,
channel_params: snd_pcm_plugin_channel_params,
channel_setup: snd_pcm_plugin_channel_setup,
dump: snd_pcm_adpcm_dump,
nonblock: snd_pcm_plugin_nonblock,
async: snd_pcm_plugin_async,
@ -546,7 +524,7 @@ int snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *sla
snd_pcm_adpcm_t *adpcm;
assert(pcmp && slave);
if (snd_pcm_format_linear(sformat) != 1 &&
sformat != SND_PCM_SFMT_IMA_ADPCM)
sformat != SND_PCM_FORMAT_IMA_ADPCM)
return -EINVAL;
adpcm = calloc(1, sizeof(snd_pcm_adpcm_t));
if (!adpcm) {
@ -614,7 +592,7 @@ int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name,
if (sformat < 0)
return -EINVAL;
if (snd_pcm_format_linear(sformat) != 1 &&
sformat != SND_PCM_SFMT_IMA_ADPCM)
sformat != SND_PCM_FORMAT_IMA_ADPCM)
return -EINVAL;
continue;
}

View file

@ -35,8 +35,6 @@ typedef struct {
int getput_idx;
alaw_f func;
int sformat;
int cformat;
int cxfer_mode, cmmap_shape;
} snd_pcm_alaw_t;
static inline int val_seg(int val)
@ -213,96 +211,76 @@ static void alaw_encode(snd_pcm_channel_area_t *src_areas,
}
}
static int snd_pcm_alaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_alaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_alaw_t *alaw = pcm->private;
unsigned int req_mask = info->req_mask;
unsigned int sfmt = info->req.format.sfmt;
unsigned int format_mask, access_mask;
int err;
if (req_mask & SND_PCM_PARAMS_SFMT) {
if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
!snd_pcm_format_linear(sfmt) :
sfmt != SND_PCM_SFMT_A_LAW) {
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
access_mask = info->access_mask;
if (access_mask == 0)
return -EINVAL;
if (alaw->sformat == SND_PCM_FORMAT_MU_LAW)
info->format_mask &= SND_PCM_FMTBIT_LINEAR;
else
info->format_mask &= SND_PCM_FMTBIT_MU_LAW;
format_mask = info->format_mask;
if (format_mask == 0)
return -EINVAL;
info->format_mask = 1U << alaw->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(alaw->plug.slave, info);
if (info->format_mask)
info->format_mask = format_mask;
if (info->access_mask) {
alaw->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
info->req_mask |= SND_PCM_PARAMS_SFMT;
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
SND_PCM_PARAMS_XFER_MODE);
info->req.format.sfmt = alaw->sformat;
err = snd_pcm_params_info(alaw->plug.slave, info);
info->req_mask = req_mask;
info->req.format.sfmt = sfmt;
if (err < 0)
return err;
if (req_mask & SND_PCM_PARAMS_SFMT)
info->formats = 1 << sfmt;
else
info->formats = alaw->sformat == SND_PCM_SFMT_A_LAW ?
SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_A_LAW;
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info);
return 0;
}
static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_alaw_t *alaw = pcm->private;
snd_pcm_t *slave = alaw->plug.slave;
unsigned int format, access;
int err;
if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
!snd_pcm_format_linear(params->format.sfmt) :
params->format.sfmt != SND_PCM_SFMT_A_LAW) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
alaw->cformat = params->format.sfmt;
alaw->cxfer_mode = params->xfer_mode;
alaw->cmmap_shape = params->mmap_shape;
params->format.sfmt = alaw->sformat;
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
err = snd_pcm_params_mmap(slave, params);
params->format.sfmt = alaw->cformat;
params->xfer_mode = alaw->cxfer_mode;
params->mmap_shape = alaw->cmmap_shape;
return err;
}
static int snd_pcm_alaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
{
snd_pcm_alaw_t *alaw = pcm->private;
int err = snd_pcm_setup(alaw->plug.slave, setup);
format = params->format;
access = params->access;
params->format = alaw->sformat;
if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0)
return err;
assert(alaw->sformat == setup->format.sfmt);
if (alaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
setup->xfer_mode = alaw->cxfer_mode;
if (alaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
else
setup->mmap_shape = alaw->cmmap_shape;
setup->format.sfmt = alaw->cformat;
setup->mmap_bytes = 0;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
alaw->getput_idx = get_index(alaw->cformat, SND_PCM_SFMT_S16);
if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) {
alaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16);
alaw->func = alaw_encode;
} else {
alaw->getput_idx = put_index(SND_PCM_SFMT_S16, alaw->sformat);
alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, alaw->sformat);
alaw->func = alaw_decode;
}
} else {
if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
alaw->getput_idx = put_index(SND_PCM_SFMT_S16, alaw->cformat);
if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) {
alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format);
alaw->func = alaw_decode;
} else {
alaw->getput_idx = get_index(alaw->sformat, SND_PCM_SFMT_S16);
alaw->getput_idx = get_index(alaw->sformat, SND_PCM_FORMAT_S16);
alaw->func = alaw_encode;
}
}
@ -326,7 +304,7 @@ static ssize_t snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
alaw->func(areas, offset,
snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
frames, pcm->setup.format.channels,
frames, pcm->channels,
alaw->getput_idx);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
@ -361,7 +339,7 @@ static ssize_t snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
alaw->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
areas, offset,
frames, pcm->setup.format.channels,
frames, pcm->channels,
alaw->getput_idx);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
@ -384,7 +362,7 @@ static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_alaw_t *alaw = pcm->private;
fprintf(fp, "A-Law conversion PCM (%s)\n",
snd_pcm_format_name(alaw->sformat));
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -395,12 +373,12 @@ static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_alaw_ops = {
close: snd_pcm_plugin_close,
info: snd_pcm_plugin_info,
params_info: snd_pcm_alaw_params_info,
params: snd_pcm_alaw_params,
setup: snd_pcm_alaw_setup,
hw_info: snd_pcm_alaw_hw_info,
hw_params: snd_pcm_alaw_hw_params,
sw_params: snd_pcm_plugin_sw_params,
dig_info: snd_pcm_plugin_dig_info,
dig_params: snd_pcm_plugin_dig_params,
channel_info: snd_pcm_plugin_channel_info,
channel_params: snd_pcm_plugin_channel_params,
channel_setup: snd_pcm_plugin_channel_setup,
dump: snd_pcm_alaw_dump,
nonblock: snd_pcm_plugin_nonblock,
async: snd_pcm_plugin_async,
@ -414,7 +392,7 @@ int snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *slav
snd_pcm_alaw_t *alaw;
assert(pcmp && slave);
if (snd_pcm_format_linear(sformat) != 1 &&
sformat != SND_PCM_SFMT_A_LAW)
sformat != SND_PCM_FORMAT_A_LAW)
return -EINVAL;
alaw = calloc(1, sizeof(snd_pcm_alaw_t));
if (!alaw) {
@ -481,7 +459,7 @@ int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name,
if (sformat < 0)
return -EINVAL;
if (snd_pcm_format_linear(sformat) != 1 &&
sformat != SND_PCM_SFMT_A_LAW)
sformat != SND_PCM_FORMAT_A_LAW)
return -EINVAL;
continue;
}

View file

@ -23,11 +23,16 @@
#include "pcm_local.h"
#include "pcm_plugin.h"
enum {
SND_PCM_FILE_FORMAT_RAW
};
typedef struct {
snd_pcm_t *slave;
int close_slave;
char *fname;
int fd;
int format;
} snd_pcm_file_t;
static int snd_pcm_file_close(snd_pcm_t *pcm)
@ -68,18 +73,6 @@ static int snd_pcm_file_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * in
return snd_pcm_channel_info(file->slave, info);
}
static int snd_pcm_file_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
{
snd_pcm_file_t *file = pcm->private;
return snd_pcm_channel_params(file->slave, params);
}
static int snd_pcm_file_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
{
snd_pcm_file_t *file = pcm->private;
return snd_pcm_channel_setup(file->slave, setup);
}
static int snd_pcm_file_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
{
snd_pcm_file_t *file = pcm->private;
@ -148,11 +141,16 @@ static void snd_pcm_file_write_areas(snd_pcm_t *pcm,
snd_pcm_file_t *file = pcm->private;
size_t bytes = snd_pcm_frames_to_bytes(pcm, frames);
char *buf;
size_t channels = pcm->setup.format.channels;
size_t channels = pcm->channels;
snd_pcm_channel_area_t buf_areas[channels];
size_t channel;
ssize_t r;
if (pcm->setup.mmap_shape != SND_PCM_MMAP_INTERLEAVED) {
switch (pcm->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
case SND_PCM_ACCESS_RW_INTERLEAVED:
buf = snd_pcm_channel_area_addr(areas, offset);
break;
default:
buf = alloca(bytes);
for (channel = 0; channel < channels; ++channel) {
snd_pcm_channel_area_t *a = &buf_areas[channel];
@ -161,9 +159,8 @@ static void snd_pcm_file_write_areas(snd_pcm_t *pcm,
a->step = pcm->bits_per_frame;
}
snd_pcm_areas_copy(areas, offset, buf_areas, 0,
channels, frames, pcm->setup.format.sfmt);
} else
buf = snd_pcm_channel_area_addr(areas, offset);
channels, frames, pcm->format);
}
r = write(file->fd, buf, bytes);
assert(r == (ssize_t)bytes);
}
@ -183,7 +180,7 @@ static ssize_t snd_pcm_file_writei(snd_pcm_t *pcm, const void *buffer, size_t si
static ssize_t snd_pcm_file_writen(snd_pcm_t *pcm, void **bufs, size_t size)
{
snd_pcm_file_t *file = pcm->private;
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
ssize_t n = snd_pcm_writen(file->slave, bufs, size);
if (n > 0) {
snd_pcm_areas_from_bufs(pcm, areas, bufs);
@ -207,7 +204,7 @@ static ssize_t snd_pcm_file_readi(snd_pcm_t *pcm, void *buffer, size_t size)
static ssize_t snd_pcm_file_readn(snd_pcm_t *pcm, void **bufs, size_t size)
{
snd_pcm_file_t *file = pcm->private;
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
ssize_t n = snd_pcm_writen(file->slave, bufs, size);
if (n > 0) {
snd_pcm_areas_from_bufs(pcm, areas, bufs);
@ -226,12 +223,12 @@ static ssize_t snd_pcm_file_mmap_forward(snd_pcm_t *pcm, size_t size)
return n;
while (xfer < (size_t)n) {
size_t frames = size - xfer;
size_t cont = pcm->setup.buffer_size - ofs;
size_t cont = pcm->buffer_size - ofs;
if (cont < frames)
frames = cont;
snd_pcm_file_write_areas(pcm, snd_pcm_mmap_areas(file->slave), ofs, frames);
ofs += frames;
if (ofs == pcm->setup.buffer_size)
if (ofs == pcm->buffer_size)
ofs = 0;
xfer += frames;
}
@ -250,44 +247,46 @@ static int snd_pcm_file_set_avail_min(snd_pcm_t *pcm, size_t frames)
return snd_pcm_set_avail_min(file->slave, frames);
}
static int snd_pcm_file_mmap(snd_pcm_t *pcm)
static int snd_pcm_file_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_file_t *file = pcm->private;
int err = snd_pcm_mmap(file->slave);
if (err < 0)
return err;
pcm->mmap_info_count = file->slave->mmap_info_count;
pcm->mmap_info = file->slave->mmap_info;
return 0;
return snd_pcm_hw_info(file->slave, info);
}
static int snd_pcm_file_munmap(snd_pcm_t *pcm)
static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_file_t *file = pcm->private;
int err = snd_pcm_munmap(file->slave);
if (err < 0)
return err;
pcm->mmap_info_count = 0;
pcm->mmap_info = 0;
return 0;
return snd_pcm_hw_params(file->slave, params);
}
static int snd_pcm_file_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_file_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
{
snd_pcm_file_t *file = pcm->private;
return snd_pcm_params_info(file->slave, info);
return snd_pcm_sw_params(file->slave, params);
}
static int snd_pcm_file_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_file_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info)
{
snd_pcm_file_t *file = pcm->private;
return snd_pcm_params(file->slave, params);
return snd_pcm_dig_info(file->slave, info);
}
static int snd_pcm_file_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
static int snd_pcm_file_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params)
{
snd_pcm_file_t *file = pcm->private;
return snd_pcm_setup(file->slave, setup);
return snd_pcm_dig_params(file->slave, params);
}
static int snd_pcm_file_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
snd_pcm_file_t *file = pcm->private;
return snd_pcm_mmap(file->slave);
}
static int snd_pcm_file_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
snd_pcm_file_t *file = pcm->private;
return snd_pcm_munmap(file->slave);
}
static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp)
@ -297,7 +296,7 @@ static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp)
fprintf(fp, "File PCM (file=%s)\n", file->fname);
else
fprintf(fp, "File PCM (fd=%d)\n", file->fd);
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -308,12 +307,12 @@ static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_file_ops = {
close: snd_pcm_file_close,
info: snd_pcm_file_info,
params_info: snd_pcm_file_params_info,
params: snd_pcm_file_params,
setup: snd_pcm_file_setup,
hw_info: snd_pcm_file_hw_info,
hw_params: snd_pcm_file_hw_params,
sw_params: snd_pcm_file_sw_params,
dig_info: snd_pcm_file_dig_info,
dig_params: snd_pcm_file_dig_params,
channel_info: snd_pcm_file_channel_info,
channel_params: snd_pcm_file_channel_params,
channel_setup: snd_pcm_file_channel_setup,
dump: snd_pcm_file_dump,
nonblock: snd_pcm_file_nonblock,
async: snd_pcm_file_async,
@ -340,11 +339,11 @@ snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
set_avail_min: snd_pcm_file_set_avail_min,
};
int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, snd_pcm_t *slave, int close_slave)
int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, char *fmt, snd_pcm_t *slave, int close_slave)
{
snd_pcm_t *pcm;
snd_pcm_file_t *file;
assert(pcmp && slave);
assert(pcmp);
if (fname) {
fd = open(fname, O_WRONLY|O_CREAT, 0666);
if (fd < 0) {
@ -354,8 +353,16 @@ int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, snd_pcm
}
file = calloc(1, sizeof(snd_pcm_file_t));
if (!file) {
if (fname)
close(fd);
return -ENOMEM;
}
if (fmt == NULL ||
strcmp(fmt, "raw") == 0)
file->format = SND_PCM_FILE_FORMAT_RAW;
else
ERR("file format %s is unknown", fmt);
file->fname = fname;
file->fd = fd;
file->slave = slave;
@ -393,6 +400,7 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name,
int err;
snd_pcm_t *spcm;
char *fname = NULL;
char *format = NULL;
long fd = -1;
snd_config_foreach(i, conf) {
snd_config_t *n = snd_config_entry(i);
@ -408,6 +416,12 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name,
return -EINVAL;
continue;
}
if (strcmp(n->id, "format") == 0) {
err = snd_config_string_get(n, &format);
if (err < 0)
return -EINVAL;
continue;
}
if (strcmp(n->id, "file") == 0) {
err = snd_config_string_get(n, &fname);
if (err < 0) {
@ -434,7 +448,7 @@ int _snd_pcm_file_open(snd_pcm_t **pcmp, char *name,
free(sname);
if (err < 0)
return err;
err = snd_pcm_file_open(pcmp, name, fname, fd, spcm, 1);
err = snd_pcm_file_open(pcmp, name, fname, fd, format, spcm, 1);
if (err < 0)
snd_pcm_close(spcm);
return err;

View file

@ -29,17 +29,24 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <asm/page.h>
#include "pcm_local.h"
#include "../control/control_local.h"
#ifndef F_SETSIG
#define F_SETSIG 10
#endif
#ifndef PAGE_ALIGN
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
#endif
typedef struct {
int fd;
int card, device, subdevice;
volatile snd_pcm_mmap_status_t *mmap_status;
snd_pcm_mmap_control_t *mmap_control;
int shmid;
} snd_pcm_hw_t;
#define SND_FILE_PCM_STREAM_PLAYBACK "/dev/snd/pcmC%iD%ip"
@ -102,7 +109,7 @@ static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid)
return 0;
}
static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
static int _snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
@ -113,41 +120,57 @@ static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
return 0;
}
static int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_hw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0) {
SYSERR("SND_PCM_IOCTL_PARAMS_INFO failed");
if (ioctl(fd, SND_PCM_IOCTL_HW_INFO, info) < 0) {
// SYSERR("SND_PCM_IOCTL_HW_INFO failed");
return -errno;
}
return 0;
}
static int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_PARAMS, params) < 0) {
SYSERR("SND_PCM_IOCTL_PARAMS failed");
if (ioctl(fd, SND_PCM_IOCTL_HW_PARAMS, params) < 0) {
SYSERR("SND_PCM_IOCTL_HW_PARAMS failed");
return -errno;
}
return 0;
}
static int snd_pcm_hw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0) {
SYSERR("SND_PCM_IOCTL_SETUP failed");
if (ioctl(fd, SND_PCM_IOCTL_SW_PARAMS, params) < 0) {
SYSERR("SND_PCM_IOCTL_SW_PARAMS failed");
return -errno;
}
if (setup->mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
if (setup->xfer_mode == SND_PCM_XFER_INTERLEAVED)
setup->mmap_shape = SND_PCM_MMAP_INTERLEAVED;
else
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
return 0;
}
static int snd_pcm_hw_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_DIG_INFO, info) < 0) {
SYSERR("SND_PCM_IOCTL_DIG_INFO failed");
return -errno;
}
return 0;
}
static int snd_pcm_hw_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_DIG_PARAMS, params) < 0) {
SYSERR("SND_PCM_IOCTL_DIG_PARAMS failed");
return -errno;
}
return 0;
}
@ -155,51 +178,24 @@ static int snd_pcm_hw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
{
snd_pcm_hw_t *hw = pcm->private;
snd_pcm_hw_channel_info_t hw_info;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, info) < 0) {
hw_info.channel = info->channel;
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_INFO, &hw_info) < 0) {
SYSERR("SND_PCM_IOCTL_CHANNEL_INFO failed");
return -errno;
}
return 0;
}
static int snd_pcm_hw_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_PARAMS, params) < 0) {
SYSERR("SND_PCM_IOCTL_CHANNEL_PARAMS failed");
return -errno;
}
return 0;
}
static int snd_pcm_hw_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0) {
SYSERR("SND_PCM_IOCTL_CHANNEL_SETUP failed");
return -errno;
}
if (!pcm->mmap_info)
info->channel = hw_info.channel;
if (pcm->info & SND_PCM_INFO_MMAP) {
info->addr = 0;
info->first = hw_info.first;
info->step = hw_info.step;
info->type = SND_PCM_AREA_MMAP;
info->u.mmap.fd = fd;
info->u.mmap.offset = hw_info.offset;
return 0;
if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) {
setup->running_area.addr = pcm->mmap_info->addr;
setup->running_area.first = setup->channel * pcm->bits_per_sample;
setup->running_area.step = pcm->bits_per_frame;
} else {
setup->running_area.addr = pcm->mmap_info->addr + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
setup->running_area.first = 0;
setup->running_area.step = pcm->bits_per_sample;
}
setup->stopped_area = setup->running_area;
} else {
setup->running_area.addr = pcm->mmap_info->addr + (long)setup->running_area.addr;
setup->stopped_area.addr = setup->running_area.addr;
}
return 0;
return snd_pcm_channel_info_shm(pcm, info, hw->shmid);
}
static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
@ -245,6 +241,8 @@ static int snd_pcm_hw_start(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
assert(pcm->stream != SND_PCM_STREAM_PLAYBACK ||
snd_pcm_mmap_playback_hw_avail(pcm) > 0);
if (ioctl(fd, SND_PCM_IOCTL_START) < 0) {
SYSERR("SND_PCM_IOCTL_START failed");
return -errno;
@ -288,7 +286,7 @@ static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
static ssize_t snd_pcm_hw_rewind(snd_pcm_t *pcm, size_t frames)
{
ssize_t hw_avail;
if (pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) {
if (pcm->xrun_mode == SND_PCM_XRUN_ASAP) {
ssize_t d;
int err = snd_pcm_hw_delay(pcm, &d);
if (err < 0)
@ -363,7 +361,7 @@ static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private;
void *ptr;
ptr = mmap(NULL, sizeof(snd_pcm_mmap_status_t), PROT_READ, MAP_FILE|MAP_SHARED,
ptr = mmap(NULL, PAGE_ALIGN(sizeof(snd_pcm_mmap_status_t)), PROT_READ, MAP_FILE|MAP_SHARED,
hw->fd, SND_PCM_MMAP_OFFSET_STATUS);
if (ptr == MAP_FAILED || ptr == NULL) {
SYSERR("status mmap failed");
@ -378,7 +376,7 @@ static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private;
void *ptr;
ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
ptr = mmap(NULL, PAGE_ALIGN(sizeof(snd_pcm_mmap_control_t)), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED,
hw->fd, SND_PCM_MMAP_OFFSET_CONTROL);
if (ptr == MAP_FAILED || ptr == NULL) {
SYSERR("control mmap failed");
@ -389,31 +387,6 @@ static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
return 0;
}
static int snd_pcm_hw_mmap(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private;
snd_pcm_mmap_info_t *i = calloc(1, sizeof(*i));
int err;
if (!i)
return -ENOMEM;
if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
err = snd_pcm_alloc_user_mmap(pcm, i);
if (err < 0) {
free(i);
return err;
}
} else {
err = snd_pcm_alloc_kernel_mmap(pcm, i, hw->fd);
if (err < 0) {
free(i);
return err;
}
}
pcm->mmap_info = i;
pcm->mmap_info_count = 1;
return 0;
}
static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private;
@ -434,34 +407,50 @@ static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm)
return 0;
}
static int snd_pcm_hw_mmap(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private;
if (!(pcm->info & SND_PCM_INFO_MMAP)) {
size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size);
int id = shmget(IPC_PRIVATE, size, 0666);
if (id < 0) {
SYSERR("shmget failed");
return -errno;
}
hw->shmid = id;
}
return 0;
}
static int snd_pcm_hw_munmap(snd_pcm_t *pcm)
{
int err = snd_pcm_free_mmap(pcm, pcm->mmap_info);
if (err < 0)
return err;
pcm->mmap_info_count = 0;
free(pcm->mmap_info);
pcm->mmap_info = 0;
snd_pcm_hw_t *hw = pcm->private;
if (!(pcm->info & SND_PCM_INFO_MMAP)) {
if (shmctl(hw->shmid, IPC_RMID, 0) < 0) {
SYSERR("shmctl IPC_RMID failed");
return -errno;
}
}
return 0;
}
static int snd_pcm_hw_close(snd_pcm_t *pcm)
{
snd_pcm_hw_t *hw = pcm->private;
int fd = hw->fd;
free(hw);
if (close(fd)) {
if (close(hw->fd)) {
SYSERR("close failed\n");
return -errno;
}
snd_pcm_hw_munmap_status(pcm);
snd_pcm_hw_munmap_control(pcm);
free(hw);
return 0;
}
static ssize_t snd_pcm_hw_mmap_forward(snd_pcm_t *pcm, size_t size)
{
if (pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED && pcm->stream == SND_PCM_STREAM_PLAYBACK)
if (!(pcm->info & SND_PCM_INFO_MMAP) &&
pcm->stream == SND_PCM_STREAM_PLAYBACK)
return snd_pcm_write_mmap(pcm, size);
snd_pcm_mmap_appl_forward(pcm, size);
return size;
@ -471,8 +460,8 @@ static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
{
size_t avail;
ssize_t err;
if (pcm->setup.ready_mode == SND_PCM_READY_ASAP ||
pcm->setup.xrun_mode == SND_PCM_XRUN_ASAP) {
if (pcm->ready_mode == SND_PCM_READY_ASAP ||
pcm->xrun_mode == SND_PCM_XRUN_ASAP) {
ssize_t d;
int err = snd_pcm_hw_delay(pcm, &d);
if (err < 0)
@ -482,7 +471,8 @@ static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
avail = snd_pcm_mmap_playback_avail(pcm);
} else {
avail = snd_pcm_mmap_capture_avail(pcm);
if (avail > 0 && pcm->setup.mmap_shape == SND_PCM_MMAP_UNSPECIFIED) {
if (avail > 0 &&
!(pcm->info & SND_PCM_INFO_MMAP)) {
err = snd_pcm_read_mmap(pcm, avail);
if (err < 0)
return err;
@ -490,7 +480,7 @@ static ssize_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
return err;
}
}
if (avail > pcm->setup.buffer_size)
if (avail > pcm->buffer_size)
return -EPIPE;
return avail;
}
@ -510,7 +500,7 @@ static void snd_pcm_hw_dump(snd_pcm_t *pcm, FILE *fp)
fprintf(fp, "Hardware PCM card %d '%s' device %d subdevice %d\n",
hw->card, name, hw->device, hw->subdevice);
free(name);
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "\nIts setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -518,13 +508,13 @@ static void snd_pcm_hw_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_hw_ops = {
close: snd_pcm_hw_close,
info: snd_pcm_hw_info,
params_info: snd_pcm_hw_params_info,
params: snd_pcm_hw_params,
setup: snd_pcm_hw_setup,
info: _snd_pcm_hw_info,
hw_info: snd_pcm_hw_hw_info,
hw_params: snd_pcm_hw_hw_params,
sw_params: snd_pcm_hw_sw_params,
dig_info: snd_pcm_hw_dig_info,
dig_params: snd_pcm_hw_dig_params,
channel_info: snd_pcm_hw_channel_info,
channel_params: snd_pcm_hw_channel_params,
channel_setup: snd_pcm_hw_channel_setup,
dump: snd_pcm_hw_dump,
nonblock: snd_pcm_hw_nonblock,
async: snd_pcm_hw_async,

View file

@ -28,8 +28,6 @@ typedef struct {
snd_pcm_plugin_t plug;
int conv_idx;
int sformat;
int cformat;
int cxfer_mode, cmmap_shape;
} snd_pcm_linear_t;
static void linear_transfer(snd_pcm_channel_area_t *src_areas, size_t src_offset,
@ -74,83 +72,65 @@ static void linear_transfer(snd_pcm_channel_area_t *src_areas, size_t src_offset
}
}
static int snd_pcm_linear_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_linear_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_linear_t *linear = pcm->private;
unsigned int req_mask = info->req_mask;
unsigned int sfmt = info->req.format.sfmt;
unsigned int format_mask, access_mask;
int err;
if (req_mask & SND_PCM_PARAMS_SFMT &&
!snd_pcm_format_linear(sfmt)) {
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
access_mask = info->access_mask;
if (access_mask == 0)
return -EINVAL;
info->format_mask &= SND_PCM_FMTBIT_LINEAR;
format_mask = info->format_mask;
if (format_mask == 0)
return -EINVAL;
info->format_mask = 1U << linear->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(linear->plug.slave, info);
if (info->format_mask)
info->format_mask = format_mask;
if (info->access_mask) {
linear->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
info->req_mask |= SND_PCM_PARAMS_SFMT;
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
SND_PCM_PARAMS_XFER_MODE);
info->req.format.sfmt = linear->sformat;
err = snd_pcm_params_info(linear->plug.slave, info);
info->req_mask = req_mask;
info->req.format.sfmt = sfmt;
if (err < 0)
return err;
if (req_mask & SND_PCM_PARAMS_SFMT)
info->formats = 1 << sfmt;
else
info->formats = SND_PCM_LINEAR_FORMATS;
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info);
return 0;
}
static int snd_pcm_linear_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_linear_t *linear = pcm->private;
snd_pcm_t *slave = linear->plug.slave;
unsigned int format, access;
int err;
if (!snd_pcm_format_linear(params->format.sfmt)) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
linear->cformat = params->format.sfmt;
linear->cxfer_mode = params->xfer_mode;
linear->cmmap_shape = params->mmap_shape;
params->format.sfmt = linear->sformat;
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
err = snd_pcm_params_mmap(slave, params);
params->format.sfmt = linear->cformat;
params->xfer_mode = linear->cxfer_mode;
params->mmap_shape = linear->cmmap_shape;
return err;
}
static int snd_pcm_linear_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
{
snd_pcm_linear_t *linear = pcm->private;
int err = snd_pcm_setup(linear->plug.slave, setup);
format = params->format;
access = params->access;
params->format = linear->sformat;
if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0)
return err;
assert(linear->sformat == setup->format.sfmt);
if (linear->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
setup->xfer_mode = linear->cxfer_mode;
if (linear->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
else
setup->mmap_shape = linear->cmmap_shape;
setup->format.sfmt = linear->cformat;
setup->mmap_bytes = 0;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
linear->conv_idx = conv_index(linear->cformat,
linear->conv_idx = conv_index(format,
linear->sformat);
else
linear->conv_idx = conv_index(linear->sformat,
linear->cformat);
format);
return 0;
}
@ -171,7 +151,7 @@ static ssize_t snd_pcm_linear_write_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
linear_transfer(areas, offset,
snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
frames, pcm->setup.format.channels, linear->conv_idx);
frames, pcm->channels, linear->conv_idx);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
break;
@ -205,7 +185,7 @@ static ssize_t snd_pcm_linear_read_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
linear_transfer(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
areas, offset,
frames, pcm->setup.format.channels, linear->conv_idx);
frames, pcm->channels, linear->conv_idx);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
break;
@ -227,7 +207,7 @@ static void snd_pcm_linear_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_linear_t *linear = pcm->private;
fprintf(fp, "Linear conversion PCM (%s)\n",
snd_pcm_format_name(linear->sformat));
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -238,12 +218,12 @@ static void snd_pcm_linear_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_linear_ops = {
close: snd_pcm_plugin_close,
info: snd_pcm_plugin_info,
params_info: snd_pcm_linear_params_info,
params: snd_pcm_linear_params,
setup: snd_pcm_linear_setup,
hw_info: snd_pcm_linear_hw_info,
hw_params: snd_pcm_linear_hw_params,
sw_params: snd_pcm_plugin_sw_params,
dig_info: snd_pcm_plugin_dig_info,
dig_params: snd_pcm_plugin_dig_params,
channel_info: snd_pcm_plugin_channel_info,
channel_params: snd_pcm_plugin_channel_params,
channel_setup: snd_pcm_plugin_channel_setup,
dump: snd_pcm_linear_dump,
nonblock: snd_pcm_plugin_nonblock,
async: snd_pcm_plugin_async,

View file

@ -22,29 +22,48 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/uio.h>
#include <errno.h>
#include "asoundlib.h"
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
#define ERR(...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__)
#define SYSERR(...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__)
#define ERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, __VA_ARGS__)
#define SYSERR(...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, __VA_ARGS__)
#else
#define ERR(args...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args)
#define SYSERR(args...) snd_pcm_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args)
#define ERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, 0, ##args)
#define SYSERR(args...) snd_lib_error(__FILE__, __LINE__, __FUNCTION__, errno, ##args)
#endif
typedef struct _snd_pcm_channel_info {
unsigned int channel;
void *addr; /* base address of channel samples */
unsigned int first; /* offset to first sample in bits */
unsigned int step; /* samples distance in bits */
enum { SND_PCM_AREA_SHM, SND_PCM_AREA_MMAP } type;
union {
struct {
int shmid;
} shm;
struct {
int fd;
off_t offset;
} mmap;
} u;
char reserved[64];
} snd_pcm_channel_info_t;
typedef struct {
int (*close)(snd_pcm_t *pcm);
int (*nonblock)(snd_pcm_t *pcm, int nonblock);
int (*async)(snd_pcm_t *pcm, int sig, pid_t pid);
int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info);
int (*params_info)(snd_pcm_t *pcm, snd_pcm_params_info_t *info);
int (*params)(snd_pcm_t *pcm, snd_pcm_params_t *params);
int (*setup)(snd_pcm_t *pcm, snd_pcm_setup_t *setup);
int (*hw_info)(snd_pcm_t *pcm, snd_pcm_hw_info_t *info);
int (*hw_params)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
int (*sw_params)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
int (*dig_info)(snd_pcm_t *pcm, snd_pcm_dig_info_t *info);
int (*dig_params)(snd_pcm_t *pcm, snd_pcm_dig_params_t *params);
int (*channel_info)(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
int (*channel_params)(snd_pcm_t *pcm, snd_pcm_channel_params_t *params);
int (*channel_setup)(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup);
void (*dump)(snd_pcm_t *pcm, FILE *fp);
int (*mmap)(snd_pcm_t *pcm);
int (*munmap)(snd_pcm_t *pcm);
@ -69,38 +88,43 @@ typedef struct {
int (*set_avail_min)(snd_pcm_t *pcm, size_t frames);
} snd_pcm_fast_ops_t;
typedef struct {
unsigned int index;
enum { SND_PCM_MMAP_KERNEL, SND_PCM_MMAP_USER } type;
void *addr;
size_t size;
union {
struct {
int shmid;
} user;
struct {
int fd;
} kernel;
} u;
} snd_pcm_mmap_info_t;
struct snd_pcm {
struct _snd_pcm {
char *name;
snd_pcm_type_t type;
int stream;
int mode;
int poll_fd;
int valid_setup;
snd_pcm_setup_t setup;
int setup;
unsigned int access; /* access mode */
unsigned int format; /* SND_PCM_FORMAT_* */
unsigned int subformat; /* subformat */
unsigned int rate; /* rate in Hz */
unsigned int channels; /* channels */
size_t fragment_size; /* fragment size */
unsigned int fragments; /* fragments */
unsigned int start_mode; /* start mode */
unsigned int ready_mode; /* ready detection mode */
unsigned int xrun_mode; /* xrun detection mode */
size_t avail_min; /* min avail frames for wakeup */
size_t xfer_min; /* xfer min size */
size_t xfer_align; /* xfer size need to be a multiple */
unsigned int time: 1; /* timestamp switch */
size_t boundary; /* pointers wrap point */
unsigned int info; /* Info for returned setup */
unsigned int msbits; /* used most significant bits */
unsigned int rate_master; /* Exact rate is rate_master / */
unsigned int rate_divisor; /* rate_divisor */
size_t fifo_size; /* chip FIFO size in frames */
size_t buffer_size;
size_t bits_per_sample;
size_t bits_per_frame;
size_t *appl_ptr;
volatile size_t *hw_ptr;
int mmap_auto;
size_t mmap_info_count;
snd_pcm_mmap_info_t *mmap_info;
int mmap_rw;
snd_pcm_channel_info_t *mmap_channels;
snd_pcm_channel_area_t *running_areas;
snd_pcm_channel_area_t *stopped_areas;
void *stopped;
snd_pcm_ops_t *ops;
snd_pcm_fast_ops_t *fast_ops;
snd_pcm_t *op_arg;
@ -108,10 +132,18 @@ struct snd_pcm {
void *private;
};
int snd_pcm_hw_open(snd_pcm_t **pcm, char *name, int card, int device, int subdevice, int stream, int mode);
int snd_pcm_plug_open_hw(snd_pcm_t **pcm, char *name, int card, int device, int subdevice, int stream, int mode);
int snd_pcm_shm_open(snd_pcm_t **pcmp, char *name, char *socket, char *sname, int stream, int mode);
int snd_pcm_file_open(snd_pcm_t **pcmp, char *name, char *fname, int fd, char *fmt, snd_pcm_t *slave, int close_slave);
int snd_pcm_null_open(snd_pcm_t **pcmp, char *name, int stream, int mode);
void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void *buf);
void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas, void **bufs);
int snd_pcm_params_mmap(snd_pcm_t *pcm, snd_pcm_params_t *params);
int snd_pcm_mmap(snd_pcm_t *pcm);
int snd_pcm_munmap(snd_pcm_t *pcm);
int snd_pcm_mmap_ready(snd_pcm_t *pcm);
ssize_t snd_pcm_mmap_appl_ptr(snd_pcm_t *pcm, off_t offset);
void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames);
@ -135,16 +167,19 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
snd_pcm_xfer_areas_func_t func);
ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size);
ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size);
int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i);
int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd);
int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i);
int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info);
void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info);
void snd_pcm_hw_info_to_params(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params);
void snd_pcm_hw_info_to_params_fail(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params);
int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid);
static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
{
ssize_t avail;
avail = *pcm->hw_ptr + pcm->setup.buffer_size - *pcm->appl_ptr;
avail = *pcm->hw_ptr + pcm->buffer_size - *pcm->appl_ptr;
if (avail < 0)
avail += pcm->setup.boundary;
avail += pcm->boundary;
return avail;
}
@ -153,7 +188,7 @@ static inline size_t snd_pcm_mmap_capture_avail(snd_pcm_t *pcm)
ssize_t avail;
avail = *pcm->hw_ptr - *pcm->appl_ptr;
if (avail < 0)
avail += pcm->setup.boundary;
avail += pcm->boundary;
return avail;
}
@ -162,19 +197,19 @@ static inline size_t snd_pcm_mmap_avail(snd_pcm_t *pcm)
ssize_t avail;
avail = *pcm->hw_ptr - *pcm->appl_ptr;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
avail += pcm->setup.buffer_size;
avail += pcm->buffer_size;
if (avail < 0)
avail += pcm->setup.boundary;
avail += pcm->boundary;
return avail;
}
static inline ssize_t snd_pcm_mmap_playback_hw_avail(snd_pcm_t *pcm)
{
ssize_t avail;
avail = *pcm->hw_ptr + pcm->setup.buffer_size - *pcm->appl_ptr;
avail = *pcm->hw_ptr + pcm->buffer_size - *pcm->appl_ptr;
if (avail < 0)
avail += pcm->setup.boundary;
return pcm->setup.buffer_size - avail;
avail += pcm->boundary;
return pcm->buffer_size - avail;
}
static inline ssize_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm)
@ -182,8 +217,8 @@ static inline ssize_t snd_pcm_mmap_capture_hw_avail(snd_pcm_t *pcm)
ssize_t avail;
avail = *pcm->hw_ptr - *pcm->appl_ptr;
if (avail < 0)
avail += pcm->setup.boundary;
return pcm->setup.buffer_size - avail;
avail += pcm->boundary;
return pcm->buffer_size - avail;
}
static inline ssize_t snd_pcm_mmap_hw_avail(snd_pcm_t *pcm)
@ -191,10 +226,10 @@ static inline ssize_t snd_pcm_mmap_hw_avail(snd_pcm_t *pcm)
ssize_t avail;
avail = *pcm->hw_ptr - *pcm->appl_ptr;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
avail += pcm->setup.buffer_size;
avail += pcm->buffer_size;
if (avail < 0)
avail += pcm->setup.boundary;
return pcm->setup.buffer_size - avail;
avail += pcm->boundary;
return pcm->buffer_size - avail;
}
#define snd_pcm_mmap_playback_delay snd_pcm_mmap_playback_hw_avail

View file

@ -39,21 +39,21 @@
int snd_pcm_format_signed(int format)
{
switch (format) {
case SND_PCM_SFMT_S8:
case SND_PCM_SFMT_S16_LE:
case SND_PCM_SFMT_S16_BE:
case SND_PCM_SFMT_S24_LE:
case SND_PCM_SFMT_S24_BE:
case SND_PCM_SFMT_S32_LE:
case SND_PCM_SFMT_S32_BE:
case SND_PCM_FORMAT_S8:
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_BE:
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
return 1;
case SND_PCM_SFMT_U8:
case SND_PCM_SFMT_U16_LE:
case SND_PCM_SFMT_U16_BE:
case SND_PCM_SFMT_U24_LE:
case SND_PCM_SFMT_U24_BE:
case SND_PCM_SFMT_U32_LE:
case SND_PCM_SFMT_U32_BE:
case SND_PCM_FORMAT_U8:
case SND_PCM_FORMAT_U16_LE:
case SND_PCM_FORMAT_U16_BE:
case SND_PCM_FORMAT_U24_LE:
case SND_PCM_FORMAT_U24_BE:
case SND_PCM_FORMAT_U32_LE:
case SND_PCM_FORMAT_U32_BE:
return 0;
default:
return -EINVAL;
@ -78,25 +78,25 @@ int snd_pcm_format_linear(int format)
int snd_pcm_format_little_endian(int format)
{
switch (format) {
case SND_PCM_SFMT_S16_LE:
case SND_PCM_SFMT_U16_LE:
case SND_PCM_SFMT_S24_LE:
case SND_PCM_SFMT_U24_LE:
case SND_PCM_SFMT_S32_LE:
case SND_PCM_SFMT_U32_LE:
case SND_PCM_SFMT_FLOAT_LE:
case SND_PCM_SFMT_FLOAT64_LE:
case SND_PCM_SFMT_IEC958_SUBFRAME_LE:
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_U16_LE:
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_U24_LE:
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_U32_LE:
case SND_PCM_FORMAT_FLOAT_LE:
case SND_PCM_FORMAT_FLOAT64_LE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
return 1;
case SND_PCM_SFMT_S16_BE:
case SND_PCM_SFMT_U16_BE:
case SND_PCM_SFMT_S24_BE:
case SND_PCM_SFMT_U24_BE:
case SND_PCM_SFMT_S32_BE:
case SND_PCM_SFMT_U32_BE:
case SND_PCM_SFMT_FLOAT_BE:
case SND_PCM_SFMT_FLOAT64_BE:
case SND_PCM_SFMT_IEC958_SUBFRAME_BE:
case SND_PCM_FORMAT_S16_BE:
case SND_PCM_FORMAT_U16_BE:
case SND_PCM_FORMAT_S24_BE:
case SND_PCM_FORMAT_U24_BE:
case SND_PCM_FORMAT_S32_BE:
case SND_PCM_FORMAT_U32_BE:
case SND_PCM_FORMAT_FLOAT_BE:
case SND_PCM_FORMAT_FLOAT64_BE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
return 0;
default:
return -EINVAL;
@ -116,36 +116,36 @@ int snd_pcm_format_big_endian(int format)
int snd_pcm_format_width(int format)
{
switch (format) {
case SND_PCM_SFMT_S8:
case SND_PCM_SFMT_U8:
case SND_PCM_FORMAT_S8:
case SND_PCM_FORMAT_U8:
return 8;
case SND_PCM_SFMT_S16_LE:
case SND_PCM_SFMT_S16_BE:
case SND_PCM_SFMT_U16_LE:
case SND_PCM_SFMT_U16_BE:
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
case SND_PCM_FORMAT_U16_LE:
case SND_PCM_FORMAT_U16_BE:
return 16;
case SND_PCM_SFMT_S24_LE:
case SND_PCM_SFMT_S24_BE:
case SND_PCM_SFMT_U24_LE:
case SND_PCM_SFMT_U24_BE:
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_BE:
case SND_PCM_FORMAT_U24_LE:
case SND_PCM_FORMAT_U24_BE:
return 24;
case SND_PCM_SFMT_S32_LE:
case SND_PCM_SFMT_S32_BE:
case SND_PCM_SFMT_U32_LE:
case SND_PCM_SFMT_U32_BE:
case SND_PCM_SFMT_FLOAT_LE:
case SND_PCM_SFMT_FLOAT_BE:
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
case SND_PCM_FORMAT_U32_LE:
case SND_PCM_FORMAT_U32_BE:
case SND_PCM_FORMAT_FLOAT_LE:
case SND_PCM_FORMAT_FLOAT_BE:
return 32;
case SND_PCM_SFMT_FLOAT64_LE:
case SND_PCM_SFMT_FLOAT64_BE:
case SND_PCM_FORMAT_FLOAT64_LE:
case SND_PCM_FORMAT_FLOAT64_BE:
return 64;
case SND_PCM_SFMT_IEC958_SUBFRAME_LE:
case SND_PCM_SFMT_IEC958_SUBFRAME_BE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
return 24;
case SND_PCM_SFMT_MU_LAW:
case SND_PCM_SFMT_A_LAW:
case SND_PCM_FORMAT_MU_LAW:
case SND_PCM_FORMAT_A_LAW:
return 8;
case SND_PCM_SFMT_IMA_ADPCM:
case SND_PCM_FORMAT_IMA_ADPCM:
return 4;
default:
return -EINVAL;
@ -155,34 +155,34 @@ int snd_pcm_format_width(int format)
int snd_pcm_format_physical_width(int format)
{
switch (format) {
case SND_PCM_SFMT_S8:
case SND_PCM_SFMT_U8:
case SND_PCM_FORMAT_S8:
case SND_PCM_FORMAT_U8:
return 8;
case SND_PCM_SFMT_S16_LE:
case SND_PCM_SFMT_S16_BE:
case SND_PCM_SFMT_U16_LE:
case SND_PCM_SFMT_U16_BE:
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
case SND_PCM_FORMAT_U16_LE:
case SND_PCM_FORMAT_U16_BE:
return 16;
case SND_PCM_SFMT_S24_LE:
case SND_PCM_SFMT_S24_BE:
case SND_PCM_SFMT_U24_LE:
case SND_PCM_SFMT_U24_BE:
case SND_PCM_SFMT_S32_LE:
case SND_PCM_SFMT_S32_BE:
case SND_PCM_SFMT_U32_LE:
case SND_PCM_SFMT_U32_BE:
case SND_PCM_SFMT_FLOAT_LE:
case SND_PCM_SFMT_FLOAT_BE:
case SND_PCM_SFMT_IEC958_SUBFRAME_LE:
case SND_PCM_SFMT_IEC958_SUBFRAME_BE:
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_BE:
case SND_PCM_FORMAT_U24_LE:
case SND_PCM_FORMAT_U24_BE:
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
case SND_PCM_FORMAT_U32_LE:
case SND_PCM_FORMAT_U32_BE:
case SND_PCM_FORMAT_FLOAT_LE:
case SND_PCM_FORMAT_FLOAT_BE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
return 32;
case SND_PCM_SFMT_FLOAT64_LE:
case SND_PCM_SFMT_FLOAT64_BE:
case SND_PCM_FORMAT_FLOAT64_LE:
case SND_PCM_FORMAT_FLOAT64_BE:
return 64;
case SND_PCM_SFMT_MU_LAW:
case SND_PCM_SFMT_A_LAW:
case SND_PCM_FORMAT_MU_LAW:
case SND_PCM_FORMAT_A_LAW:
return 8;
case SND_PCM_SFMT_IMA_ADPCM:
case SND_PCM_FORMAT_IMA_ADPCM:
return 4;
default:
return -EINVAL;
@ -192,35 +192,35 @@ int snd_pcm_format_physical_width(int format)
ssize_t snd_pcm_format_size(int format, size_t samples)
{
switch (format) {
case SND_PCM_SFMT_S8:
case SND_PCM_SFMT_U8:
case SND_PCM_FORMAT_S8:
case SND_PCM_FORMAT_U8:
return samples;
case SND_PCM_SFMT_S16_LE:
case SND_PCM_SFMT_S16_BE:
case SND_PCM_SFMT_U16_LE:
case SND_PCM_SFMT_U16_BE:
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
case SND_PCM_FORMAT_U16_LE:
case SND_PCM_FORMAT_U16_BE:
return samples * 2;
case SND_PCM_SFMT_S24_LE:
case SND_PCM_SFMT_S24_BE:
case SND_PCM_SFMT_U24_LE:
case SND_PCM_SFMT_U24_BE:
case SND_PCM_SFMT_S32_LE:
case SND_PCM_SFMT_S32_BE:
case SND_PCM_SFMT_U32_LE:
case SND_PCM_SFMT_U32_BE:
case SND_PCM_SFMT_FLOAT_LE:
case SND_PCM_SFMT_FLOAT_BE:
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_BE:
case SND_PCM_FORMAT_U24_LE:
case SND_PCM_FORMAT_U24_BE:
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
case SND_PCM_FORMAT_U32_LE:
case SND_PCM_FORMAT_U32_BE:
case SND_PCM_FORMAT_FLOAT_LE:
case SND_PCM_FORMAT_FLOAT_BE:
return samples * 4;
case SND_PCM_SFMT_FLOAT64_LE:
case SND_PCM_SFMT_FLOAT64_BE:
case SND_PCM_FORMAT_FLOAT64_LE:
case SND_PCM_FORMAT_FLOAT64_BE:
return samples * 8;
case SND_PCM_SFMT_IEC958_SUBFRAME_LE:
case SND_PCM_SFMT_IEC958_SUBFRAME_BE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
return samples * 4;
case SND_PCM_SFMT_MU_LAW:
case SND_PCM_SFMT_A_LAW:
case SND_PCM_FORMAT_MU_LAW:
case SND_PCM_FORMAT_A_LAW:
return samples;
case SND_PCM_SFMT_IMA_ADPCM:
case SND_PCM_FORMAT_IMA_ADPCM:
if (samples & 1)
return -EINVAL;
return samples / 2;
@ -232,44 +232,44 @@ ssize_t snd_pcm_format_size(int format, size_t samples)
u_int64_t snd_pcm_format_silence_64(int format)
{
switch (format) {
case SND_PCM_SFMT_S8:
case SND_PCM_SFMT_S16_LE:
case SND_PCM_SFMT_S16_BE:
case SND_PCM_SFMT_S24_LE:
case SND_PCM_SFMT_S24_BE:
case SND_PCM_SFMT_S32_LE:
case SND_PCM_SFMT_S32_BE:
case SND_PCM_FORMAT_S8:
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
case SND_PCM_FORMAT_S24_LE:
case SND_PCM_FORMAT_S24_BE:
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
return 0;
case SND_PCM_SFMT_U8:
case SND_PCM_FORMAT_U8:
return 0x8080808080808080ULL;
#ifdef SND_LITTLE_ENDIAN
case SND_PCM_SFMT_U16_LE:
case SND_PCM_FORMAT_U16_LE:
return 0x8000800080008000ULL;
case SND_PCM_SFMT_U24_LE:
case SND_PCM_FORMAT_U24_LE:
return 0x0080000000800000ULL;
case SND_PCM_SFMT_U32_LE:
case SND_PCM_FORMAT_U32_LE:
return 0x8000000080000000ULL;
case SND_PCM_SFMT_U16_BE:
case SND_PCM_FORMAT_U16_BE:
return 0x0080008000800080ULL;
case SND_PCM_SFMT_U24_BE:
case SND_PCM_FORMAT_U24_BE:
return 0x0000800000008000ULL;
case SND_PCM_SFMT_U32_BE:
case SND_PCM_FORMAT_U32_BE:
return 0x0000008000000080ULL;
#else
case SND_PCM_SFMT_U16_LE:
case SND_PCM_FORMAT_U16_LE:
return 0x0080008000800080ULL;
case SND_PCM_SFMT_U24_LE:
case SND_PCM_FORMAT_U24_LE:
return 0x0000800000008000ULL;
case SND_PCM_SFMT_U32_LE:
case SND_PCM_FORMAT_U32_LE:
return 0x0000008000000080ULL;
case SND_PCM_SFMT_U16_BE:
case SND_PCM_FORMAT_U16_BE:
return 0x8000800080008000ULL;
case SND_PCM_SFMT_U24_BE:
case SND_PCM_FORMAT_U24_BE:
return 0x0080000000800000ULL;
case SND_PCM_SFMT_U32_BE:
case SND_PCM_FORMAT_U32_BE:
return 0x8000000080000000ULL;
#endif
case SND_PCM_SFMT_FLOAT_LE:
case SND_PCM_FORMAT_FLOAT_LE:
{
union {
float f;
@ -282,7 +282,7 @@ u_int64_t snd_pcm_format_silence_64(int format)
return bswap_32(u.i);
#endif
}
case SND_PCM_SFMT_FLOAT64_LE:
case SND_PCM_FORMAT_FLOAT64_LE:
{
union {
double f;
@ -295,7 +295,7 @@ u_int64_t snd_pcm_format_silence_64(int format)
return bswap_64(u.i);
#endif
}
case SND_PCM_SFMT_FLOAT_BE:
case SND_PCM_FORMAT_FLOAT_BE:
{
union {
float f;
@ -308,7 +308,7 @@ u_int64_t snd_pcm_format_silence_64(int format)
return u.i;
#endif
}
case SND_PCM_SFMT_FLOAT64_BE:
case SND_PCM_FORMAT_FLOAT64_BE:
{
union {
double f;
@ -321,16 +321,16 @@ u_int64_t snd_pcm_format_silence_64(int format)
return u.i;
#endif
}
case SND_PCM_SFMT_IEC958_SUBFRAME_LE:
case SND_PCM_SFMT_IEC958_SUBFRAME_BE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_LE:
case SND_PCM_FORMAT_IEC958_SUBFRAME_BE:
return 0;
case SND_PCM_SFMT_MU_LAW:
case SND_PCM_FORMAT_MU_LAW:
return 0x7f7f7f7f7f7f7f7fULL;
case SND_PCM_SFMT_A_LAW:
case SND_PCM_FORMAT_A_LAW:
return 0x5555555555555555ULL;
case SND_PCM_SFMT_IMA_ADPCM: /* special case */
case SND_PCM_SFMT_MPEG:
case SND_PCM_SFMT_GSM:
case SND_PCM_FORMAT_IMA_ADPCM: /* special case */
case SND_PCM_FORMAT_MPEG:
case SND_PCM_FORMAT_GSM:
return 0;
}
return 0;
@ -395,22 +395,22 @@ ssize_t snd_pcm_format_set_silence(int format, void *data, size_t samples)
}
static int linear_formats[4*2*2] = {
SND_PCM_SFMT_S8,
SND_PCM_SFMT_U8,
SND_PCM_SFMT_S8,
SND_PCM_SFMT_U8,
SND_PCM_SFMT_S16_LE,
SND_PCM_SFMT_S16_BE,
SND_PCM_SFMT_U16_LE,
SND_PCM_SFMT_U16_BE,
SND_PCM_SFMT_S24_LE,
SND_PCM_SFMT_S24_BE,
SND_PCM_SFMT_U24_LE,
SND_PCM_SFMT_U24_BE,
SND_PCM_SFMT_S32_LE,
SND_PCM_SFMT_S32_BE,
SND_PCM_SFMT_U32_LE,
SND_PCM_SFMT_U32_BE
SND_PCM_FORMAT_S8,
SND_PCM_FORMAT_U8,
SND_PCM_FORMAT_S8,
SND_PCM_FORMAT_U8,
SND_PCM_FORMAT_S16_LE,
SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U16_LE,
SND_PCM_FORMAT_U16_BE,
SND_PCM_FORMAT_S24_LE,
SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_U24_LE,
SND_PCM_FORMAT_U24_BE,
SND_PCM_FORMAT_S32_LE,
SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_U32_LE,
SND_PCM_FORMAT_U32_BE
};
int snd_pcm_build_linear_format(int width, int unsignd, int big_endian)

View file

@ -23,15 +23,23 @@
#include <string.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <asm/page.h>
#include "pcm_local.h"
#ifndef PAGE_ALIGN
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
#endif
snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm)
{
int state = snd_pcm_state(pcm);
if (state == SND_PCM_STATE_RUNNING)
return pcm->running_areas;
else
return pcm->stopped_areas;
if (pcm->stopped_areas &&
snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING)
return pcm->stopped_areas;
return pcm->running_areas;
}
size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames)
@ -40,7 +48,7 @@ size_t snd_pcm_mmap_playback_xfer(snd_pcm_t *pcm, size_t frames)
size_t avail = snd_pcm_mmap_playback_avail(pcm);
if (avail < frames)
frames = avail;
cont = pcm->setup.buffer_size - *pcm->appl_ptr % pcm->setup.buffer_size;
cont = pcm->buffer_size - *pcm->appl_ptr % pcm->buffer_size;
if (cont < frames)
frames = cont;
return frames;
@ -52,7 +60,7 @@ size_t snd_pcm_mmap_capture_xfer(snd_pcm_t *pcm, size_t frames)
size_t avail = snd_pcm_mmap_capture_avail(pcm);
if (avail < frames)
frames = avail;
cont = pcm->setup.buffer_size - *pcm->appl_ptr % pcm->setup.buffer_size;
cont = pcm->buffer_size - *pcm->appl_ptr % pcm->buffer_size;
if (cont < frames)
frames = cont;
return frames;
@ -70,13 +78,13 @@ size_t snd_pcm_mmap_xfer(snd_pcm_t *pcm, size_t frames)
size_t snd_pcm_mmap_offset(snd_pcm_t *pcm)
{
assert(pcm);
return *pcm->appl_ptr % pcm->setup.buffer_size;
return *pcm->appl_ptr % pcm->buffer_size;
}
size_t snd_pcm_mmap_hw_offset(snd_pcm_t *pcm)
{
assert(pcm);
return *pcm->hw_ptr % pcm->setup.buffer_size;
return *pcm->hw_ptr % pcm->buffer_size;
}
void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames)
@ -84,7 +92,7 @@ void snd_pcm_mmap_appl_backward(snd_pcm_t *pcm, size_t frames)
ssize_t appl_ptr = *pcm->appl_ptr;
appl_ptr -= frames;
if (appl_ptr < 0)
appl_ptr += pcm->setup.boundary;
appl_ptr += pcm->boundary;
*pcm->appl_ptr = appl_ptr;
}
@ -92,8 +100,8 @@ void snd_pcm_mmap_appl_forward(snd_pcm_t *pcm, size_t frames)
{
size_t appl_ptr = *pcm->appl_ptr;
appl_ptr += frames;
if (appl_ptr >= pcm->setup.boundary)
appl_ptr -= pcm->setup.boundary;
if (appl_ptr >= pcm->boundary)
appl_ptr -= pcm->boundary;
*pcm->appl_ptr = appl_ptr;
}
@ -102,7 +110,7 @@ void snd_pcm_mmap_hw_backward(snd_pcm_t *pcm, size_t frames)
ssize_t hw_ptr = *pcm->hw_ptr;
hw_ptr -= frames;
if (hw_ptr < 0)
hw_ptr += pcm->setup.boundary;
hw_ptr += pcm->boundary;
*pcm->hw_ptr = hw_ptr;
}
@ -110,8 +118,8 @@ void snd_pcm_mmap_hw_forward(snd_pcm_t *pcm, size_t frames)
{
size_t hw_ptr = *pcm->hw_ptr;
hw_ptr += frames;
if (hw_ptr >= pcm->setup.boundary)
hw_ptr -= pcm->setup.boundary;
if (hw_ptr >= pcm->boundary)
hw_ptr -= pcm->boundary;
*pcm->hw_ptr = hw_ptr;
}
@ -127,11 +135,13 @@ ssize_t snd_pcm_mmap_write_areas(snd_pcm_t *pcm,
xfer = 0;
while (xfer < size) {
size_t frames = snd_pcm_mmap_playback_xfer(pcm, size - xfer);
ssize_t err;
snd_pcm_areas_copy(areas, offset,
snd_pcm_mmap_areas(pcm), snd_pcm_mmap_offset(pcm),
pcm->setup.format.channels,
frames, pcm->setup.format.sfmt);
snd_pcm_mmap_forward(pcm, frames);
pcm->channels,
frames, pcm->format);
err = snd_pcm_mmap_forward(pcm, frames);
assert(err == (ssize_t)frames);
offset += frames;
xfer += frames;
}
@ -152,11 +162,13 @@ ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm,
xfer = 0;
while (xfer < size) {
size_t frames = snd_pcm_mmap_capture_xfer(pcm, size - xfer);
ssize_t err;
snd_pcm_areas_copy(snd_pcm_mmap_areas(pcm), snd_pcm_mmap_offset(pcm),
areas, offset,
pcm->setup.format.channels,
frames, pcm->setup.format.sfmt);
snd_pcm_mmap_forward(pcm, frames);
pcm->channels,
frames, pcm->format);
err = snd_pcm_mmap_forward(pcm, frames);
assert(err == (ssize_t)frames);
offset += frames;
xfer += frames;
}
@ -167,7 +179,7 @@ ssize_t snd_pcm_mmap_read_areas(snd_pcm_t *pcm,
ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
{
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
return snd_pcm_write_areas(pcm, areas, 0, size,
snd_pcm_mmap_write_areas);
@ -175,7 +187,7 @@ ssize_t snd_pcm_mmap_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size)
{
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
snd_pcm_areas_from_bufs(pcm, areas, bufs);
return snd_pcm_write_areas(pcm, areas, 0, size,
snd_pcm_mmap_write_areas);
@ -183,7 +195,7 @@ ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size)
ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size)
{
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
snd_pcm_areas_from_buf(pcm, areas, buffer);
return snd_pcm_read_areas(pcm, areas, 0, size,
snd_pcm_mmap_read_areas);
@ -191,71 +203,204 @@ ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size)
ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size)
{
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
snd_pcm_areas_from_bufs(pcm, areas, bufs);
return snd_pcm_read_areas(pcm, areas, 0, size,
snd_pcm_mmap_read_areas);
}
int snd_pcm_mmap_get_areas(snd_pcm_t *pcm, snd_pcm_channel_area_t *stopped_areas, snd_pcm_channel_area_t *running_areas)
int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
snd_pcm_channel_setup_t setup;
snd_pcm_channel_area_t *r, *rp, *s, *sp;
unsigned int channel;
int err;
assert(pcm);
assert(pcm->mmap_info);
if (!pcm->running_areas) {
r = calloc(pcm->setup.format.channels, sizeof(*r));
s = calloc(pcm->setup.format.channels, sizeof(*s));
for (channel = 0, rp = r, sp = s; channel < pcm->setup.format.channels; ++channel, ++rp, ++sp) {
setup.channel = channel;
err = snd_pcm_channel_setup(pcm, &setup);
if (err < 0) {
free(r);
free(s);
return err;
}
*rp = setup.running_area;
*sp = setup.stopped_area;
}
pcm->running_areas = r;
pcm->stopped_areas = s;
}
if (running_areas)
memcpy(running_areas, pcm->running_areas, pcm->setup.format.channels * sizeof(*running_areas));
if (stopped_areas)
memcpy(stopped_areas, pcm->stopped_areas, pcm->setup.format.channels * sizeof(*stopped_areas));
return 0;
return pcm->ops->channel_info(pcm, info);
}
int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info,
int shmid)
{
switch (pcm->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
case SND_PCM_ACCESS_RW_INTERLEAVED:
info->first = info->channel * pcm->bits_per_sample;
info->step = pcm->bits_per_frame;
break;
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
case SND_PCM_ACCESS_RW_NONINTERLEAVED:
info->first = 0;
info->step = pcm->bits_per_sample;
break;
default:
assert(0);
break;
}
info->addr = 0;
info->type = SND_PCM_AREA_SHM;
info->u.shm.shmid = shmid;
return 0;
}
int snd_pcm_mmap(snd_pcm_t *pcm)
{
int err;
unsigned int c;
assert(pcm);
assert(pcm->valid_setup);
if (pcm->mmap_info)
return 0;
if ((err = pcm->ops->mmap(pcm->op_arg)) < 0)
return err;
err = snd_pcm_mmap_get_areas(pcm, NULL, NULL);
assert(pcm->setup);
assert(!pcm->mmap_channels);
err = pcm->ops->mmap(pcm);
if (err < 0)
return err;
pcm->mmap_channels = calloc(pcm->channels, sizeof(pcm->mmap_channels[0]));
if (!pcm->mmap_channels)
return -ENOMEM;
assert(!pcm->running_areas);
pcm->running_areas = calloc(pcm->channels, sizeof(pcm->running_areas[0]));
if (!pcm->running_areas) {
free(pcm->mmap_channels);
pcm->mmap_channels = NULL;
return -ENOMEM;
}
for (c = 0; c < pcm->channels; ++c) {
snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
i->channel = c;
err = snd_pcm_channel_info(pcm, i);
if (err < 0)
return err;
}
for (c = 0; c < pcm->channels; ++c) {
snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
snd_pcm_channel_area_t *a = &pcm->running_areas[c];
unsigned int c1;
if (!i->addr) {
char *ptr;
size_t size = i->first + i->step * pcm->buffer_size;
for (c1 = c + 1; c1 < pcm->channels; ++c1) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
size_t s;
if (i1->type != i->type)
continue;
switch (i1->type) {
case SND_PCM_AREA_MMAP:
if (i1->u.mmap.fd != i->u.mmap.fd ||
i1->u.mmap.offset != i->u.mmap.offset)
continue;
break;
case SND_PCM_AREA_SHM:
if (i1->u.shm.shmid != i->u.shm.shmid)
continue;
break;
default:
assert(0);
}
s = i1->first + i1->step * pcm->buffer_size;
if (s > size)
size = s;
}
size = (size + 7) / 8;
size = PAGE_ALIGN(size);
switch (i->type) {
case SND_PCM_AREA_MMAP:
ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, i->u.mmap.fd, i->u.mmap.offset);
if (ptr == MAP_FAILED) {
SYSERR("mmap failed");
return -errno;
}
i->addr = ptr;
break;
case SND_PCM_AREA_SHM:
if (i->u.shm.shmid < 0) {
int id;
id = shmget(IPC_PRIVATE, size, 0666);
if (id < 0) {
SYSERR("shmget failed");
return -errno;
}
i->u.shm.shmid = id;
}
ptr = shmat(i->u.shm.shmid, 0, 0);
if (ptr == (void*) -1) {
SYSERR("shmat failed");
return -errno;
}
i->addr = ptr;
break;
default:
assert(0);
}
}
for (c1 = c + 1; c1 < pcm->channels; ++c1) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
if (i1->type != i->type)
continue;
switch (i1->type) {
case SND_PCM_AREA_MMAP:
if (i1->u.mmap.fd != i->u.mmap.fd ||
i1->u.mmap.offset != i->u.mmap.offset)
continue;
break;
case SND_PCM_AREA_SHM:
if (i1->u.shm.shmid != i->u.shm.shmid)
continue;
break;
default:
assert(0);
}
i1->addr = i->addr;
}
a->addr = i->addr;
a->first = i->first;
a->step = i->step;
}
return 0;
}
int snd_pcm_munmap(snd_pcm_t *pcm)
{
int err;
unsigned int c;
assert(pcm);
assert(pcm->mmap_info);
if ((err = pcm->ops->munmap(pcm->op_arg)) < 0)
assert(pcm->mmap_channels);
for (c = 0; c < pcm->channels; ++c) {
snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
unsigned int c1;
size_t size = i->first + i->step * pcm->buffer_size;
if (!i->addr)
continue;
for (c1 = c + 1; c1 < pcm->channels; ++c1) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
size_t s;
if (i1->addr != i->addr)
continue;
i1->addr = NULL;
s = i1->first + i1->step * pcm->buffer_size;
if (s > size)
size = s;
}
size = (size + 7) / 8;
size = PAGE_ALIGN(size);
switch (i->type) {
case SND_PCM_AREA_MMAP:
err = munmap(i->addr, size);
if (err < 0) {
SYSERR("mmap failed");
return -errno;
}
break;
case SND_PCM_AREA_SHM:
err = shmdt(i->addr);
if (err < 0) {
SYSERR("shmdt failed");
return -errno;
}
break;
default:
assert(0);
}
i->addr = NULL;
}
err = pcm->ops->munmap(pcm);
if (err < 0)
return err;
free(pcm->stopped_areas);
free(pcm->running_areas);
pcm->stopped_areas = 0;
pcm->running_areas = 0;
free(pcm->mmap_channels);
pcm->mmap_channels = 0;
return 0;
}
@ -267,25 +412,34 @@ ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size)
while (xfer < size) {
size_t frames = size - xfer;
size_t offset = snd_pcm_mmap_hw_offset(pcm);
size_t cont = pcm->setup.buffer_size - offset;
size_t cont = pcm->buffer_size - offset;
if (cont < frames)
frames = cont;
if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) {
switch (pcm->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
{
snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
char *buf = snd_pcm_channel_area_addr(a, offset);
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED);
err = _snd_pcm_writei(pcm, buf, size);
} else {
size_t channels = pcm->setup.format.channels;
break;
}
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
{
size_t channels = pcm->channels;
unsigned int c;
void *bufs[channels];
snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED);
for (c = 0; c < channels; ++c) {
snd_pcm_channel_area_t *a = &areas[c];
bufs[c] = snd_pcm_channel_area_addr(a, offset);
}
err = _snd_pcm_writen(pcm, bufs, size);
break;
}
default:
assert(0);
err = -EINVAL;
break;
}
if (err < 0)
break;
@ -304,26 +458,34 @@ ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size)
while (xfer < size) {
size_t frames = size - xfer;
size_t offset = snd_pcm_mmap_hw_offset(pcm);
size_t cont = pcm->setup.buffer_size - offset;
size_t cont = pcm->buffer_size - offset;
if (cont < frames)
frames = cont;
if (pcm->setup.xfer_mode == SND_PCM_XFER_INTERLEAVED) {
switch (pcm->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
{
snd_pcm_channel_area_t *a = snd_pcm_mmap_areas(pcm);
char *buf = snd_pcm_channel_area_addr(a, offset);
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED);
err = _snd_pcm_readi(pcm, buf, size);
} else {
size_t channels = pcm->setup.format.channels;
break;
}
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
{
size_t channels = pcm->channels;
unsigned int c;
void *bufs[channels];
snd_pcm_channel_area_t *areas = snd_pcm_mmap_areas(pcm);
assert(pcm->setup.mmap_shape == SND_PCM_MMAP_NONINTERLEAVED);
for (c = 0; c < channels; ++c) {
snd_pcm_channel_area_t *a = &areas[c];
bufs[c] = snd_pcm_channel_area_addr(a, offset);
}
err = _snd_pcm_readn(pcm->fast_op_arg, bufs, size);
}
default:
assert(0);
err = -EINVAL;
break;
}
if (err < 0)
break;
xfer += frames;

View file

@ -35,8 +35,6 @@ typedef struct {
int getput_idx;
mulaw_f func;
int sformat;
int cformat;
int cxfer_mode, cmmap_shape;
} snd_pcm_mulaw_t;
static inline int val_seg(int val)
@ -230,96 +228,76 @@ static void mulaw_encode(snd_pcm_channel_area_t *src_areas,
}
}
static int snd_pcm_mulaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_mulaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_mulaw_t *mulaw = pcm->private;
unsigned int req_mask = info->req_mask;
unsigned int sfmt = info->req.format.sfmt;
unsigned int format_mask, access_mask;
int err;
if (req_mask & SND_PCM_PARAMS_SFMT) {
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ?
!snd_pcm_format_linear(sfmt) :
sfmt != SND_PCM_SFMT_MU_LAW) {
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
access_mask = info->access_mask;
if (access_mask == 0)
return -EINVAL;
if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW)
info->format_mask &= SND_PCM_FMTBIT_LINEAR;
else
info->format_mask &= SND_PCM_FMTBIT_MU_LAW;
format_mask = info->format_mask;
if (format_mask == 0)
return -EINVAL;
info->format_mask = 1U << mulaw->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(mulaw->plug.slave, info);
if (info->format_mask)
info->format_mask = format_mask;
if (info->access_mask) {
mulaw->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
info->req_mask |= SND_PCM_PARAMS_SFMT;
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
SND_PCM_PARAMS_XFER_MODE);
info->req.format.sfmt = mulaw->sformat;
err = snd_pcm_params_info(mulaw->plug.slave, info);
info->req_mask = req_mask;
info->req.format.sfmt = sfmt;
if (err < 0)
return err;
if (req_mask & SND_PCM_PARAMS_SFMT)
info->formats = 1 << sfmt;
else
info->formats = mulaw->sformat == SND_PCM_SFMT_MU_LAW ?
SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_MU_LAW;
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info);
return 0;
}
static int snd_pcm_mulaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_mulaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_mulaw_t *mulaw = pcm->private;
snd_pcm_t *slave = mulaw->plug.slave;
unsigned int format, access;
int err;
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW ?
!snd_pcm_format_linear(params->format.sfmt) :
params->format.sfmt != SND_PCM_SFMT_MU_LAW) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
mulaw->cformat = params->format.sfmt;
mulaw->cxfer_mode = params->xfer_mode;
mulaw->cmmap_shape = params->mmap_shape;
params->format.sfmt = mulaw->sformat;
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
err = snd_pcm_params_mmap(slave, params);
params->format.sfmt = mulaw->cformat;
params->xfer_mode = mulaw->cxfer_mode;
params->mmap_shape = mulaw->cmmap_shape;
return err;
}
static int snd_pcm_mulaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
{
snd_pcm_mulaw_t *mulaw = pcm->private;
int err = snd_pcm_setup(mulaw->plug.slave, setup);
format = params->format;
access = params->access;
params->format = mulaw->sformat;
if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0)
return err;
assert(mulaw->sformat == setup->format.sfmt);
if (mulaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
setup->xfer_mode = mulaw->cxfer_mode;
if (mulaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
else
setup->mmap_shape = mulaw->cmmap_shape;
setup->format.sfmt = mulaw->cformat;
setup->mmap_bytes = 0;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) {
mulaw->getput_idx = get_index(mulaw->cformat, SND_PCM_SFMT_S16);
if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) {
mulaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16);
mulaw->func = mulaw_encode;
} else {
mulaw->getput_idx = put_index(SND_PCM_SFMT_S16, mulaw->sformat);
mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, mulaw->sformat);
mulaw->func = mulaw_decode;
}
} else {
if (mulaw->sformat == SND_PCM_SFMT_MU_LAW) {
mulaw->getput_idx = put_index(SND_PCM_SFMT_S16, mulaw->cformat);
if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) {
mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format);
mulaw->func = mulaw_decode;
} else {
mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_SFMT_S16);
mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_FORMAT_S16);
mulaw->func = mulaw_encode;
}
}
@ -343,7 +321,7 @@ static ssize_t snd_pcm_mulaw_write_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
mulaw->func(areas, offset,
snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
frames, pcm->setup.format.channels,
frames, pcm->channels,
mulaw->getput_idx);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
@ -378,7 +356,7 @@ static ssize_t snd_pcm_mulaw_read_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
mulaw->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
areas, offset,
frames, pcm->setup.format.channels,
frames, pcm->channels,
mulaw->getput_idx);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
@ -401,7 +379,7 @@ static void snd_pcm_mulaw_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_mulaw_t *mulaw = pcm->private;
fprintf(fp, "Mu-Law conversion PCM (%s)\n",
snd_pcm_format_name(mulaw->sformat));
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -412,12 +390,12 @@ static void snd_pcm_mulaw_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_mulaw_ops = {
close: snd_pcm_plugin_close,
info: snd_pcm_plugin_info,
params_info: snd_pcm_mulaw_params_info,
params: snd_pcm_mulaw_params,
setup: snd_pcm_mulaw_setup,
hw_info: snd_pcm_mulaw_hw_info,
hw_params: snd_pcm_mulaw_hw_params,
sw_params: snd_pcm_plugin_sw_params,
dig_info: snd_pcm_plugin_dig_info,
dig_params: snd_pcm_plugin_dig_params,
channel_info: snd_pcm_plugin_channel_info,
channel_params: snd_pcm_plugin_channel_params,
channel_setup: snd_pcm_plugin_channel_setup,
dump: snd_pcm_mulaw_dump,
nonblock: snd_pcm_plugin_nonblock,
async: snd_pcm_plugin_async,
@ -431,7 +409,7 @@ int snd_pcm_mulaw_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *sla
snd_pcm_mulaw_t *mulaw;
assert(pcmp && slave);
if (snd_pcm_format_linear(sformat) != 1 &&
sformat != SND_PCM_SFMT_MU_LAW)
sformat != SND_PCM_FORMAT_MU_LAW)
return -EINVAL;
mulaw = calloc(1, sizeof(snd_pcm_mulaw_t));
if (!mulaw) {
@ -498,7 +476,7 @@ int _snd_pcm_mulaw_open(snd_pcm_t **pcmp, char *name,
if (sformat < 0)
return -EINVAL;
if (snd_pcm_format_linear(sformat) != 1 &&
sformat != SND_PCM_SFMT_MU_LAW)
sformat != SND_PCM_FORMAT_MU_LAW)
return -EINVAL;
continue;
}

View file

@ -31,6 +31,7 @@ typedef struct {
snd_pcm_t *pcm;
unsigned int channels_count;
int close_slave;
unsigned int access_mask;
} snd_pcm_multi_slave_t;
typedef struct {
@ -43,7 +44,6 @@ typedef struct {
snd_pcm_multi_slave_t *slaves;
size_t channels_count;
snd_pcm_multi_channel_t *channels;
int xfer_mode;
} snd_pcm_multi_t;
static int snd_pcm_multi_close(snd_pcm_t *pcm)
@ -91,178 +91,106 @@ static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
return 0;
}
static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info)
static int snd_pcm_multi_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int i;
int err;
snd_pcm_t *slave_0 = multi->slaves[0].pcm;
unsigned int req_mask = info->req_mask;
unsigned int channels = info->req.format.channels;
if ((req_mask & SND_PCM_PARAMS_CHANNELS) &&
channels != multi->channels_count) {
info->req.fail_mask |= SND_PCM_PARAMS_CHANNELS;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
info->req_mask |= SND_PCM_PARAMS_CHANNELS;
info->req.format.channels = multi->slaves[0].channels_count;
err = snd_pcm_params_info(slave_0, info);
info->req_mask = req_mask;
info->req.format.channels = channels;
if (err < 0)
return err;
info->min_channels = multi->channels_count;
info->max_channels = multi->channels_count;
for (i = 1; i < multi->slaves_count; ++i) {
snd_pcm_t *slave_i = multi->slaves[i].pcm;
snd_pcm_params_info_t info_i;
info_i = *info;
info_i.req_mask |= SND_PCM_PARAMS_CHANNELS;
info_i.req.format.channels = multi->slaves[i].channels_count;
err = snd_pcm_params_info(slave_i, &info_i);
if (err < 0)
return err;
info->formats &= info_i.formats;
info->rates &= info_i.rates;
if (info_i.min_rate > info->min_rate)
info->min_rate = info_i.min_rate;
if (info_i.max_rate < info->max_rate)
info->max_rate = info_i.max_rate;
if (info_i.buffer_size < info->buffer_size)
info->buffer_size = info_i.buffer_size;
if (info_i.min_fragment_size > info->min_fragment_size)
info->min_fragment_size = info_i.min_fragment_size;
if (info_i.max_fragment_size < info->max_fragment_size)
info->max_fragment_size = info_i.max_fragment_size;
if (info_i.min_fragments > info->min_fragments)
info->min_fragments = info_i.min_fragments;
if (info_i.max_fragments < info->max_fragments)
info->max_fragments = info_i.max_fragments;
info->flags &= info_i.flags;
}
if (info->flags & SND_PCM_INFO_INTERLEAVED) {
if (multi->slaves_count > 0) {
info->flags &= ~SND_PCM_INFO_INTERLEAVED;
info->flags |= SND_PCM_INFO_COMPLEX;
}
} else if (!(info->flags & SND_PCM_INFO_NONINTERLEAVED))
info->flags |= SND_PCM_INFO_COMPLEX;
info->req_mask = req_mask;
return 0;
}
static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int i;
size_t count = 0;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
snd_pcm_setup_t *setup;
int err = snd_pcm_mmap(slave);
if (err < 0)
return err;
count += slave->mmap_info_count;
setup = &slave->setup;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
snd_pcm_channel_area_t r[setup->format.channels];
snd_pcm_channel_area_t s[setup->format.channels];
err = snd_pcm_mmap_get_areas(slave, s, r);
if (err < 0)
return err;
err = snd_pcm_areas_silence(s, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt);
if (err < 0)
return err;
err = snd_pcm_areas_silence(r, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt);
if (err < 0)
return err;
}
}
pcm->mmap_info_count = count;
pcm->mmap_info = malloc(count * sizeof(*pcm->mmap_info));
count = 0;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
memcpy(&pcm->mmap_info[count], slave->mmap_info, slave->mmap_info_count * sizeof(*pcm->mmap_info));
count += slave->mmap_info_count;
}
return 0;
}
static int snd_pcm_multi_munmap(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
int err = snd_pcm_munmap(slave);
if (err < 0)
return err;
}
pcm->mmap_info_count = 0;
free(pcm->mmap_info);
pcm->mmap_info = 0;
return 0;
}
static int snd_pcm_multi_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int i;
snd_pcm_params_t p;
unsigned int k;
snd_pcm_hw_info_t i;
unsigned int access_mask = ~0;
int err = 0;
if (params->format.channels != multi->channels_count) {
params->fail_mask = SND_PCM_PARAMS_CHANNELS;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
if (info->channels_min < multi->channels_count)
info->channels_min = multi->channels_count;
if (info->channels_max > multi->channels_count)
info->channels_max = multi->channels_count;
if (info->channels_max > info->channels_max)
return -EINVAL;
i = *info;
for (k = 0; k < multi->slaves_count; ++k) {
snd_pcm_t *slave = multi->slaves[k].pcm;
i.access_mask = SND_PCM_ACCBIT_MMAP;
i.channels_min = i.channels_max = multi->slaves[k].channels_count;
err = snd_pcm_hw_info(slave, &i);
access_mask &= i.access_mask;
if (err < 0)
break;
multi->slaves[k].access_mask = i.access_mask;
}
*info = i;
if (i.channels_min <= i.channels_max)
info->channels_min = info->channels_max = multi->channels_count;
if (i.access_mask) {
if (!(access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) ||
multi->slaves_count > 1)
info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED;
if (!(access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED))
info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED;
}
return err;
}
static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int i;
snd_pcm_hw_params_t p;
int err;
if (params->channels != multi->channels_count) {
params->fail_mask = SND_PCM_HW_PARBIT_CHANNELS;
return -EINVAL;
}
p = *params;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
p.format.channels = multi->slaves[i].channels_count;
err = snd_pcm_params_mmap(slave, &p);
if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
p.access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
p.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
p.channels = multi->slaves[i].channels_count;
err = snd_pcm_hw_params(slave, &p);
if (err < 0) {
params->fail_mask = p.fail_mask;
params->fail_reason = p.fail_reason;
break;
return err;
}
err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
if (err < 0)
return err;
if (slave->stopped_areas) {
err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
if (err < 0)
return err;
}
}
if (err == 0)
multi->xfer_mode = params->xfer_mode;
return err;
return 0;
}
static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int i;
int err;
err = snd_pcm_setup(multi->slaves[0].pcm, setup);
if (err < 0)
return err;
for (i = 1; i < multi->slaves_count; ++i) {
snd_pcm_setup_t s;
snd_pcm_t *sh = multi->slaves[i].pcm;
err = snd_pcm_setup(sh, &s);
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
err = snd_pcm_sw_params(slave, params);
if (err < 0)
return err;
if (setup->format.rate != s.format.rate)
return -EINVAL;
if (setup->buffer_size != s.buffer_size)
return -EINVAL;
if (setup->mmap_shape != SND_PCM_MMAP_NONINTERLEAVED ||
s.mmap_shape != SND_PCM_MMAP_NONINTERLEAVED)
setup->mmap_shape = SND_PCM_MMAP_COMPLEX;
}
setup->format.channels = multi->channels_count;
if (multi->xfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
setup->xfer_mode = multi->xfer_mode;
return 0;
}
static int snd_pcm_multi_dig_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_info_t *info ATTRIBUTE_UNUSED)
{
/* FIXME */
return -ENOSYS;
}
static int snd_pcm_multi_dig_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_params_t *params ATTRIBUTE_UNUSED)
{
/* FIXME */
return -ENOSYS;
}
static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
{
snd_pcm_multi_t *multi = pcm->private;
@ -335,34 +263,6 @@ static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *in
return err;
}
static int snd_pcm_multi_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int channel = params->channel;
snd_pcm_multi_channel_t *c = &multi->channels[channel];
int err;
if (c->slave_idx < 0)
return -ENXIO;
params->channel = c->slave_channel;
err = snd_pcm_channel_params(multi->slaves[c->slave_idx].pcm, params);
params->channel = channel;
return err;
}
static int snd_pcm_multi_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
{
snd_pcm_multi_t *multi = pcm->private;
unsigned int channel = setup->channel;
snd_pcm_multi_channel_t *c = &multi->channels[channel];
int err;
if (c->slave_idx < 0)
return -ENXIO;
setup->channel = c->slave_channel;
err = snd_pcm_channel_setup(multi->slaves[c->slave_idx].pcm, setup);
setup->channel = channel;
return err;
}
static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames)
{
snd_pcm_multi_t *multi = pcm->private;
@ -413,6 +313,16 @@ static int snd_pcm_multi_set_avail_min(snd_pcm_t *pcm, size_t frames)
return snd_pcm_set_avail_min(multi->slaves[0].pcm, frames);
}
static int snd_pcm_multi_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_multi_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private;
@ -433,7 +343,7 @@ static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp)
fprintf(fp, "%d: slave %d, channel %d\n",
k, c->slave_idx, c->slave_channel);
}
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "\nIts setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -446,12 +356,12 @@ static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_multi_ops = {
close: snd_pcm_multi_close,
info: snd_pcm_multi_info,
params_info: snd_pcm_multi_params_info,
params: snd_pcm_multi_params,
setup: snd_pcm_multi_setup,
hw_info: snd_pcm_multi_hw_info,
hw_params: snd_pcm_multi_hw_params,
sw_params: snd_pcm_multi_sw_params,
dig_info: snd_pcm_multi_dig_info,
dig_params: snd_pcm_multi_dig_params,
channel_info: snd_pcm_multi_channel_info,
channel_params: snd_pcm_multi_channel_params,
channel_setup: snd_pcm_multi_channel_setup,
dump: snd_pcm_multi_dump,
nonblock: snd_pcm_multi_nonblock,
async: snd_pcm_multi_async,
@ -538,7 +448,7 @@ int snd_pcm_multi_open(snd_pcm_t **pcmp, char *name,
pcm->type = SND_PCM_TYPE_MULTI;
pcm->stream = stream;
pcm->mode = multi->slaves[0].pcm->mode;
pcm->mmap_auto = 1;
pcm->mmap_rw = 1;
pcm->ops = &snd_pcm_multi_ops;
pcm->op_arg = pcm;
pcm->fast_ops = &snd_pcm_multi_fast_ops;

View file

@ -21,13 +21,14 @@
#include <byteswap.h>
#include <limits.h>
#include <sys/shm.h>
#include "pcm_local.h"
#include "pcm_plugin.h"
typedef struct {
snd_pcm_setup_t setup;
snd_timestamp_t trigger_time;
int state;
int shmid;
size_t appl_ptr;
size_t hw_ptr;
int poll_fd;
@ -58,25 +59,10 @@ static int snd_pcm_null_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_info_t * i
return 0;
}
static int snd_pcm_null_channel_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_info_t * info)
static int snd_pcm_null_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
{
int channel = info->channel;
memset(info, 0, sizeof(*info));
info->channel = channel;
return 0;
}
static int snd_pcm_null_channel_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_params_t * params ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_null_channel_setup(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_channel_setup_t * setup)
{
int channel = setup->channel;
memset(setup, 0, sizeof(*setup));
setup->channel = channel;
return 0;
snd_pcm_null_t *null = pcm->private;
return snd_pcm_channel_info_shm(pcm, info, null->shmid);
}
static int snd_pcm_null_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
@ -86,7 +72,7 @@ static int snd_pcm_null_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
status->state = null->state;
status->trigger_time = null->trigger_time;
gettimeofday(&status->tstamp, 0);
status->avail = pcm->setup.buffer_size;
status->avail = pcm->buffer_size;
status->avail_max = status->avail;
return 0;
}
@ -118,7 +104,7 @@ static int snd_pcm_null_start(snd_pcm_t *pcm)
assert(null->state == SND_PCM_STATE_PREPARED);
null->state = SND_PCM_STATE_RUNNING;
if (pcm->stream == SND_PCM_STREAM_CAPTURE)
snd_pcm_mmap_appl_forward(pcm, pcm->setup.buffer_size);
snd_pcm_mmap_appl_forward(pcm, pcm->buffer_size);
return 0;
}
@ -182,7 +168,7 @@ static ssize_t snd_pcm_null_writei(snd_pcm_t *pcm, const void *buffer ATTRIBUTE_
{
snd_pcm_null_t *null = pcm->private;
if (null->state == SND_PCM_STATE_PREPARED &&
pcm->setup.start_mode != SND_PCM_START_EXPLICIT) {
pcm->start_mode != SND_PCM_START_EXPLICIT) {
null->state = SND_PCM_STATE_RUNNING;
}
return snd_pcm_null_fwd(pcm, size);
@ -192,7 +178,7 @@ static ssize_t snd_pcm_null_writen(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED,
{
snd_pcm_null_t *null = pcm->private;
if (null->state == SND_PCM_STATE_PREPARED &&
pcm->setup.start_mode != SND_PCM_START_EXPLICIT) {
pcm->start_mode != SND_PCM_START_EXPLICIT) {
null->state = SND_PCM_STATE_RUNNING;
}
return snd_pcm_null_fwd(pcm, size);
@ -202,9 +188,9 @@ static ssize_t snd_pcm_null_readi(snd_pcm_t *pcm, void *buffer ATTRIBUTE_UNUSED,
{
snd_pcm_null_t *null = pcm->private;
if (null->state == SND_PCM_STATE_PREPARED &&
pcm->setup.start_mode != SND_PCM_START_EXPLICIT) {
pcm->start_mode != SND_PCM_START_EXPLICIT) {
null->state = SND_PCM_STATE_RUNNING;
snd_pcm_mmap_hw_forward(pcm, pcm->setup.buffer_size);
snd_pcm_mmap_hw_forward(pcm, pcm->buffer_size);
}
return snd_pcm_null_fwd(pcm, size);
}
@ -213,9 +199,9 @@ static ssize_t snd_pcm_null_readn(snd_pcm_t *pcm, void **bufs ATTRIBUTE_UNUSED,
{
snd_pcm_null_t *null = pcm->private;
if (null->state == SND_PCM_STATE_PREPARED &&
pcm->setup.start_mode != SND_PCM_START_EXPLICIT) {
pcm->start_mode != SND_PCM_START_EXPLICIT) {
null->state = SND_PCM_STATE_RUNNING;
snd_pcm_mmap_hw_forward(pcm, pcm->setup.buffer_size);
snd_pcm_mmap_hw_forward(pcm, pcm->buffer_size);
}
return snd_pcm_null_fwd(pcm, size);
}
@ -227,111 +213,83 @@ static ssize_t snd_pcm_null_mmap_forward(snd_pcm_t *pcm, size_t size)
static ssize_t snd_pcm_null_avail_update(snd_pcm_t *pcm)
{
return pcm->setup.buffer_size;
return pcm->buffer_size;
}
static int snd_pcm_null_set_avail_min(snd_pcm_t *pcm, size_t frames)
{
pcm->setup.buffer_size = frames;
pcm->avail_min = frames;
return 0;
}
static int snd_pcm_null_hw_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_info_t * info)
{
snd_pcm_hw_info_complete(info);
info->fifo_size = 0;
return 0;
}
static int snd_pcm_null_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t * params ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params)
{
if (params->start_mode > SND_PCM_START_LAST) {
params->fail_mask = SND_PCM_SW_PARBIT_START_MODE;
return -EINVAL;
}
if (params->ready_mode > SND_PCM_READY_LAST) {
params->fail_mask = SND_PCM_SW_PARBIT_READY_MODE;
return -EINVAL;
}
if (params->xrun_mode > SND_PCM_XRUN_LAST) {
params->fail_mask = SND_PCM_SW_PARBIT_XRUN_MODE;
return -EINVAL;
}
return 0;
}
static int snd_pcm_null_dig_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_params_t *params ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_null_dig_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_dig_info_t *info ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_null_mmap(snd_pcm_t *pcm)
{
snd_pcm_mmap_info_t *i;
int err;
i = calloc(1, sizeof(*i));
if (!i)
return -ENOMEM;
err = snd_pcm_alloc_user_mmap(pcm, i);
if (err < 0) {
free(i);
return err;
snd_pcm_null_t *null = pcm->private;
if (!(pcm->info & SND_PCM_INFO_MMAP)) {
size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size);
int id = shmget(IPC_PRIVATE, size, 0666);
if (id < 0) {
SYSERR("shmget failed");
return -errno;
}
null->shmid = id;
}
pcm->mmap_info = i;
pcm->mmap_info_count = 1;
return 0;
}
static int snd_pcm_null_munmap(snd_pcm_t *pcm)
{
int err = snd_pcm_free_mmap(pcm, pcm->mmap_info);
if (err < 0)
return err;
free(pcm->mmap_info);
pcm->mmap_info_count = 0;
pcm->mmap_info = 0;
return 0;
}
static int snd_pcm_null_params_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_params_info_t * info)
{
int sizes = ((info->req_mask & SND_PCM_PARAMS_SFMT) &&
(info->req_mask & SND_PCM_PARAMS_CHANNELS));
info->flags = SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID |
SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED |
SND_PCM_INFO_PAUSE;
info->formats = ~0;
info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
info->min_rate = 4000;
info->max_rate = 192000;
info->min_channels = 1;
info->max_channels = 32;
info->min_fragments = 1;
info->max_fragments = 1024 * 1024;
if (sizes) {
info->buffer_size = 1024 * 1024;
info->min_fragment_size = 1;
info->max_fragment_size = 1024 * 1024;
info->fragment_align = 1;
}
return 0;
}
static int snd_pcm_null_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
{
snd_pcm_null_t *null = pcm->private;
snd_pcm_setup_t *s = &null->setup;
int w = snd_pcm_format_width(s->format.sfmt);
if (w < 0) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
return -EINVAL;
if (shmctl(null->shmid, IPC_RMID, 0) < 0) {
SYSERR("shmctl IPC_RMID failed");
return -errno;
}
s->msbits = w;
s->format = params->format;
s->start_mode = params->start_mode;
s->ready_mode = params->ready_mode;
s->avail_min = params->avail_min;
s->xfer_mode = params->xfer_mode;
s->xfer_min = params->xfer_min;
s->xfer_align = params->xfer_align;
s->xrun_mode = params->xrun_mode;
s->mmap_shape = params->mmap_shape;
s->frag_size = params->frag_size;
s->frags = s->buffer_size / s->frag_size;
if (s->frags < 1)
s->frags = 1;
s->buffer_size = s->frag_size * s->frags;
s->boundary = LONG_MAX - LONG_MAX % s->buffer_size;
s->time = params->time;
s->rate_master = s->format.rate;
s->rate_divisor = 1;
s->mmap_bytes = 0;
s->fifo_size = 1;
return 0;
}
static int snd_pcm_null_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
{
snd_pcm_null_t *null = pcm->private;
*setup = null->setup;
return 0;
}
static void snd_pcm_null_dump(snd_pcm_t *pcm, FILE *fp)
{
fprintf(fp, "Null PCM\n");
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -340,12 +298,12 @@ static void snd_pcm_null_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_null_ops = {
close: snd_pcm_null_close,
info: snd_pcm_null_info,
params_info: snd_pcm_null_params_info,
params: snd_pcm_null_params,
setup: snd_pcm_null_setup,
hw_info: snd_pcm_null_hw_info,
hw_params: snd_pcm_null_hw_params,
sw_params: snd_pcm_null_sw_params,
dig_params: snd_pcm_null_dig_params,
dig_info: snd_pcm_null_dig_info,
channel_info: snd_pcm_null_channel_info,
channel_params: snd_pcm_null_channel_params,
channel_setup: snd_pcm_null_channel_setup,
dump: snd_pcm_null_dump,
nonblock: snd_pcm_null_nonblock,
async: snd_pcm_null_async,

View file

@ -32,37 +32,25 @@ typedef struct {
} snd_pcm_plug_t;
unsigned int snd_pcm_plug_formats(unsigned int formats)
{
int fmts = (SND_PCM_LINEAR_FORMATS | SND_PCM_FMT_MU_LAW |
SND_PCM_FMT_A_LAW | SND_PCM_FMT_IMA_ADPCM);
if (formats & fmts)
formats |= fmts;
return formats;
}
static int preferred_formats[] = {
SND_PCM_SFMT_S16_LE,
SND_PCM_SFMT_S16_BE,
SND_PCM_SFMT_U16_LE,
SND_PCM_SFMT_U16_BE,
SND_PCM_SFMT_S24_LE,
SND_PCM_SFMT_S24_BE,
SND_PCM_SFMT_U24_LE,
SND_PCM_SFMT_U24_BE,
SND_PCM_SFMT_S32_LE,
SND_PCM_SFMT_S32_BE,
SND_PCM_SFMT_U32_LE,
SND_PCM_SFMT_U32_BE,
SND_PCM_SFMT_S8,
SND_PCM_SFMT_U8
SND_PCM_FORMAT_S16_LE,
SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U16_LE,
SND_PCM_FORMAT_U16_BE,
SND_PCM_FORMAT_S24_LE,
SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_U24_LE,
SND_PCM_FORMAT_U24_BE,
SND_PCM_FORMAT_S32_LE,
SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_U32_LE,
SND_PCM_FORMAT_U32_BE,
SND_PCM_FORMAT_S8,
SND_PCM_FORMAT_U8
};
static int snd_pcm_plug_slave_fmt(int format,
snd_pcm_params_info_t *slave_info)
static int snd_pcm_plug_slave_fmt(int format, unsigned int format_mask)
{
if ((snd_pcm_plug_formats(slave_info->formats) & (1 << format)) == 0)
return -EINVAL;
if (snd_pcm_format_linear(format)) {
int width = snd_pcm_format_width(format);
int unsignd = snd_pcm_format_unsigned(format);
@ -77,8 +65,8 @@ static int snd_pcm_plug_slave_fmt(int format,
for (sgn = 0; sgn < 2; ++sgn) {
format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
if (format1 >= 0 &&
slave_info->formats & (1 << format1))
goto _found;
format_mask & (1U << format1))
return format1;
unsignd1 = !unsignd1;
}
big1 = !big1;
@ -89,80 +77,24 @@ static int snd_pcm_plug_slave_fmt(int format,
}
width1 += dwidth1;
}
return -EINVAL;
_found:
return format1;
return ffs(format_mask) - 1;
} else {
unsigned int i;
switch (format) {
case SND_PCM_SFMT_MU_LAW:
case SND_PCM_SFMT_A_LAW:
case SND_PCM_SFMT_IMA_ADPCM:
case SND_PCM_FORMAT_MU_LAW:
case SND_PCM_FORMAT_A_LAW:
case SND_PCM_FORMAT_IMA_ADPCM:
for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
int format1 = preferred_formats[i];
if (slave_info->formats & (1 << format1))
if (format_mask & (1U << format1))
return format1;
}
default:
return -EINVAL;
return ffs(format_mask) - 1;
}
}
}
struct {
unsigned int rate;
unsigned int flag;
} snd_pcm_rates[] = {
{ 8000, SND_PCM_RATE_8000 },
{ 11025, SND_PCM_RATE_11025 },
{ 16000, SND_PCM_RATE_16000 },
{ 22050, SND_PCM_RATE_22050 },
{ 32000, SND_PCM_RATE_32000 },
{ 44100, SND_PCM_RATE_44100 },
{ 48000, SND_PCM_RATE_48000 },
{ 88200, SND_PCM_RATE_88200 },
{ 96000, SND_PCM_RATE_96000 },
{ 176400, SND_PCM_RATE_176400 },
{ 192000, SND_PCM_RATE_192000 }
};
static int snd_pcm_plug_slave_rate(unsigned int rate,
snd_pcm_params_info_t *slave_info)
{
if (rate <= slave_info->min_rate)
return slave_info->min_rate;
else if (rate >= slave_info->max_rate)
return slave_info->max_rate;
else if (!(slave_info->rates & (SND_PCM_RATE_CONTINUOUS |
SND_PCM_RATE_KNOT))) {
unsigned int k;
unsigned int rate1 = 0, rate2 = 0;
int delta1, delta2;
for (k = 0; k < sizeof(snd_pcm_rates) /
sizeof(snd_pcm_rates[0]); ++k) {
if (!(snd_pcm_rates[k].flag & slave_info->rates))
continue;
if (snd_pcm_rates[k].rate < rate) {
rate1 = snd_pcm_rates[k].rate;
} else if (snd_pcm_rates[k].rate >= rate) {
rate2 = snd_pcm_rates[k].rate;
break;
}
}
if (rate1 == 0)
return rate2;
if (rate2 == 0)
return rate1;
delta1 = rate - rate1;
delta2 = rate2 - rate;
if (delta1 < delta2)
return rate1;
else
return rate2;
}
return rate;
}
static int snd_pcm_plug_close(snd_pcm_t *pcm)
{
snd_pcm_plug_t *plug = pcm->private;
@ -206,97 +138,156 @@ static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
return 0;
}
static int snd_pcm_plug_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info)
static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
{
int err;
snd_pcm_plug_t *plug = pcm->private;
snd_pcm_t *slave = plug->req_slave;
snd_pcm_params_info_t slave_info;
int sformat, srate;
unsigned int schannels;
int crate;
info->req.fail_reason = 0;
info->req.fail_mask = 0;
if (info->req_mask & SND_PCM_PARAMS_RATE) {
info->min_rate = info->req.format.rate;
info->max_rate = info->req.format.rate;
} else {
info->min_rate = 4000;
info->max_rate = 192000;
}
info->rates = SND_PCM_RATE_CONTINUOUS | SND_PCM_RATE_8000_192000;
if (info->req_mask & SND_PCM_PARAMS_CHANNELS) {
info->min_channels = info->req.format.channels;
info->max_channels = info->req.format.channels;
} else {
info->min_channels = 1;
info->max_channels = 32;
}
memset(&slave_info, 0, sizeof(slave_info));
if ((err = snd_pcm_params_info(slave, &slave_info)) < 0)
return err;
info->flags = slave_info.flags;
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
info->min_fragments = slave_info.min_fragments;
info->max_fragments = slave_info.max_fragments;
snd_pcm_hw_info_t sinfo, i;
snd_pcm_hw_params_t sparams;
unsigned int rate_min, rate_max;
unsigned int channels_min, channels_max;
unsigned int format, format_mask;
size_t fragment_size_min, fragment_size_max;
if (info->req_mask & SND_PCM_PARAMS_SFMT)
info->formats = 1 << info->req.format.sfmt;
else {
info->formats = snd_pcm_plug_formats(slave_info.formats);
return 0;
}
sformat = snd_pcm_plug_slave_fmt(info->req.format.sfmt, &slave_info);
if (sformat < 0) {
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
if (info->access_mask == 0)
return -EINVAL;
}
if (!(info->req_mask & SND_PCM_PARAMS_RATE))
return 0;
crate = info->req.format.rate;
srate = snd_pcm_plug_slave_rate(crate, &slave_info);
if (srate < 0) {
info->req.fail_mask = SND_PCM_PARAMS_RATE;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
info->format_mask &= (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW |
SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM);
if (info->format_mask == 0)
return -EINVAL;
}
if (!(info->req_mask & SND_PCM_PARAMS_CHANNELS))
return 0;
schannels = info->req.format.channels;
if (schannels < info->min_channels)
schannels = info->min_channels;
else if (schannels > info->max_channels)
schannels = info->max_channels;
if (info->rate_min < 4000)
info->rate_min = 4000;
if (info->rate_max > 192000)
info->rate_max = 192000;
if (info->rate_max < info->rate_min)
return -EINVAL;
slave_info.req_mask = (SND_PCM_PARAMS_SFMT |
SND_PCM_PARAMS_CHANNELS |
SND_PCM_PARAMS_RATE);
slave_info.req.format.sfmt = sformat;
slave_info.req.format.channels = schannels;
slave_info.req.format.rate = srate;
if ((err = snd_pcm_params_info(slave, &slave_info)) < 0) {
info->req.fail_mask = slave_info.req.fail_mask;
info->req.fail_reason = slave_info.req.fail_reason;
if (info->channels_min < 1)
info->channels_min = 1;
if (info->channels_max > 1024)
info->channels_max = 1024;
if (info->channels_max < info->channels_min)
return -EINVAL;
if (info->fragment_size_max > 1024 * 1024)
info->fragment_size_max = 1024 * 1024;
if (info->fragment_size_max < info->fragment_size_min)
return -EINVAL;
sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW |
SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM);
sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD;
sinfo.channels_min = 1;
sinfo.channels_max = 1024;
sinfo.rate_min = 4000;
sinfo.rate_max = 192000;
sinfo.fragments_min = 1;
sinfo.fragments_max = UINT_MAX;
sinfo.fragment_size_min = 1;
sinfo.fragment_size_max = ULONG_MAX;
/* Formats */
err = snd_pcm_hw_info(slave, &sinfo);
if (err < 0) {
*info = i;
return err;
}
info->buffer_size = muldiv64(slave_info.buffer_size, crate, srate);
info->min_fragment_size = muldiv64(slave_info.min_fragment_size, crate, srate);
info->max_fragment_size = muldiv64(slave_info.max_fragment_size, crate, srate);
info->fragment_align = muldiv64(slave_info.fragment_align, crate, srate);
if (sformat != info->req.format.sfmt ||
(unsigned int) srate != info->req.format.rate ||
schannels != info->req.format.channels)
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
format_mask = 0;
for (format = 0; format < SND_PCM_FORMAT_LAST; ++format) {
if (!(info->format_mask & (1 << format)))
continue;
err = snd_pcm_plug_slave_fmt(format, sinfo.format_mask);
if (err < 0)
info->format_mask &= ~(1 << format);
else
format_mask |= (1 << err);
}
sinfo.format_mask = format_mask;
/* Rate (and fragment_size) */
i = sinfo;
sparams.rate = info->rate_min;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
-1);
if (err < 0) {
*info = i;
return err;
}
rate_min = i.rate_min;
if (info->rate_max != info->rate_min) {
i = sinfo;
sparams.rate = info->rate_max;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
-1);
if (err < 0) {
*info = i;
return err;
}
rate_max = i.rate_min;
} else
rate_max = rate_min;
sinfo.rate_min = rate_min;
sinfo.rate_max = rate_max;
/* Channels */
i = sinfo;
sparams.channels = info->channels_min;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-1);
if (err < 0) {
*info = i;
return err;
}
channels_min = i.channels_min;
if (info->channels_max != info->channels_min) {
i = sinfo;
sparams.channels = info->channels_max;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-1);
if (err < 0) {
*info = i;
return err;
}
channels_max = i.channels_min;
} else
channels_max = channels_min;
sinfo.channels_min = channels_min;
sinfo.channels_max = channels_max;
sinfo.fragments_min = info->fragments_min;
sinfo.fragments_max = info->fragments_max;
sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max);
sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min);
err = snd_pcm_hw_info(slave, &sinfo);
if (err < 0) {
*info = sinfo;
return err;
}
info->subformat_mask = sinfo.subformat_mask;
info->fragments_min = sinfo.fragments_min;
info->fragments_max = sinfo.fragments_max;
fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
if (fragment_size_min > info->fragment_size_min)
info->fragment_size_min = fragment_size_min;
if (fragment_size_max < info->fragment_size_max)
info->fragment_size_max = fragment_size_max;
info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info);
return 0;
}
@ -313,29 +304,29 @@ static void snd_pcm_plug_clear(snd_pcm_t *pcm)
}
}
static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv)
static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv)
{
snd_pcm_plug_t *plug = pcm->private;
int err;
assert(snd_pcm_format_linear(slv->sfmt));
assert(snd_pcm_format_linear(slv->format));
if (clt->rate == slv->rate)
return 0;
err = snd_pcm_rate_open(new, NULL, slv->sfmt, slv->rate, plug->slave, plug->slave != plug->req_slave);
err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->slave, plug->slave != plug->req_slave);
if (err < 0)
return err;
slv->rate = clt->rate;
if (snd_pcm_format_linear(clt->sfmt))
slv->sfmt = clt->sfmt;
if (snd_pcm_format_linear(clt->format))
slv->format = clt->format;
return 1;
}
static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv)
static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv)
{
snd_pcm_plug_t *plug = pcm->private;
unsigned int tt_ssize, tt_cused, tt_sused;
ttable_entry_t *ttable;
int err;
assert(snd_pcm_format_linear(slv->sfmt));
assert(snd_pcm_format_linear(slv->format));
if (clt->channels == slv->channels)
return 0;
if (clt->rate != slv->rate &&
@ -384,100 +375,100 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm
s = 0;
}
}
err = snd_pcm_route_open(new, NULL, slv->sfmt, slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave);
err = snd_pcm_route_open(new, NULL, slv->format, slv->channels, ttable, tt_ssize, tt_cused, tt_sused, plug->slave, plug->slave != plug->req_slave);
if (err < 0)
return err;
slv->channels = clt->channels;
if (snd_pcm_format_linear(clt->sfmt))
slv->sfmt = clt->sfmt;
if (snd_pcm_format_linear(clt->format))
slv->format = clt->format;
return 1;
}
static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *clt, snd_pcm_format_t *slv)
static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv)
{
snd_pcm_plug_t *plug = pcm->private;
int err, cfmt;
int (*f)(snd_pcm_t **pcm, char *name, int sformat, snd_pcm_t *slave, int close_slave);
if (snd_pcm_format_linear(slv->sfmt)) {
if (snd_pcm_format_linear(slv->format)) {
/* Conversion is done in another plugin */
if (clt->sfmt == slv->sfmt ||
if (clt->format == slv->format ||
clt->rate != slv->rate ||
clt->channels != slv->channels)
return 0;
} else {
/* No conversion is needed */
if (clt->sfmt == slv->sfmt &&
if (clt->format == slv->format &&
clt->rate == slv->rate &&
clt->channels == clt->channels)
return 0;
}
if (snd_pcm_format_linear(slv->sfmt)) {
cfmt = clt->sfmt;
switch (clt->sfmt) {
case SND_PCM_SFMT_MU_LAW:
if (snd_pcm_format_linear(slv->format)) {
cfmt = clt->format;
switch (clt->format) {
case SND_PCM_FORMAT_MU_LAW:
f = snd_pcm_mulaw_open;
break;
case SND_PCM_SFMT_A_LAW:
case SND_PCM_FORMAT_A_LAW:
f = snd_pcm_alaw_open;
break;
case SND_PCM_SFMT_IMA_ADPCM:
case SND_PCM_FORMAT_IMA_ADPCM:
f = snd_pcm_adpcm_open;
break;
default:
assert(snd_pcm_format_linear(clt->sfmt));
assert(snd_pcm_format_linear(clt->format));
f = snd_pcm_linear_open;
break;
}
} else {
switch (slv->sfmt) {
case SND_PCM_SFMT_MU_LAW:
switch (slv->format) {
case SND_PCM_FORMAT_MU_LAW:
f = snd_pcm_mulaw_open;
break;
case SND_PCM_SFMT_A_LAW:
case SND_PCM_FORMAT_A_LAW:
f = snd_pcm_alaw_open;
break;
case SND_PCM_SFMT_IMA_ADPCM:
case SND_PCM_FORMAT_IMA_ADPCM:
f = snd_pcm_adpcm_open;
break;
default:
assert(0);
return -EINVAL;
}
if (snd_pcm_format_linear(clt->sfmt))
cfmt = clt->sfmt;
if (snd_pcm_format_linear(clt->format))
cfmt = clt->format;
else
cfmt = SND_PCM_SFMT_S16;
cfmt = SND_PCM_FORMAT_S16;
}
err = f(new, NULL, slv->sfmt, plug->slave, plug->slave != plug->req_slave);
err = f(new, NULL, slv->format, plug->slave, plug->slave != plug->req_slave);
if (err < 0)
return err;
slv->sfmt = cfmt;
slv->format = cfmt;
return 1;
}
static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
snd_pcm_format_t *client_fmt,
snd_pcm_format_t *slave_fmt)
snd_pcm_hw_params_t *client,
snd_pcm_hw_params_t *slave)
{
snd_pcm_plug_t *plug = pcm->private;
int (*funcs[])(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_format_t *s, snd_pcm_format_t *d) = {
int (*funcs[])(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *s, snd_pcm_hw_params_t *d) = {
snd_pcm_plug_change_format,
snd_pcm_plug_change_channels,
snd_pcm_plug_change_rate,
snd_pcm_plug_change_channels,
snd_pcm_plug_change_format
};
snd_pcm_format_t sfmt = *slave_fmt;
snd_pcm_hw_params_t p = *slave;
unsigned int k = 0;
while (1) {
snd_pcm_t *new;
int err;
if (client_fmt->sfmt == sfmt.sfmt &&
client_fmt->channels == sfmt.channels &&
client_fmt->rate == sfmt.rate)
if (client->format == p.format &&
client->channels == p.channels &&
client->rate == p.rate)
return 0;
assert(k < sizeof(funcs)/sizeof(*funcs));
err = funcs[k](pcm, &new, client_fmt, &sfmt);
err = funcs[k](pcm, &new, client, &p);
if (err < 0) {
snd_pcm_plug_clear(pcm);
return err;
@ -493,134 +484,118 @@ static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm,
return 0;
}
static int snd_pcm_plug_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_plug_t *plug = pcm->private;
snd_pcm_t *slave = plug->req_slave;
snd_pcm_format_t *slave_format, *format;
snd_pcm_params_info_t slave_info;
int srate;
snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams;
int err;
memset(&slave_info, 0, sizeof(slave_info));
err = snd_pcm_params_info(slave, &slave_info);
assert(err >= 0);
if (err < 0)
sparams = *params;
snd_pcm_hw_params_to_info(&sparams, &sinfo);
sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW |
SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM);
sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD;
sinfo.channels_min = 1;
sinfo.channels_max = 1024;
sinfo.rate_min = 4000;
sinfo.rate_max = 192000;
sinfo.fragment_size_min = 1;
sinfo.fragment_size_max = ULONG_MAX;
err = snd_pcm_hw_info_rulesv(slave, &sinfo, params,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-1);
if (err < 0) {
snd_pcm_hw_info_to_params_fail(&sinfo, params);
return err;
slave_info.req = *params;
format = &params->format;
slave_format = &slave_info.req.format;
srate = snd_pcm_plug_slave_rate(format->rate, &slave_info);
if (srate < 0) {
params->fail_mask = SND_PCM_PARAMS_RATE;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return srate;
}
slave_format->rate = srate;
slave_info.req_mask |= SND_PCM_PARAMS_RATE;
err = snd_pcm_params_info(slave, &slave_info);
assert(err >= 0);
err = snd_pcm_plug_slave_fmt(sparams.format, sinfo.format_mask);
if (err < 0)
return err;
if (slave_format->rate - slave_info.min_rate < slave_info.max_rate - slave_format->rate)
slave_format->rate = slave_info.min_rate;
sparams.format = err;
sinfo.format_mask = 1U << err;
sparams.fragment_size = muldiv_near(params->fragment_size, sparams.rate, params->rate);
err = snd_pcm_hw_info_rulesv(slave, &sinfo, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE,
-1);
if (err < 0) {
snd_pcm_hw_info_to_params_fail(&sinfo, params);
return err;
}
if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
sparams.access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
sparams.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
slave_format->rate = slave_info.max_rate;
assert(0);
if (format->channels < slave_info.min_channels)
slave_format->channels = slave_info.min_channels;
else if (format->channels > slave_info.max_channels)
slave_format->channels = slave_info.max_channels;
slave_info.req_mask |= SND_PCM_PARAMS_CHANNELS;
err = snd_pcm_params_info(slave, &slave_info);
assert(err >= 0);
err = snd_pcm_plug_insert_plugins(pcm, params, &sparams);
if (err < 0)
return err;
if ((slave_info.formats & (1 << format->sfmt)) == 0) {
int slave_fmt = snd_pcm_plug_slave_fmt(format->sfmt, &slave_info);
if (slave_fmt < 0) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return slave_fmt;
}
slave_format->sfmt = slave_fmt;
}
slave_info.req_mask |= SND_PCM_PARAMS_SFMT;
if (slave_info.formats != 1U << slave_format->sfmt) {
err = snd_pcm_params_info(slave, &slave_info);
assert(err >= 0);
if (err < 0)
return err;
}
err = snd_pcm_plug_insert_plugins(pcm, format, slave_format);
if (err < 0)
return err;
err = snd_pcm_params(plug->slave, params);
err = snd_pcm_hw_params(plug->slave, params);
if (err < 0) {
snd_pcm_plug_clear(pcm);
return err;
}
assert(slave->setup.format.sfmt == slave_format->sfmt);
assert(slave->setup.format.channels == slave_format->channels);
assert(slave->setup.format.rate == slave_format->rate);
pcm->hw_ptr = slave->hw_ptr;
pcm->appl_ptr = slave->appl_ptr;
return 0;
}
static int snd_pcm_plug_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
static int snd_pcm_plug_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
{
snd_pcm_plug_t *plug = pcm->private;
return snd_pcm_setup(plug->slave, setup);
snd_pcm_t *slave = plug->req_slave;
size_t avail_min, xfer_min, xfer_align;
int err;
avail_min = params->avail_min;
xfer_min = params->xfer_min;
xfer_align = params->xfer_align;
params->avail_min = muldiv_near(params->avail_min, slave->rate, pcm->rate);
params->xfer_min = muldiv_near(params->xfer_min, slave->rate, pcm->rate);
params->xfer_align = muldiv_near(params->xfer_align, slave->rate, pcm->rate);
err = snd_pcm_sw_params(slave, params);
params->avail_min = avail_min;
params->xfer_min = xfer_min;
params->xfer_align = xfer_align;
params->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size;
return err;
}
static int snd_pcm_plug_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info)
{
snd_pcm_plug_t *plug = pcm->private;
return snd_pcm_dig_info(plug->slave, info);
}
static int snd_pcm_plug_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params)
{
snd_pcm_plug_t *plug = pcm->private;
return snd_pcm_dig_params(plug->slave, params);
}
static int snd_pcm_plug_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
snd_pcm_plug_t *plug = pcm->private;
return snd_pcm_channel_info(plug->slave, info);
}
static int snd_pcm_plug_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
{
snd_pcm_plug_t *plug = pcm->private;
return snd_pcm_channel_params(plug->slave, params);
}
static int snd_pcm_plug_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
{
snd_pcm_plug_t *plug = pcm->private;
return snd_pcm_channel_setup(plug->slave, setup);
}
static int snd_pcm_plug_mmap(snd_pcm_t *pcm)
{
snd_pcm_plug_t *plug = pcm->private;
int err = snd_pcm_mmap(plug->slave);
if (err < 0)
return err;
pcm->mmap_info_count = plug->slave->mmap_info_count;
pcm->mmap_info = plug->slave->mmap_info;
return 0;
snd_pcm_plugin_t *plug = pcm->private;
return snd_pcm_mmap(plug->slave);
}
static int snd_pcm_plug_munmap(snd_pcm_t *pcm)
{
snd_pcm_plug_t *plug = pcm->private;
int err = snd_pcm_munmap(plug->slave);
if (err < 0)
return err;
pcm->mmap_info_count = 0;
pcm->mmap_info = 0;
return 0;
snd_pcm_plugin_t *plug = pcm->private;
return snd_pcm_munmap(plug->slave);
}
static void snd_pcm_plug_dump(snd_pcm_t *pcm, FILE *fp)
{
snd_pcm_plug_t *plug = pcm->private;
@ -631,12 +606,12 @@ static void snd_pcm_plug_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_plug_ops = {
close: snd_pcm_plug_close,
info: snd_pcm_plug_info,
params_info: snd_pcm_plug_params_info,
params: snd_pcm_plug_params,
setup: snd_pcm_plug_setup,
hw_info: snd_pcm_plug_hw_info,
hw_params: snd_pcm_plug_hw_params,
sw_params: snd_pcm_plug_sw_params,
dig_info: snd_pcm_plug_dig_info,
dig_params: snd_pcm_plug_dig_params,
channel_info: snd_pcm_plug_channel_info,
channel_params: snd_pcm_plug_channel_params,
channel_setup: snd_pcm_plug_channel_setup,
dump: snd_pcm_plug_dump,
nonblock: snd_pcm_plug_nonblock,
async: snd_pcm_plug_async,
@ -687,19 +662,14 @@ int snd_pcm_plug_open(snd_pcm_t **pcmp,
return 0;
}
int snd_pcm_plug_open_subdevice(snd_pcm_t **pcmp, int card, int device, int subdevice, int stream, int mode)
int snd_pcm_plug_open_hw(snd_pcm_t **pcmp, char *name, int card, int device, int subdevice, int stream, int mode)
{
snd_pcm_t *slave;
int err;
err = snd_pcm_hw_open_subdevice(&slave, card, device, subdevice, stream, mode);
err = snd_pcm_hw_open(&slave, NULL, card, device, subdevice, stream, mode);
if (err < 0)
return err;
return snd_pcm_plug_open(pcmp, NULL, 0, 0, 0, 0, slave, 1);
}
int snd_pcm_plug_open_device(snd_pcm_t **pcmp, int card, int device, int stream, int mode)
{
return snd_pcm_plug_open_subdevice(pcmp, card, device, -1, stream, mode);
return snd_pcm_plug_open(pcmp, name, 0, 0, 0, 0, slave, 1);
}
#define MAX_CHANNELS 32

View file

@ -52,38 +52,28 @@ int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
return snd_pcm_info(plugin->slave, info);
}
int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
int snd_pcm_plugin_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
snd_pcm_plugin_t *plugin = pcm->private;
return snd_pcm_channel_info(plugin->slave, info);
return snd_pcm_sw_params(plugin->slave, params);
}
int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
int snd_pcm_plugin_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info)
{
snd_pcm_plugin_t *plugin = pcm->private;
return snd_pcm_channel_params(plugin->slave, params);
return snd_pcm_dig_info(plugin->slave, info);
}
int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
int snd_pcm_plugin_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params)
{
snd_pcm_plugin_t *plugin = pcm->private;
int err;
err = snd_pcm_channel_setup(plugin->slave, setup);
if (err < 0)
return err;
if (!pcm->mmap_info)
return 0;
if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) {
setup->running_area.addr = pcm->mmap_info->addr;
setup->running_area.first = setup->channel * pcm->bits_per_sample;
setup->running_area.step = pcm->bits_per_frame;
} else {
setup->running_area.addr = pcm->mmap_info->addr + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
setup->running_area.first = 0;
setup->running_area.step = pcm->bits_per_sample;
}
setup->stopped_area = setup->running_area;
return 0;
return snd_pcm_dig_params(plugin->slave, params);
}
int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
snd_pcm_plugin_t *plugin = pcm->private;
return snd_pcm_channel_info_shm(pcm, info, plugin->shmid);
}
int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
@ -185,7 +175,7 @@ ssize_t snd_pcm_plugin_rewind(snd_pcm_t *pcm, size_t frames)
ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
{
snd_pcm_plugin_t *plugin = pcm->private;
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
ssize_t frames;
snd_pcm_areas_from_buf(pcm, areas, (void*)buffer);
frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write);
@ -197,7 +187,7 @@ ssize_t snd_pcm_plugin_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size)
{
snd_pcm_plugin_t *plugin = pcm->private;
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
ssize_t frames;
snd_pcm_areas_from_bufs(pcm, areas, bufs);
frames = snd_pcm_write_areas(pcm, areas, 0, size, plugin->write);
@ -209,7 +199,7 @@ ssize_t snd_pcm_plugin_writen(snd_pcm_t *pcm, void **bufs, size_t size)
ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size)
{
snd_pcm_plugin_t *plugin = pcm->private;
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
ssize_t frames;
snd_pcm_areas_from_buf(pcm, areas, buffer);
frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read);
@ -221,7 +211,7 @@ ssize_t snd_pcm_plugin_readi(snd_pcm_t *pcm, void *buffer, size_t size)
ssize_t snd_pcm_plugin_readn(snd_pcm_t *pcm, void **bufs, size_t size)
{
snd_pcm_plugin_t *plugin = pcm->private;
snd_pcm_channel_area_t areas[pcm->setup.format.channels];
snd_pcm_channel_area_t areas[pcm->channels];
ssize_t frames;
snd_pcm_areas_from_bufs(pcm, areas, bufs);
frames = snd_pcm_read_areas(pcm, areas, 0, size, plugin->read);
@ -251,7 +241,7 @@ ssize_t snd_pcm_plugin_mmap_forward(snd_pcm_t *pcm, size_t client_size)
size_t slave_frames = slave_size - slave_xfer;
size_t client_frames = client_size - client_xfer;
size_t offset = snd_pcm_mmap_hw_offset(pcm);
size_t cont = pcm->setup.buffer_size - offset;
size_t cont = pcm->buffer_size - offset;
if (cont < client_frames)
client_frames = cont;
err = plugin->write(pcm, pcm->running_areas, offset,
@ -279,17 +269,18 @@ ssize_t snd_pcm_plugin_avail_update(snd_pcm_t *pcm)
if (slave_size <= 0)
return slave_size;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK ||
!pcm->mmap_info)
pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED ||
pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED)
return plugin->client_frames ?
plugin->client_frames(pcm, slave_size) : slave_size;
client_xfer = snd_pcm_mmap_capture_avail(pcm);
client_size = pcm->setup.buffer_size;
client_size = pcm->buffer_size;
while (slave_xfer < (size_t)slave_size &&
client_xfer < client_size) {
size_t slave_frames = slave_size - slave_xfer;
size_t client_frames = client_size - client_xfer;
size_t offset = snd_pcm_mmap_hw_offset(pcm);
size_t cont = pcm->setup.buffer_size - offset;
size_t cont = pcm->buffer_size - offset;
if (cont < client_frames)
client_frames = cont;
err = plugin->read(pcm, pcm->running_areas, offset,
@ -313,38 +304,26 @@ int snd_pcm_plugin_set_avail_min(snd_pcm_t *pcm, size_t frames)
int snd_pcm_plugin_mmap(snd_pcm_t *pcm)
{
snd_pcm_plugin_t *plugin = pcm->private;
snd_pcm_t *slave = plugin->slave;
snd_pcm_mmap_info_t *i;
int err = snd_pcm_mmap(slave);
if (err < 0)
return err;
i = calloc(1, sizeof(*i));
if (!i)
return -ENOMEM;
err = snd_pcm_alloc_user_mmap(pcm, i);
if (err < 0) {
free(i);
return err;
snd_pcm_plugin_t *plug = pcm->private;
if (!(pcm->info & SND_PCM_INFO_MMAP)) {
size_t size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size);
int id = shmget(IPC_PRIVATE, size, 0666);
if (id < 0) {
SYSERR("shmget failed");
return -errno;
}
plug->shmid = id;
}
pcm->mmap_info = i;
pcm->mmap_info_count = 1;
return 0;
}
int snd_pcm_plugin_munmap(snd_pcm_t *pcm)
{
snd_pcm_plugin_t *plugin = pcm->private;
snd_pcm_t *slave = plugin->slave;
int err = snd_pcm_munmap(slave);
if (err < 0)
return err;
err = snd_pcm_free_mmap(pcm, pcm->mmap_info);
if (err < 0)
return err;
free(pcm->mmap_info);
pcm->mmap_info_count = 0;
pcm->mmap_info = 0;
snd_pcm_plugin_t *plug = pcm->private;
if (shmctl(plug->shmid, IPC_RMID, 0) < 0) {
SYSERR("shmctl IPC_RMID failed");
return -errno;
}
return 0;
}

View file

@ -24,18 +24,21 @@ typedef struct {
int close_slave;
snd_pcm_xfer_areas_func_t read;
snd_pcm_xfer_areas_func_t write;
size_t (*client_frames)(snd_pcm_t *pcm, size_t frames);
ssize_t (*client_frames)(snd_pcm_t *pcm, ssize_t frames);
int (*init)(snd_pcm_t *pcm);
int shmid;
size_t appl_ptr, hw_ptr;
unsigned int saccess_mask;
} snd_pcm_plugin_t;
int snd_pcm_plugin_close(snd_pcm_t *pcm);
int snd_pcm_plugin_nonblock(snd_pcm_t *pcm, int nonblock);
int snd_pcm_plugin_async(snd_pcm_t *pcm, int sig, pid_t pid);
int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_info_t * info);
int snd_pcm_plugin_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
int snd_pcm_plugin_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info);
int snd_pcm_plugin_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params);
int snd_pcm_plugin_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info);
int snd_pcm_plugin_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params);
int snd_pcm_plugin_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup);
int snd_pcm_plugin_status(snd_pcm_t *pcm, snd_pcm_status_t * status);
int snd_pcm_plugin_state(snd_pcm_t *pcm);
int snd_pcm_plugin_delay(snd_pcm_t *pcm, ssize_t *delayp);
@ -58,22 +61,34 @@ int snd_pcm_plugin_munmap_status(snd_pcm_t *pcm);
int snd_pcm_plugin_munmap_control(snd_pcm_t *pcm);
int snd_pcm_plugin_munmap(snd_pcm_t *pcm);
int snd_pcm_plugin_poll_descriptor(snd_pcm_t *pcm);
int snd_pcm_plugin_channels_mask(snd_pcm_t *pcm, bitset_t *cmask);
int get_index(int src_format, int dst_format);
int put_index(int src_format, int dst_format);
int conv_index(int src_format, int dst_format);
#define SND_PCM_LINEAR_FORMATS (SND_PCM_FMT_S8 | SND_PCM_FMT_U8 | \
SND_PCM_FMT_S16_LE | SND_PCM_FMT_S16_BE | \
SND_PCM_FMT_U16_LE | SND_PCM_FMT_U16_BE | \
SND_PCM_FMT_S24_LE | SND_PCM_FMT_S24_BE | \
SND_PCM_FMT_U24_LE | SND_PCM_FMT_U24_BE | \
SND_PCM_FMT_S32_LE | SND_PCM_FMT_S32_BE | \
SND_PCM_FMT_U32_LE | SND_PCM_FMT_U32_BE)
#define SND_PCM_FMTBIT_LINEAR (SND_PCM_FMTBIT_S8 |SND_PCM_FMTBIT_U8 | \
SND_PCM_FMTBIT_S16_LE|SND_PCM_FMTBIT_S16_BE | \
SND_PCM_FMTBIT_U16_LE|SND_PCM_FMTBIT_U16_BE | \
SND_PCM_FMTBIT_S24_LE|SND_PCM_FMTBIT_S24_BE | \
SND_PCM_FMTBIT_U24_LE|SND_PCM_FMTBIT_U24_BE | \
SND_PCM_FMTBIT_S32_LE|SND_PCM_FMTBIT_S32_BE | \
SND_PCM_FMTBIT_U32_LE|SND_PCM_FMTBIT_U32_BE)
extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops;
#define muldiv64(a,b,d) (((int64_t)(a) * (b) + (b) / 2) / (d))
static inline ssize_t muldiv_down(ssize_t a, ssize_t b, ssize_t d)
{
return (int64_t) (a * b) / d;
}
static inline ssize_t muldiv_up(ssize_t a, ssize_t b, ssize_t d)
{
return (int64_t) (a * b + (d - 1)) / d;
}
static inline ssize_t muldiv_near(ssize_t a, ssize_t b, ssize_t d)
{
return (int64_t) (a * b + (d / 2)) / d;
}
#define ROUTE_PLUGIN_FLOAT 1
#define ROUTE_PLUGIN_RESOLUTION 16

View file

@ -48,13 +48,8 @@ typedef struct {
int put_idx;
unsigned int pitch;
rate_f func;
int req_sformat;
int req_srate;
int sformat;
int cformat;
int srate;
int crate;
int cxfer_mode, cmmap_shape;
rate_state_t *states;
} snd_pcm_rate_t;
@ -93,7 +88,7 @@ static size_t resample_expand(snd_pcm_channel_area_t *src_areas,
#if 0
if (!src_area->enabled) {
if (dst_area->wanted)
snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt);
snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format);
dst_area->enabled = 0;
continue;
}
@ -177,7 +172,7 @@ static size_t resample_shrink(snd_pcm_channel_area_t *src_areas,
#if 0
if (!src_area->enabled) {
if (dst_area->wanted)
snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt);
snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format);
dst_area->enabled = 0;
continue;
}
@ -238,142 +233,132 @@ static int snd_pcm_rate_close(snd_pcm_t *pcm)
return 0;
}
static int snd_pcm_rate_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_rate_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_rate_t *rate = pcm->private;
unsigned int req_mask = info->req_mask;
unsigned int sfmt = info->req.format.sfmt;
unsigned int crate = info->req.format.rate;
unsigned int srate;
snd_pcm_hw_info_t sinfo;
unsigned int access_mask;
size_t fragment_size_min, fragment_size_max;
int err;
if (req_mask & SND_PCM_PARAMS_SFMT &&
!snd_pcm_format_linear(sfmt)) {
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
access_mask = info->access_mask;
if (access_mask == 0)
return -EINVAL;
info->format_mask &= SND_PCM_FMTBIT_LINEAR;
if (info->format_mask == 0)
return -EINVAL;
if (info->rate_min < 4000)
info->rate_min = 4000;
if (info->rate_max > 192000)
info->rate_max = 192000;
if (info->rate_max < info->rate_min)
return -EINVAL;
if (info->fragment_size_max > 1024 * 1024)
info->fragment_size_max = 1024 * 1024;
if (info->fragment_size_max < info->fragment_size_min)
return -EINVAL;
sinfo = *info;
sinfo.rate_min = rate->srate;
sinfo.rate_max = rate->srate;
if (rate->sformat >= 0)
sinfo.format_mask = 1U << rate->sformat;
sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max);
sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min);
sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(rate->plug.slave, &sinfo);
info->subformat_mask = sinfo.subformat_mask;
info->channels_min = sinfo.channels_min;
info->channels_max = sinfo.channels_max;
info->fragments_min = sinfo.fragments_min;
info->fragments_max = sinfo.fragments_max;
if (!sinfo.access_mask) {
info->access_mask = 0;
}
if (rate->req_sformat >= 0) {
info->req_mask |= SND_PCM_PARAMS_SFMT;
info->req.format.sfmt = rate->req_sformat;
if (!sinfo.format_mask) {
info->format_mask = 0;
}
if (sinfo.rate_min > sinfo.rate_max) {
info->rate_min = UINT_MAX;
info->rate_max = 0;
}
if (sinfo.fragment_size_min > sinfo.fragment_size_max) {
info->fragment_size_min = ULONG_MAX;
info->fragment_size_max = 0;
}
info->req_mask |= SND_PCM_PARAMS_RATE;
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
SND_PCM_PARAMS_XFER_MODE);
info->req.format.rate = rate->req_srate;
err = snd_pcm_params_info(rate->plug.slave, info);
info->req_mask = req_mask;
info->req.format.sfmt = sfmt;
info->req.format.rate = crate;
if (err < 0)
return err;
if (req_mask & SND_PCM_PARAMS_SFMT)
info->formats = 1 << sfmt;
else
info->formats = SND_PCM_LINEAR_FORMATS;
if (!(req_mask & SND_PCM_PARAMS_RATE)) {
info->min_rate = 4000;
info->max_rate = 192000;
return 0;
}
if (rate->req_srate - info->min_rate < info->max_rate - rate->req_srate)
srate = info->min_rate;
else
srate = info->max_rate;
info->min_rate = crate;
info->max_rate = crate;
if (info->buffer_size)
info->buffer_size = muldiv64(info->buffer_size, crate, srate);
if (info->min_fragment_size)
info->min_fragment_size = muldiv64(info->min_fragment_size, crate, srate);
if (info->max_fragment_size)
info->max_fragment_size = muldiv64(info->max_fragment_size, crate, srate);
if (info->fragment_align)
info->fragment_align = muldiv64(info->fragment_align, crate, srate);
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
if (fragment_size_min > info->fragment_size_min)
info->fragment_size_min = fragment_size_min;
if (fragment_size_max < info->fragment_size_max)
info->fragment_size_max = fragment_size_max;
rate->plug.saccess_mask = sinfo.access_mask;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info);
return 0;
}
static int snd_pcm_rate_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_rate_t *rate = pcm->private;
snd_pcm_t *slave = rate->plug.slave;
snd_pcm_params_t slave_params;
snd_pcm_params_info_t slave_info;
int srate, crate;
int err;
if (!snd_pcm_format_linear(params->format.sfmt)) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
slave_params = *params;
rate->cformat = params->format.sfmt;
rate->crate = crate = params->format.rate;
rate->cxfer_mode = params->xfer_mode;
rate->cmmap_shape = params->mmap_shape;
memset(&slave_info, 0, sizeof(slave_info));
slave_info.req = *params;
if (rate->req_sformat >= 0) {
slave_info.req.format.sfmt = rate->req_sformat;
slave_params.format.sfmt = rate->req_sformat;
}
slave_info.req.format.rate = rate->req_srate;
slave_info.req_mask = ~0;
err = snd_pcm_params_info(slave, &slave_info);
if (err < 0) {
params->fail_mask = slave_info.req.fail_mask;
params->fail_reason = slave_info.req.fail_reason;
return err;
}
if (rate->req_srate - slave_info.min_rate < slave_info.max_rate - rate->req_srate)
srate = slave_info.min_rate;
else
srate = slave_info.max_rate;
slave_params.format.rate = srate;
slave_params.avail_min = muldiv64(params->avail_min, srate, crate);
slave_params.xfer_min = muldiv64(params->xfer_min, srate, crate);
slave_params.buffer_size = muldiv64(params->buffer_size, srate, crate);
slave_params.frag_size = muldiv64(params->frag_size, srate, crate);
slave_params.xfer_align = muldiv64(params->xfer_align, srate, crate);
/* FIXME: boundary? */
slave_params.xfer_mode = SND_PCM_XFER_UNSPECIFIED;
slave_params.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
err = snd_pcm_params_mmap(slave, &slave_params);
params->fail_mask = slave_params.fail_mask;
params->fail_reason = slave_params.fail_reason;
return err;
}
static int snd_pcm_rate_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
{
snd_pcm_rate_t *rate = pcm->private;
int src_format, dst_format;
int src_rate, dst_rate;
snd_pcm_hw_info_t sinfo;
unsigned int format, access, crate;
unsigned int src_format, dst_format;
unsigned int src_rate, dst_rate;
size_t fragment_size;
int mul, div;
int err = snd_pcm_setup(rate->plug.slave, setup);
int err;
crate = params->rate;
format = params->format;
fragment_size = params->fragment_size;
access = params->access;
params->rate = rate->srate;
if (rate->sformat >= 0)
params->format = rate->sformat;
if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
params->fragment_size = muldiv_near(params->fragment_size, params->rate, crate);
snd_pcm_hw_params_to_info(params, &sinfo);
sinfo.fragment_size_min = 0;
sinfo.fragment_size_max = ULONG_MAX;
err = snd_pcm_hw_info_rulesv(slave, &sinfo, params,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE,
-1);
snd_pcm_hw_info_to_params(&sinfo, params);
if (err >= 0)
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->rate = crate;
params->access = access;
params->fragment_size = fragment_size;
if (err < 0)
return err;
if (rate->req_sformat >= 0)
assert(rate->req_sformat == setup->format.sfmt);
rate->sformat = setup->format.sfmt;
rate->srate = setup->format.rate;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
src_format = rate->cformat;
dst_format = rate->sformat;
src_rate = rate->crate;
dst_rate = rate->srate;
src_format = format;
dst_format = slave->format;
src_rate = crate;
dst_rate = slave->rate;
} else {
src_format = rate->sformat;
dst_format = rate->cformat;
src_rate = rate->srate;
dst_rate = rate->crate;
src_format = slave->format;
dst_format = format;
src_rate = slave->rate;
dst_rate = crate;
}
rate->get_idx = get_index(src_format, SND_PCM_SFMT_S16);
rate->put_idx = put_index(SND_PCM_SFMT_S16, dst_format);
rate->get_idx = get_index(src_format, SND_PCM_FORMAT_S16);
rate->put_idx = put_index(SND_PCM_FORMAT_S16, dst_format);
if (src_rate < dst_rate) {
rate->func = resample_expand;
/* pitch is get_threshold */
@ -389,43 +374,37 @@ static int snd_pcm_rate_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
mul = rate->pitch;
div = DIV;
}
rate->crate = muldiv64(rate->srate, mul, div);
if (rate->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
setup->xfer_mode = rate->cxfer_mode;
if (rate->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
else
setup->mmap_shape = rate->cmmap_shape;
setup->format.sfmt = rate->cformat;
setup->format.rate = rate->crate;
/* FIXME */
setup->rate_master = rate->crate;
setup->rate_divisor = 1;
setup->mmap_bytes = 0;
setup->avail_min = muldiv64(setup->avail_min, mul, div);
setup->xfer_min = muldiv64(setup->xfer_min, mul, div);
/* FIXME: the three above are not a lot sensible */
setup->buffer_size = muldiv64(setup->buffer_size, mul, div);
setup->frag_size = muldiv64(setup->frag_size, mul, div);
setup->xfer_align = muldiv64(setup->xfer_align, mul, div);
/* FIXME */
setup->boundary = LONG_MAX - LONG_MAX % setup->buffer_size;
if (rate->states)
free(rate->states);
rate->states = malloc(setup->format.channels * sizeof(*rate->states));
rate->states = malloc(params->channels * sizeof(*rate->states));
return 0;
}
static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
{
snd_pcm_rate_t *rate = pcm->private;
snd_pcm_t *slave = rate->plug.slave;
size_t avail_min, xfer_min, xfer_align;
int err;
avail_min = params->avail_min;
xfer_min = params->xfer_min;
xfer_align = params->xfer_align;
params->avail_min = muldiv_near(params->avail_min, slave->rate, pcm->rate);
params->xfer_min = muldiv_near(params->xfer_min, slave->rate, pcm->rate);
params->xfer_align = muldiv_near(params->xfer_align, slave->rate, pcm->rate);
err = snd_pcm_sw_params(slave, params);
params->avail_min = avail_min;
params->xfer_min = xfer_min;
params->xfer_align = xfer_align;
params->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size;
return err;
}
static int snd_pcm_rate_init(snd_pcm_t *pcm)
{
snd_pcm_rate_t *rate = pcm->private;
unsigned int k;
for (k = 0; k < pcm->setup.format.channels; ++k) {
for (k = 0; k < pcm->channels; ++k) {
rate->states[k].sum = 0;
rate->states[k].sample = 0;
if (rate->func == resample_expand) {
@ -463,7 +442,7 @@ static ssize_t snd_pcm_rate_write_areas(snd_pcm_t *pcm,
src_frames = rate->func(areas, client_offset, src_frames,
snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
&dst_frames,
pcm->setup.format.channels,
pcm->channels,
rate->get_idx, rate->put_idx,
rate->pitch, rate->states);
err = snd_pcm_mmap_forward(slave, dst_frames);
@ -509,7 +488,7 @@ static ssize_t snd_pcm_rate_read_areas(snd_pcm_t *pcm,
src_frames = rate->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
src_frames,
areas, client_offset, &dst_frames,
pcm->setup.format.channels,
pcm->channels,
rate->get_idx, rate->put_idx,
rate->pitch, rate->states);
err = snd_pcm_mmap_forward(slave, src_frames);
@ -529,27 +508,27 @@ static ssize_t snd_pcm_rate_read_areas(snd_pcm_t *pcm,
return err;
}
size_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, size_t frames)
ssize_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, ssize_t frames)
{
snd_pcm_rate_t *rate = pcm->private;
/* Round toward zero */
if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
return (int64_t)frames * DIV / rate->pitch;
return muldiv_down(frames, DIV, rate->pitch);
else
return (int64_t)frames * rate->pitch / DIV;
return muldiv_down(frames, rate->pitch, DIV);
}
static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp)
{
snd_pcm_rate_t *rate = pcm->private;
if (rate->req_sformat < 0)
if (rate->sformat < 0)
fprintf(fp, "Rate conversion PCM (%d)\n",
rate->req_srate);
rate->srate);
else
fprintf(fp, "Rate conversion PCM (%d, sformat=%s)\n",
rate->req_srate,
snd_pcm_format_name(rate->req_sformat));
if (pcm->valid_setup) {
rate->srate,
snd_pcm_format_name(rate->sformat));
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -560,12 +539,12 @@ static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_rate_ops = {
close: snd_pcm_rate_close,
info: snd_pcm_plugin_info,
params_info: snd_pcm_rate_params_info,
params: snd_pcm_rate_params,
setup: snd_pcm_rate_setup,
hw_info: snd_pcm_rate_hw_info,
hw_params: snd_pcm_rate_hw_params,
sw_params: snd_pcm_rate_sw_params,
dig_info: snd_pcm_plugin_dig_info,
dig_params: snd_pcm_plugin_dig_params,
channel_info: snd_pcm_plugin_channel_info,
channel_params: snd_pcm_plugin_channel_params,
channel_setup: snd_pcm_plugin_channel_setup,
dump: snd_pcm_rate_dump,
nonblock: snd_pcm_plugin_nonblock,
async: snd_pcm_plugin_async,
@ -584,8 +563,8 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, char *name, int sformat, int srate, snd_
if (!rate) {
return -ENOMEM;
}
rate->req_srate = srate;
rate->req_sformat = sformat;
rate->srate = srate;
rate->sformat = sformat;
rate->plug.read = snd_pcm_rate_read_areas;
rate->plug.write = snd_pcm_rate_write_areas;
rate->plug.client_frames = snd_pcm_rate_client_frames;

View file

@ -81,12 +81,8 @@ typedef union {
typedef struct {
/* This field need to be the first */
snd_pcm_plugin_t plug;
int req_sformat, req_schannels;
int sformat;
int cformat;
int schannels;
int cchannels;
int cxfer_mode, cmmap_shape;
route_params_t params;
} snd_pcm_route_t;
@ -429,110 +425,91 @@ static int snd_pcm_route_close(snd_pcm_t *pcm)
return 0;
}
static int snd_pcm_route_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
{
snd_pcm_route_t *route = pcm->private;
unsigned int req_mask = info->req_mask;
unsigned int sfmt = info->req.format.sfmt;
unsigned int channels = info->req.format.channels;
unsigned int format_mask, access_mask, channels_min, channels_max;
int err;
if (req_mask & SND_PCM_PARAMS_SFMT &&
!snd_pcm_format_linear(sfmt)) {
info->req.fail_mask = SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
access_mask = info->access_mask;
if (access_mask == 0)
return -EINVAL;
info->format_mask &= SND_PCM_FMTBIT_LINEAR;
format_mask = info->format_mask;
if (format_mask == 0)
return -EINVAL;
if (info->channels_min < 1)
info->channels_min = 1;
if (info->channels_max > 1024)
info->channels_max = 1024;
if (info->channels_max < info->channels_min)
return -EINVAL;
channels_min = info->channels_min;
channels_max = info->channels_max;
if (route->sformat >= 0)
info->format_mask = 1U << route->sformat;
if (route->schannels >= 0)
info->channels_min = info->channels_max = route->schannels;
info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(route->plug.slave, info);
if (info->format_mask)
info->format_mask = format_mask;
if (info->channels_min <= info->channels_max) {
info->channels_min = channels_min;
info->channels_max = channels_max;
}
if (route->req_sformat >= 0) {
info->req_mask |= SND_PCM_PARAMS_SFMT;
info->req.format.sfmt = route->req_sformat;
if (info->access_mask) {
route->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
if (route->req_schannels >= 0) {
info->req_mask |= SND_PCM_PARAMS_CHANNELS;
info->req.format.channels = route->req_schannels;
}
info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE |
SND_PCM_PARAMS_XFER_MODE);
err = snd_pcm_params_info(route->plug.slave, info);
info->req_mask = req_mask;
info->req.format.sfmt = sfmt;
info->req.format.channels = channels;
if (info->format_mask)
info->format_mask = format_mask;
if (err < 0)
return err;
if (req_mask & SND_PCM_PARAMS_SFMT)
info->formats = 1 << sfmt;
else
info->formats = SND_PCM_LINEAR_FORMATS;
if (req_mask & SND_PCM_PARAMS_CHANNELS) {
info->min_channels = channels;
info->max_channels = channels;
} else {
info->min_channels = 1;
info->max_channels = 1024;
}
info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info);
return 0;
}
static int snd_pcm_route_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_route_t *route = pcm->private;
snd_pcm_t *slave = route->plug.slave;
unsigned int format, access, channels;
unsigned int src_format, dst_format;
int err;
if (!snd_pcm_format_linear(params->format.sfmt)) {
params->fail_mask = SND_PCM_PARAMS_SFMT;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
route->cformat = params->format.sfmt;
route->cchannels = params->format.channels;
route->cxfer_mode = params->xfer_mode;
route->cmmap_shape = params->mmap_shape;
if (route->req_sformat >= 0)
params->format.sfmt = route->req_sformat;
if (route->req_schannels >= 0)
params->format.channels = route->req_schannels;
params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
err = snd_pcm_params_mmap(slave, params);
params->format.sfmt = route->cformat;
params->format.channels = route->cchannels;
params->xfer_mode = route->cxfer_mode;
params->mmap_shape = route->cmmap_shape;
return err;
}
static int snd_pcm_route_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
{
snd_pcm_route_t *route = pcm->private;
int src_format, dst_format;
int err = snd_pcm_setup(route->plug.slave, setup);
format = params->format;
channels = params->channels;
access = params->access;
if (route->sformat >= 0)
params->format = route->sformat;
if (route->schannels >= 0)
params->channels = route->schannels;
if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->channels = channels;
params->access = access;
if (err < 0)
return err;
if (route->req_sformat >= 0)
assert(route->req_sformat == setup->format.sfmt);
route->sformat = setup->format.sfmt;
route->schannels = setup->format.channels;
if (route->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else
setup->xfer_mode = route->cxfer_mode;
if (route->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
else
setup->mmap_shape = route->cmmap_shape;
setup->format.sfmt = route->cformat;
setup->format.channels = route->cchannels;
setup->mmap_bytes = 0;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
src_format = route->cformat;
dst_format = route->sformat;
src_format = format;
dst_format = slave->format;
} else {
src_format = route->sformat;
dst_format = route->cformat;
src_format = slave->format;
dst_format = format;
}
route->params.get_idx = get_index(src_format, SND_PCM_SFMT_U16);
route->params.put_idx = put_index(SND_PCM_SFMT_U32, dst_format);
route->params.get_idx = get_index(src_format, SND_PCM_FORMAT_U16);
route->params.put_idx = put_index(SND_PCM_FORMAT_U32, dst_format);
route->params.conv_idx = conv_index(src_format, dst_format);
route->params.src_size = snd_pcm_format_width(src_format) / 8;
route->params.dst_sfmt = dst_format;
@ -547,30 +524,6 @@ static int snd_pcm_route_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
return 0;
}
static int snd_pcm_route_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
{
#if 0
snd_pcm_plugin_t *plugin = pcm->private;
int err;
err = snd_pcm_channel_setup(plugin->slave, setup);
if (err < 0)
return err;
#endif
if (!pcm->mmap_info)
return 0;
if (pcm->setup.mmap_shape == SND_PCM_MMAP_INTERLEAVED) {
setup->running_area.addr = pcm->mmap_info->addr;
setup->running_area.first = setup->channel * pcm->bits_per_sample;
setup->running_area.step = pcm->bits_per_frame;
} else {
setup->running_area.addr = pcm->mmap_info->addr + setup->channel * pcm->setup.buffer_size * pcm->bits_per_sample / 8;
setup->running_area.first = 0;
setup->running_area.step = pcm->bits_per_sample;
}
setup->stopped_area = setup->running_area;
return 0;
}
static ssize_t snd_pcm_route_write_areas(snd_pcm_t *pcm,
snd_pcm_channel_area_t *areas,
size_t offset,
@ -588,7 +541,7 @@ static ssize_t snd_pcm_route_write_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
route_transfer(areas, offset,
snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
frames, route->schannels, &route->params);
frames, slave->channels, &route->params);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
break;
@ -622,7 +575,7 @@ static ssize_t snd_pcm_route_read_areas(snd_pcm_t *pcm,
size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
route_transfer(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
areas, offset,
frames, route->cchannels, &route->params);
frames, pcm->channels, &route->params);
err = snd_pcm_mmap_forward(slave, frames);
if (err < 0)
break;
@ -643,11 +596,11 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp)
{
snd_pcm_route_t *route = pcm->private;
unsigned int dst;
if (route->req_sformat < 0)
if (route->sformat < 0)
fprintf(fp, "Route conversion PCM\n");
else
fprintf(fp, "Route conversion PCM (sformat=%s)\n",
snd_pcm_format_name(route->req_sformat));
snd_pcm_format_name(route->sformat));
fputs("Transformation table:\n", fp);
for (dst = 0; dst < route->params.ndsts; dst++) {
ttable_dst_t *d = &route->params.dsts[dst];
@ -669,7 +622,7 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp)
}
putc('\n', fp);
}
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "Its setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -680,12 +633,12 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_route_ops = {
close: snd_pcm_route_close,
info: snd_pcm_plugin_info,
params_info: snd_pcm_route_params_info,
params: snd_pcm_route_params,
setup: snd_pcm_route_setup,
hw_info: snd_pcm_route_hw_info,
hw_params: snd_pcm_route_hw_params,
sw_params: snd_pcm_plugin_sw_params,
dig_info: snd_pcm_plugin_dig_info,
dig_params: snd_pcm_plugin_dig_params,
channel_info: snd_pcm_plugin_channel_info,
channel_params: snd_pcm_plugin_channel_params,
channel_setup: snd_pcm_route_channel_setup,
dump: snd_pcm_route_dump,
nonblock: snd_pcm_plugin_nonblock,
async: snd_pcm_plugin_async,
@ -782,8 +735,8 @@ int snd_pcm_route_open(snd_pcm_t **pcmp, char *name,
if (!route) {
return -ENOMEM;
}
route->req_sformat = sformat;
route->req_schannels = schannels;
route->sformat = sformat;
route->schannels = schannels;
route->plug.read = snd_pcm_route_read_areas;
route->plug.write = snd_pcm_route_write_areas;
route->plug.slave = slave;

View file

@ -21,6 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
@ -66,8 +67,8 @@ typedef struct {
struct list_head clients;
struct list_head list;
snd_pcm_t *pcm;
int sformat;
int srate;
int format;
int rate;
size_t channels_count;
size_t open_count;
size_t setup_count;
@ -104,7 +105,6 @@ typedef struct {
int ready;
int client_socket;
int slave_socket;
void *stopped_data;
} snd_pcm_share_t;
static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state);
@ -115,9 +115,9 @@ static size_t snd_pcm_share_slave_avail(snd_pcm_share_slave_t *slave)
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;
avail += pcm->buffer_size;
if (avail < 0)
avail += pcm->setup.boundary;
avail += pcm->boundary;
return avail;
}
@ -133,8 +133,8 @@ static size_t _snd_pcm_share_slave_forward(snd_pcm_share_slave_t *slave)
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;
boundary = slave->pcm->boundary;
buffer_size = slave->pcm->buffer_size;
min_frames = slave_avail;
max_frames = 0;
slave_appl_ptr = *slave->pcm->appl_ptr;
@ -191,7 +191,7 @@ 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;
snd_pcm_t *spcm = slave->pcm;
size_t buffer_size = spcm->setup.buffer_size;
size_t buffer_size = spcm->buffer_size;
int ready = 1, running = 0;
size_t avail = 0, slave_avail;
ssize_t hw_avail;
@ -208,7 +208,7 @@ static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun)
default:
return INT_MAX;
}
if (slave_xrun && pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
if (slave_xrun && pcm->xrun_mode != SND_PCM_XRUN_NONE) {
_snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
goto update_poll;
}
@ -251,7 +251,7 @@ static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun)
}
break;
case SND_PCM_STATE_RUNNING:
if (pcm->setup.xrun_mode != SND_PCM_XRUN_NONE) {
if (pcm->xrun_mode != SND_PCM_XRUN_NONE) {
if (hw_avail <= 0) {
_snd_pcm_share_stop(pcm, SND_PCM_STATE_XRUN);
break;
@ -300,7 +300,7 @@ static size_t _snd_pcm_share_missing(snd_pcm_t *pcm, int slave_xrun)
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);
snd_pcm_areas_silence(pcm->running_areas, offset, pcm->channels, frames, pcm->format);
offset += frames;
if (offset >= buffer_size)
offset = 0;
@ -357,17 +357,17 @@ void *snd_pcm_share_slave_thread(void *data)
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;
hw_ptr += spcm->fragment_size - 1;
if (hw_ptr >= spcm->boundary)
hw_ptr -= spcm->boundary;
hw_ptr -= hw_ptr % spcm->fragment_size;
avail_min = hw_ptr - *spcm->appl_ptr;
if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
avail_min += spcm->setup.buffer_size;
avail_min += spcm->buffer_size;
if (avail_min < 0)
avail_min += spcm->setup.boundary;
avail_min += spcm->boundary;
// printf("avail_min=%d\n", avail_min);
if ((size_t)avail_min != spcm->setup.avail_min)
if ((size_t)avail_min != spcm->avail_min)
snd_pcm_set_avail_min(spcm, avail_min);
slave->polling = 1;
Pthread_mutex_unlock(&slave->mutex);
@ -403,16 +403,16 @@ static void _snd_pcm_share_update(snd_pcm_t *pcm)
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;
hw_ptr += spcm->fragment_size - 1;
if (hw_ptr >= spcm->boundary)
hw_ptr -= spcm->boundary;
hw_ptr -= hw_ptr % spcm->fragment_size;
avail_min = hw_ptr - *spcm->appl_ptr;
if (spcm->stream == SND_PCM_STREAM_PLAYBACK)
avail_min += spcm->setup.buffer_size;
avail_min += spcm->buffer_size;
if (avail_min < 0)
avail_min += spcm->setup.boundary;
if ((size_t)avail_min < spcm->setup.avail_min)
avail_min += spcm->boundary;
if ((size_t)avail_min < spcm->avail_min)
snd_pcm_set_avail_min(spcm, avail_min);
}
}
@ -442,183 +442,81 @@ static int snd_pcm_share_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
return snd_pcm_info(share->slave->pcm, info);
}
static int snd_pcm_share_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info)
static int snd_pcm_share_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
unsigned int access_mask;
int err = 0;
unsigned int req_mask = info->req_mask;
unsigned int channels = info->req.format.channels;
if ((req_mask & SND_PCM_PARAMS_CHANNELS) &&
channels != share->channels_count) {
info->req.fail_mask |= SND_PCM_PARAMS_CHANNELS;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED |
SND_PCM_ACCBIT_MMAP_NONINTERLEAVED |
SND_PCM_ACCBIT_RW_NONINTERLEAVED);
access_mask = info->access_mask;
if (access_mask == 0)
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;
if (info->channels_min < share->channels_count)
info->channels_min = share->channels_count;
if (info->channels_max > share->channels_count)
info->channels_max = share->channels_count;
if (info->channels_max > info->channels_max)
return -EINVAL;
if (slave->format >= 0) {
info->format_mask &= 1U << slave->format;
if (!info->format_mask)
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;
if (slave->rate >= 0) {
if (info->rate_min < (unsigned)slave->rate)
info->rate_min = slave->rate;
if (info->rate_max > (unsigned)slave->rate)
info->rate_max = slave->rate;
if (info->rate_max > info->rate_max)
return -EINVAL;
}
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);
if (slave->setup_count > 1 ||
(slave->setup_count == 1 && !pcm->valid_setup)) {
snd_pcm_setup_t *s = &slave->pcm->setup;
if ((req_mask & SND_PCM_PARAMS_SFMT) &&
info->req.format.sfmt != s->format.sfmt) {
info->req.fail_mask |= SND_PCM_PARAMS_SFMT;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
err = -EINVAL;
goto _end;
}
info->formats = 1 << s->format.sfmt;
info->rates = SND_PCM_RATE_CONTINUOUS;
info->min_rate = info->max_rate = s->format.rate;
info->buffer_size = s->buffer_size;
info->min_fragment_size = info->max_fragment_size = s->frag_size;
info->min_fragments = info->max_fragments = s->frags;
info->fragment_align = s->frag_size;
info->req.fail_mask = 0;
}
info->min_channels = info->max_channels = share->channels_count;
if (info->flags & SND_PCM_INFO_INTERLEAVED) {
info->flags &= ~SND_PCM_INFO_INTERLEAVED;
info->flags |= SND_PCM_INFO_COMPLEX;
}
_end:
Pthread_mutex_unlock(&slave->mutex);
info->access_mask = SND_PCM_ACCBIT_MMAP;
info->channels_min = info->channels_max = slave->channels_count;
err = snd_pcm_hw_info(slave->pcm, info);
if (info->channels_min <= info->channels_max)
info->channels_min = info->channels_max = share->channels_count;
if (info->access_mask)
info->access_mask = access_mask;
info->info |= SND_PCM_INFO_DOUBLE;
return err;
}
static int snd_pcm_share_mmap(snd_pcm_t *pcm)
static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
snd_pcm_mmap_info_t *i;
size_t count;
snd_pcm_t *spcm = slave->pcm;
int err = 0;
Pthread_mutex_lock(&slave->mutex);
if (slave->mmap_count == 0) {
err = snd_pcm_mmap(slave->pcm);
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);
}
slave->mmap_count++;
count = slave->pcm->mmap_info_count;
i = malloc((count + 1) * sizeof(*i));
if (!i) {
err = -ENOMEM;
goto _end;
}
err = snd_pcm_alloc_user_mmap(pcm, i);
if (err < 0) {
free(i);
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);
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;
snd_pcm_mmap_info_t *i = pcm->mmap_info;
int err = 0;
Pthread_mutex_lock(&slave->mutex);
slave->mmap_count--;
if (slave->mmap_count == 0) {
err = snd_pcm_munmap(slave->pcm);
if (err < 0)
goto _end;
}
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);
return err;
}
static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
unsigned int channels = params->format.channels;
int err = 0;
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 (%ld)", channels, (long)share->channels_count);
return -EINVAL;
}
share->xfer_mode = params->xfer_mode;
share->xrun_mode = params->xrun_mode;
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) {
ERR("slave is already running with different format");
params->fail_mask |= SND_PCM_PARAMS_SFMT;
}
if (params->fail_mask) {
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
err = -EINVAL;
goto _end;
(slave->setup_count == 1 && !pcm->setup)) {
if (params->access != spcm->access ||
params->format != spcm->format ||
params->subformat != spcm->subformat ||
params->rate != spcm->rate ||
params->fragments != spcm->fragments ||
params->fragment_size != spcm->fragment_size) {
ERR("slave is already running with different setup");
params->fail_mask |= SND_PCM_HW_PARBIT_FORMAT;
return -EBUSY;
}
} 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_mmap(slave->pcm, &sp);
snd_pcm_hw_params_t sparams = *params;
sparams.channels = slave->channels_count;
err = snd_pcm_hw_params(slave->pcm, &sparams);
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->safety_threshold = sparams.rate * 30 / 1000;
slave->safety_threshold += sparams.fragment_size - 1;
slave->safety_threshold -= slave->safety_threshold % sparams.fragment_size;
slave->silence_frames = slave->safety_threshold;
if (slave->pcm->stream == SND_PCM_STREAM_PLAYBACK)
snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->channels, slave->pcm->buffer_size, slave->pcm->format);
}
share->state = SND_PCM_STATE_SETUP;
slave->setup_count++;
@ -627,26 +525,45 @@ static int snd_pcm_share_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
return err;
}
static int snd_pcm_share_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
static int snd_pcm_share_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t *params)
{
if (params->start_mode > SND_PCM_START_LAST) {
params->fail_mask = SND_PCM_SW_PARBIT_START_MODE;
return -EINVAL;
}
if (params->ready_mode > SND_PCM_READY_LAST) {
params->fail_mask = SND_PCM_SW_PARBIT_READY_MODE;
return -EINVAL;
}
if (params->xrun_mode > SND_PCM_XRUN_LAST) {
params->fail_mask = SND_PCM_SW_PARBIT_XRUN_MODE;
return -EINVAL;
}
return 0;
}
static int snd_pcm_share_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err;
err = snd_pcm_setup(slave->pcm, setup);
if (err < 0)
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
setup->xfer_mode = share->xfer_mode;
if (setup->mmap_shape != SND_PCM_MMAP_INTERLEAVED)
setup->mmap_shape = SND_PCM_MMAP_COMPLEX;
return 0;
/* FIXME */
Pthread_mutex_lock(&slave->mutex);
err = snd_pcm_dig_info(slave->pcm, info);
Pthread_mutex_unlock(&slave->mutex);
return err;
}
static int snd_pcm_share_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
int err;
/* FIXME */
Pthread_mutex_lock(&slave->mutex);
err = snd_pcm_dig_params(slave->pcm, params);
Pthread_mutex_unlock(&slave->mutex);
return err;
}
static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
@ -661,7 +578,7 @@ static int snd_pcm_share_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
if (share->state != SND_PCM_STATE_RUNNING &&
share->state != SND_PCM_STATE_DRAINING)
goto _notrunning;
d = pcm->setup.buffer_size - status->avail;
d = pcm->buffer_size - status->avail;
} else {
status->avail = snd_pcm_mmap_capture_avail(pcm);
if (share->state != SND_PCM_STATE_RUNNING)
@ -738,7 +655,7 @@ static ssize_t snd_pcm_share_avail_update(snd_pcm_t *pcm)
}
Pthread_mutex_unlock(&slave->mutex);
avail = snd_pcm_mmap_avail(pcm);
if ((size_t)avail > pcm->setup.buffer_size)
if ((size_t)avail > pcm->buffer_size)
return -EPIPE;
return avail;
}
@ -753,10 +670,10 @@ static ssize_t _snd_pcm_share_mmap_forward(snd_pcm_t *pcm, size_t size)
if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
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)
frames += pcm->setup.boundary;
if (frames > (ssize_t)pcm->buffer_size)
frames -= pcm->boundary;
else if (frames < -(ssize_t)pcm->buffer_size)
frames += pcm->boundary;
if (frames > 0) {
/* Latecomer PCM */
ret = snd_pcm_rewind(slave->pcm, frames);
@ -839,13 +756,13 @@ static int snd_pcm_share_start(snd_pcm_t *pcm)
while (xfer < hw_avail) {
size_t frames = hw_avail - xfer;
size_t offset = snd_pcm_mmap_offset(pcm);
size_t cont = pcm->setup.buffer_size - offset;
size_t cont = pcm->buffer_size - offset;
if (cont < frames)
frames = cont;
snd_pcm_areas_copy(pcm->stopped_areas, xfer,
pcm->running_areas, offset,
pcm->setup.format.channels, frames,
pcm->setup.format.sfmt);
pcm->channels, frames,
pcm->format);
xfer += frames;
}
snd_pcm_mmap_appl_forward(pcm, hw_avail);
@ -883,51 +800,6 @@ static int snd_pcm_share_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *in
return err;
}
static int snd_pcm_share_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
unsigned int channel = params->channel;
int c = share->slave_channels[channel];
int err;
params->channel = c;
err = snd_pcm_channel_params(slave->pcm, params);
params->channel = channel;
return err;
}
static int snd_pcm_share_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
unsigned int channel = setup->channel;
int c = share->slave_channels[channel];
int err;
setup->channel = c;
err = snd_pcm_channel_setup(slave->pcm, setup);
setup->channel = channel;
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_t *share = pcm->private;
@ -984,7 +856,7 @@ static int snd_pcm_share_set_avail_min(snd_pcm_t *pcm, size_t frames)
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
Pthread_mutex_lock(&slave->mutex);
pcm->setup.avail_min = frames;
pcm->avail_min = frames;
share->avail_min = frames;
_snd_pcm_share_update(pcm);
Pthread_mutex_unlock(&slave->mutex);
@ -996,7 +868,7 @@ static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state)
{
snd_pcm_share_t *share = pcm->private;
snd_pcm_share_slave_t *slave = share->slave;
if (!pcm->mmap_info) {
if (!pcm->mmap_channels) {
/* PCM closing already begun in the main thread */
return;
}
@ -1004,13 +876,13 @@ static void _snd_pcm_share_stop(snd_pcm_t *pcm, int state)
if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
snd_pcm_areas_copy(pcm->running_areas, 0,
pcm->stopped_areas, 0,
pcm->setup.format.channels, pcm->setup.buffer_size,
pcm->setup.format.sfmt);
pcm->channels, pcm->buffer_size,
pcm->format);
} else if (slave->running_count > 1) {
int err;
ssize_t delay;
snd_pcm_areas_silence(pcm->running_areas, 0, pcm->setup.format.channels,
pcm->setup.buffer_size, pcm->setup.format.sfmt);
snd_pcm_areas_silence(pcm->running_areas, 0, pcm->channels,
pcm->buffer_size, pcm->format);
err = snd_pcm_delay(slave->pcm, &delay);
if (err >= 0 && delay > 0)
snd_pcm_rewind(slave->pcm, delay);
@ -1116,7 +988,7 @@ static int snd_pcm_share_close(snd_pcm_t *pcm)
int err = 0;
Pthread_mutex_lock(&slaves_mutex);
Pthread_mutex_lock(&slave->mutex);
if (pcm->valid_setup)
if (pcm->setup)
slave->setup_count--;
slave->open_count--;
if (slave->open_count == 0) {
@ -1142,6 +1014,16 @@ static int snd_pcm_share_close(snd_pcm_t *pcm)
return err;
}
static int snd_pcm_share_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_share_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp)
{
snd_pcm_share_t *share = pcm->private;
@ -1151,7 +1033,7 @@ static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp)
fprintf(fp, "\nChannel bindings:\n");
for (k = 0; k < share->channels_count; ++k)
fprintf(fp, "%d: %d\n", k, share->slave_channels[k]);
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "\nIts setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -1162,12 +1044,12 @@ static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp)
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,
params: snd_pcm_share_params,
setup: snd_pcm_share_setup,
hw_info: snd_pcm_share_hw_info,
hw_params: snd_pcm_share_hw_params,
sw_params: snd_pcm_share_sw_params,
dig_info: snd_pcm_share_dig_info,
dig_params: snd_pcm_share_dig_params,
channel_info: snd_pcm_share_channel_info,
channel_params: snd_pcm_share_channel_params,
channel_setup: snd_pcm_share_channel_setup,
dump: snd_pcm_share_dump,
nonblock: snd_pcm_share_nonblock,
async: snd_pcm_share_async,
@ -1311,8 +1193,8 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
INIT_LIST_HEAD(&slave->clients);
slave->pcm = spcm;
slave->channels_count = schannels_count;
slave->sformat = sformat;
slave->srate = srate;
slave->format = sformat;
slave->rate = srate;
pthread_mutex_init(&slave->mutex, NULL);
pthread_cond_init(&slave->poll_cond, NULL);
list_add_tail(&slave->list, &slaves);
@ -1353,7 +1235,7 @@ int snd_pcm_share_open(snd_pcm_t **pcmp, char *name, char *sname,
pcm->type = SND_PCM_TYPE_SHARE;
pcm->stream = stream;
pcm->mode = mode;
pcm->mmap_auto = 1;
pcm->mmap_rw = 1;
pcm->ops = &snd_pcm_share_ops;
pcm->op_arg = pcm;
pcm->fast_ops = &snd_pcm_share_fast_ops;

View file

@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
@ -40,8 +41,8 @@
typedef struct {
int socket;
unsigned int access_mask;
volatile snd_pcm_shm_ctrl_t *ctrl;
snd_pcm_mmap_info_t *slave_mmap_info;
} snd_pcm_shm_t;
int receive_fd(int socket, void *data, size_t len, int *fd)
@ -147,108 +148,147 @@ static int snd_pcm_shm_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
return err;
}
static int snd_pcm_shm_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
static int snd_pcm_shm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
ctrl->cmd = SND_PCM_IOCTL_PARAMS_INFO;
ctrl->u.params_info = *info;
unsigned int access_mask = info->access_mask;
ctrl->cmd = SND_PCM_IOCTL_HW_INFO;
ctrl->u.hw_info = *info;
ctrl->u.hw_info.access_mask |= SND_PCM_ACCBIT_MMAP;
err = snd_pcm_shm_action(pcm);
*info = ctrl->u.hw_info;
if (info->access_mask) {
shm->access_mask = info->access_mask;
info->access_mask |= (SND_PCM_ACCESS_RW_INTERLEAVED |
SND_PCM_ACCESS_RW_NONINTERLEAVED);
info->access_mask &= access_mask;
}
if (err < 0)
return err;
*info = ctrl->u.params_info;
return err;
}
static int snd_pcm_shm_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
unsigned int access = params->access;
int err;
ctrl->cmd = SND_PCM_IOCTL_PARAMS;
ctrl->u.params = *params;
ctrl->cmd = SND_PCM_IOCTL_HW_PARAMS;
ctrl->u.hw_params = *params;
if (shm->access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
ctrl->u.hw_params.access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (shm->access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
ctrl->u.hw_params.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_shm_action(pcm);
params->access = access;
*params = ctrl->u.hw_params;
if (err < 0)
return err;
*params = ctrl->u.params;
return err;
}
static int snd_pcm_shm_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
static int snd_pcm_shm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
ctrl->cmd = SND_PCM_IOCTL_SETUP;
// ctrl->u.setup = *setup;
ctrl->cmd = SND_PCM_IOCTL_SW_PARAMS;
ctrl->u.sw_params = *params;
err = snd_pcm_shm_action(pcm);
*params = ctrl->u.sw_params;
if (err < 0)
return err;
return err;
}
static int snd_pcm_shm_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t * info)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
ctrl->cmd = SND_PCM_IOCTL_DIG_INFO;
ctrl->u.dig_info = *info;
err = snd_pcm_shm_action(pcm);
if (err < 0)
return err;
*setup = ctrl->u.setup;
*info = ctrl->u.dig_info;
return err;
}
static int snd_pcm_shm_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t * params)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
ctrl->cmd = SND_PCM_IOCTL_DIG_PARAMS;
ctrl->u.dig_params = *params;
err = snd_pcm_shm_action(pcm);
*params = ctrl->u.dig_params;
if (err < 0)
return err;
return err;
}
static int snd_pcm_shm_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_shm_munmap(snd_pcm_t *pcm)
{
unsigned int c;
for (c = 0; c < pcm->channels; ++c) {
snd_pcm_channel_info_t *i = &pcm->mmap_channels[c];
unsigned int c1;
int err;
if (i->type != SND_PCM_AREA_MMAP)
continue;
if (i->u.mmap.fd < 0)
continue;
for (c1 = c + 1; c1 < pcm->channels; ++c1) {
snd_pcm_channel_info_t *i1 = &pcm->mmap_channels[c1];
if (i1->type != SND_PCM_AREA_MMAP)
continue;
if (i1->u.mmap.fd != i->u.mmap.fd)
continue;
i1->u.mmap.fd = -1;
}
err = close(i->u.mmap.fd);
if (err < 0) {
SYSERR("close failed");
return -errno;
}
}
return 0;
}
static int snd_pcm_shm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
int fd;
ctrl->cmd = SND_PCM_IOCTL_CHANNEL_INFO;
ctrl->u.channel_info = *info;
err = snd_pcm_shm_action(pcm);
err = snd_pcm_shm_action_fd(pcm, &fd);
if (err < 0)
return err;
*info = ctrl->u.channel_info;
return err;
}
static int snd_pcm_shm_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t * params)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
ctrl->cmd = SND_PCM_IOCTL_CHANNEL_PARAMS;
ctrl->u.channel_params = *params;
err = snd_pcm_shm_action(pcm);
if (err < 0)
return err;
*params = ctrl->u.channel_params;
return err;
}
static void *convert_addr(void *addr, size_t count, snd_pcm_mmap_info_t *old, snd_pcm_mmap_info_t *new)
{
size_t k;
size_t mindist = ULONG_MAX;
int idx = -1;
for (k = 0; k < count; ++k) {
if (addr >= old[k].addr) {
size_t dist = addr - old[k].addr;
if (dist < mindist) {
mindist = dist;
idx = k;
}
}
}
assert(idx >= 0);
return new[idx].addr + mindist;
}
static int snd_pcm_shm_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t * setup)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
ctrl->cmd = SND_PCM_IOCTL_CHANNEL_SETUP;
ctrl->u.channel_setup = *setup;
err = snd_pcm_shm_action(pcm);
if (err < 0)
return err;
*setup = ctrl->u.channel_setup;
if (pcm->mmap_info) {
setup->running_area.addr = convert_addr(setup->running_area.addr, pcm->mmap_info_count, shm->slave_mmap_info, pcm->mmap_info);
setup->stopped_area.addr = convert_addr(setup->stopped_area.addr, pcm->mmap_info_count, shm->slave_mmap_info, pcm->mmap_info);
info->addr = 0;
switch (info->type) {
case SND_PCM_AREA_MMAP:
info->u.mmap.fd = fd;
break;
case SND_PCM_AREA_SHM:
break;
default:
assert(0);
break;
}
return err;
}
@ -356,86 +396,6 @@ static ssize_t snd_pcm_shm_rewind(snd_pcm_t *pcm, size_t frames)
return snd_pcm_shm_action(pcm);
}
static int snd_pcm_shm_mmap(snd_pcm_t *pcm)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int count, k, err, fd;
ctrl->cmd = SND_PCM_IOCTL_MMAP;
count = snd_pcm_shm_action(pcm);
if (count < 0)
return count;
pcm->mmap_info_count = count;
pcm->mmap_info = malloc(count * sizeof(*pcm->mmap_info));
shm->slave_mmap_info = malloc(count * sizeof(*shm->slave_mmap_info));
for (k = 0; k < count; ++k) {
snd_pcm_mmap_info_t *i = &pcm->mmap_info[k];
void *ptr;
ctrl->cmd = SND_PCM_IOCTL_MMAP_INFO;
ctrl->u.mmap_info.index = k;
err = snd_pcm_shm_action_fd(pcm, &fd);
if (err < 0)
return err;
shm->slave_mmap_info[k] = ctrl->u.mmap_info;
*i = ctrl->u.mmap_info;
if (i->type == SND_PCM_MMAP_KERNEL) {
i->u.kernel.fd = fd;
ptr = mmap(NULL, i->size, PROT_WRITE | PROT_READ,
MAP_FILE | MAP_SHARED,
fd, SND_PCM_MMAP_OFFSET_DATA);
close(fd);
if (ptr == MAP_FAILED || ptr == NULL) {
SYSERR("mmap failed");
free(pcm->mmap_info);
return -errno;
}
} else {
ptr = shmat(i->u.user.shmid, 0, 0);
if (ptr == (void*)-1) {
SYSERR("shmat failed");
free(pcm->mmap_info);
return -errno;
}
}
i->addr = ptr;
}
return 0;
}
static int snd_pcm_shm_munmap(snd_pcm_t *pcm)
{
snd_pcm_shm_t *shm = pcm->private;
volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl;
int err;
unsigned int k;
ctrl->cmd = SND_PCM_IOCTL_MUNMAP;
err = snd_pcm_shm_action(pcm);
if (err < 0)
return err;
for (k = 0; k < pcm->mmap_info_count; ++k) {
snd_pcm_mmap_info_t *i = &pcm->mmap_info[k];
if (i->type == SND_PCM_MMAP_KERNEL) {
err = munmap(i->addr, i->size);
if (err < 0) {
SYSERR("munmap failed");
return -errno;
}
} else {
err = shmdt(i->addr);
if (err < 0) {
SYSERR("shmdt failed");
return -errno;
}
}
}
pcm->mmap_info_count = 0;
free(pcm->mmap_info);
free(shm->slave_mmap_info);
pcm->mmap_info = 0;
shm->slave_mmap_info = 0;
return ctrl->result;
}
static ssize_t snd_pcm_shm_mmap_forward(snd_pcm_t *pcm, size_t size)
{
snd_pcm_shm_t *shm = pcm->private;
@ -483,7 +443,7 @@ static int snd_pcm_shm_close(snd_pcm_t *pcm)
static void snd_pcm_shm_dump(snd_pcm_t *pcm, FILE *fp)
{
fprintf(fp, "Shm PCM\n");
if (pcm->valid_setup) {
if (pcm->setup) {
fprintf(fp, "\nIts setup is:\n");
snd_pcm_dump_setup(pcm, fp);
}
@ -492,12 +452,12 @@ static void snd_pcm_shm_dump(snd_pcm_t *pcm, FILE *fp)
snd_pcm_ops_t snd_pcm_shm_ops = {
close: snd_pcm_shm_close,
info: snd_pcm_shm_info,
params_info: snd_pcm_shm_params_info,
params: snd_pcm_shm_params,
setup: snd_pcm_shm_setup,
hw_info: snd_pcm_shm_hw_info,
hw_params: snd_pcm_shm_hw_params,
sw_params: snd_pcm_shm_sw_params,
dig_info: snd_pcm_shm_dig_info,
dig_params: snd_pcm_shm_dig_params,
channel_info: snd_pcm_shm_channel_info,
channel_params: snd_pcm_shm_channel_params,
channel_setup: snd_pcm_shm_channel_setup,
dump: snd_pcm_shm_dump,
nonblock: snd_pcm_shm_nonblock,
async: snd_pcm_shm_async,
@ -656,7 +616,7 @@ int snd_pcm_shm_open(snd_pcm_t **pcmp, char *name, char *socket, char *sname, in
pcm->type = SND_PCM_TYPE_SHM;
pcm->stream = stream;
pcm->mode = mode;
pcm->mmap_auto = 1;
pcm->mmap_rw = 1;
pcm->ops = &snd_pcm_shm_ops;
pcm->op_arg = pcm;
pcm->fast_ops = &snd_pcm_shm_fast_ops;