alsa: fix for format changes

Add exclusive option
Add channel mapping
This commit is contained in:
Wim Taymans 2018-09-17 09:48:32 +02:00
parent 4acfb88cb7
commit 9955e32030

View file

@ -68,6 +68,7 @@ typedef struct {
struct pw_remote *remote;
struct spa_hook remote_listener;
uint32_t flags;
struct pw_stream *stream;
struct spa_hook stream_listener;
@ -400,9 +401,9 @@ static int snd_pcm_pipewire_prepare(snd_pcm_ioplug_t *io)
PW_DIRECTION_OUTPUT :
PW_DIRECTION_INPUT,
pw->target,
pw->flags |
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
// PW_STREAM_FLAG_EXCLUSIVE |
PW_STREAM_FLAG_RT_PROCESS,
params, 1);
@ -451,51 +452,97 @@ static int snd_pcm_pipewire_stop(snd_pcm_ioplug_t *io)
}
#if __BYTE_ORDER == __BIG_ENDIAN
#define _FORMAT_LE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## _OE
#define _FORMAT_BE(fmt) SPA_AUDIO_FORMAT_ ## fmt
#define _FORMAT_LE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE
#define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_ ## fmt ## P : SPA_AUDIO_FORMAT_ ## fmt
#elif __BYTE_ORDER == __LITTLE_ENDIAN
#define _FORMAT_LE(fmt) SPA_AUDIO_FORMAT_ ## fmt
#define _FORMAT_BE(fmt) SPA_AUDIO_FORMAT_ ## fmt ## _OE
#define _FORMAT_LE(p, fmt) p ? SPA_AUDIO_FORMAT_ ## fmt ## P : SPA_AUDIO_FORMAT_ ## fmt
#define _FORMAT_BE(p, fmt) p ? SPA_AUDIO_FORMAT_UNKNOWN : SPA_AUDIO_FORMAT_ ## fmt ## _OE
#endif
static int set_default_channels(struct spa_audio_info_raw *info)
{
switch (info->channels) {
case 1:
info->position[0] = SPA_AUDIO_CHANNEL_MONO;
return 1;
case 8:
info->position[6] = SPA_AUDIO_CHANNEL_SL;
info->position[7] = SPA_AUDIO_CHANNEL_SR;
/* Fall through */
case 6:
info->position[4] = SPA_AUDIO_CHANNEL_RL;
info->position[5] = SPA_AUDIO_CHANNEL_RR;
/* Fall through */
case 4:
info->position[3] = SPA_AUDIO_CHANNEL_LFE;
/* Fall through */
case 3:
/* Fall through */
info->position[2] = SPA_AUDIO_CHANNEL_FC;
case 2:
info->position[0] = SPA_AUDIO_CHANNEL_FL;
info->position[1] = SPA_AUDIO_CHANNEL_FR;
return 1;
default:
return 0;
}
}
static int snd_pcm_pipewire_hw_params(snd_pcm_ioplug_t * io,
snd_pcm_hw_params_t * params)
{
snd_pcm_pipewire_t *pw = io->private_data;
bool planar;
pw_log_debug("hw_params");
switch(io->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
case SND_PCM_ACCESS_RW_INTERLEAVED:
planar = false;
break;
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
case SND_PCM_ACCESS_RW_NONINTERLEAVED:
planar = true;
break;
default:
SNDERR("PipeWire: invalid access: %d\n", io->access);
return -EINVAL;
}
switch(io->format) {
case SND_PCM_FORMAT_U8:
pw->format.format = SPA_AUDIO_FORMAT_U8;
pw->format.format = planar ? SPA_AUDIO_FORMAT_U8P : SPA_AUDIO_FORMAT_U8;
break;
case SND_PCM_FORMAT_S16_LE:
pw->format.format = _FORMAT_LE(S16);
pw->format.format = _FORMAT_LE(planar, S16);
break;
case SND_PCM_FORMAT_S16_BE:
pw->format.format = _FORMAT_BE(S16);
pw->format.format = _FORMAT_BE(planar, S16);
break;
case SND_PCM_FORMAT_S24_LE:
pw->format.format = _FORMAT_LE(S24_32);
pw->format.format = _FORMAT_LE(planar, S24_32);
break;
case SND_PCM_FORMAT_S24_BE:
pw->format.format = _FORMAT_BE(S24_32);
pw->format.format = _FORMAT_BE(planar, S24_32);
break;
case SND_PCM_FORMAT_S32_LE:
pw->format.format = _FORMAT_LE(S32);
pw->format.format = _FORMAT_LE(planar, S32);
break;
case SND_PCM_FORMAT_S32_BE:
pw->format.format = _FORMAT_BE(S32);
pw->format.format = _FORMAT_BE(planar, S32);
break;
case SND_PCM_FORMAT_S24_3LE:
pw->format.format = _FORMAT_LE(S24);
pw->format.format = _FORMAT_LE(planar, S24);
break;
case SND_PCM_FORMAT_S24_3BE:
pw->format.format = _FORMAT_BE(S24);
pw->format.format = _FORMAT_BE(planar, S24);
break;
case SND_PCM_FORMAT_FLOAT_LE:
pw->format.format = _FORMAT_LE(F32);
pw->format.format = _FORMAT_LE(planar, F32);
break;
case SND_PCM_FORMAT_FLOAT_BE:
pw->format.format = _FORMAT_BE(F32);
pw->format.format = _FORMAT_BE(planar, F32);
break;
default:
SNDERR("PipeWire: invalid format: %d\n", io->format);
@ -504,24 +551,88 @@ static int snd_pcm_pipewire_hw_params(snd_pcm_ioplug_t * io,
pw->format.channels = io->channels;
pw->format.rate = io->rate;
switch(io->access) {
case SND_PCM_ACCESS_MMAP_INTERLEAVED:
case SND_PCM_ACCESS_RW_INTERLEAVED:
pw->format.layout = SPA_AUDIO_LAYOUT_INTERLEAVED;
break;
case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
case SND_PCM_ACCESS_RW_NONINTERLEAVED:
pw->format.layout = SPA_AUDIO_LAYOUT_NON_INTERLEAVED;
break;
default:
SNDERR("PipeWire: invalid access: %d\n", io->access);
return -EINVAL;
}
set_default_channels(&pw->format);
pw->sample_bits = snd_pcm_format_physical_width(io->format);
return 0;
}
struct chmap_info {
enum snd_pcm_chmap_position pos;
enum spa_audio_channel channel;
};
static const struct chmap_info chmap_info[] = {
[SND_CHMAP_UNKNOWN] = { SND_CHMAP_UNKNOWN, SPA_AUDIO_CHANNEL_UNKNOWN },
[SND_CHMAP_NA] = { SND_CHMAP_NA, SPA_AUDIO_CHANNEL_NA },
[SND_CHMAP_MONO] = { SND_CHMAP_MONO, SPA_AUDIO_CHANNEL_MONO },
[SND_CHMAP_FL] = { SND_CHMAP_FL, SPA_AUDIO_CHANNEL_FL },
[SND_CHMAP_FR] = { SND_CHMAP_FR, SPA_AUDIO_CHANNEL_FR },
[SND_CHMAP_RL] = { SND_CHMAP_RL, SPA_AUDIO_CHANNEL_RL },
[SND_CHMAP_RR] = { SND_CHMAP_RR, SPA_AUDIO_CHANNEL_RR },
[SND_CHMAP_FC] = { SND_CHMAP_FC, SPA_AUDIO_CHANNEL_FC },
[SND_CHMAP_LFE] = { SND_CHMAP_LFE, SPA_AUDIO_CHANNEL_LFE },
[SND_CHMAP_SL] = { SND_CHMAP_SL, SPA_AUDIO_CHANNEL_SL },
[SND_CHMAP_SR] = { SND_CHMAP_SR, SPA_AUDIO_CHANNEL_SR },
[SND_CHMAP_RC] = { SND_CHMAP_RC, SPA_AUDIO_CHANNEL_RC },
[SND_CHMAP_FLC] = { SND_CHMAP_FLC, SPA_AUDIO_CHANNEL_FLC },
[SND_CHMAP_FRC] = { SND_CHMAP_FRC, SPA_AUDIO_CHANNEL_FRC },
[SND_CHMAP_RLC] = { SND_CHMAP_RLC, SPA_AUDIO_CHANNEL_RLC },
[SND_CHMAP_RRC] = { SND_CHMAP_RRC, SPA_AUDIO_CHANNEL_RRC },
[SND_CHMAP_FLW] = { SND_CHMAP_FLW, SPA_AUDIO_CHANNEL_FLW },
[SND_CHMAP_FRW] = { SND_CHMAP_FRW, SPA_AUDIO_CHANNEL_FRW },
[SND_CHMAP_FLH] = { SND_CHMAP_FLH, SPA_AUDIO_CHANNEL_FLH },
[SND_CHMAP_FCH] = { SND_CHMAP_FCH, SPA_AUDIO_CHANNEL_FCH },
[SND_CHMAP_FRH] = { SND_CHMAP_FRH, SPA_AUDIO_CHANNEL_FRH },
[SND_CHMAP_TC] = { SND_CHMAP_TC, SPA_AUDIO_CHANNEL_TC },
[SND_CHMAP_TFL] = { SND_CHMAP_TFL, SPA_AUDIO_CHANNEL_TFL },
[SND_CHMAP_TFR] = { SND_CHMAP_TFR, SPA_AUDIO_CHANNEL_TFR },
[SND_CHMAP_TFC] = { SND_CHMAP_TFC, SPA_AUDIO_CHANNEL_TFC },
[SND_CHMAP_TRL] = { SND_CHMAP_TRL, SPA_AUDIO_CHANNEL_TRL },
[SND_CHMAP_TRR] = { SND_CHMAP_TRR, SPA_AUDIO_CHANNEL_TRR },
[SND_CHMAP_TRC] = { SND_CHMAP_TRC, SPA_AUDIO_CHANNEL_TRC },
[SND_CHMAP_TFLC] = { SND_CHMAP_TFLC, SPA_AUDIO_CHANNEL_TFLC },
[SND_CHMAP_TFRC] = { SND_CHMAP_TFRC, SPA_AUDIO_CHANNEL_TFRC },
[SND_CHMAP_TSL] = { SND_CHMAP_TSL, SPA_AUDIO_CHANNEL_TSL },
[SND_CHMAP_TSR] = { SND_CHMAP_TSR, SPA_AUDIO_CHANNEL_TSR },
[SND_CHMAP_LLFE] = { SND_CHMAP_LLFE, SPA_AUDIO_CHANNEL_LLFE },
[SND_CHMAP_RLFE] = { SND_CHMAP_RLFE, SPA_AUDIO_CHANNEL_RLFE },
[SND_CHMAP_BC] = { SND_CHMAP_BC, SPA_AUDIO_CHANNEL_BC },
[SND_CHMAP_BLC] = { SND_CHMAP_BLC, SPA_AUDIO_CHANNEL_BLC },
[SND_CHMAP_BRC] = { SND_CHMAP_BRC, SPA_AUDIO_CHANNEL_BRC },
};
static enum snd_pcm_chmap_position channel_to_chmap(enum spa_audio_channel channel)
{
int i;
for (i = 0; i < SPA_N_ELEMENTS(chmap_info); i++)
if (chmap_info[i].channel == channel)
return chmap_info[i].pos;
return SND_CHMAP_UNKNOWN;
}
static int snd_pcm_pipewire_set_chmap(snd_pcm_ioplug_t * io,
const snd_pcm_chmap_t * map)
{
return 1;
}
static snd_pcm_chmap_t * snd_pcm_pipewire_get_chmap(snd_pcm_ioplug_t * io)
{
snd_pcm_pipewire_t *pw = io->private_data;
snd_pcm_chmap_t *map;
int i;
map = calloc(1, sizeof(snd_pcm_chmap_t) +
pw->format.channels * sizeof(unsigned int));
map->channels = pw->format.channels;
for (i = 0; i < pw->format.channels; i++)
map->pos[i] = channel_to_chmap(pw->format.position[i]);
return map;
}
static snd_pcm_ioplug_callback_t pipewire_pcm_callback = {
.close = snd_pcm_pipewire_close,
.start = snd_pcm_pipewire_start,
@ -530,6 +641,8 @@ static snd_pcm_ioplug_callback_t pipewire_pcm_callback = {
.prepare = snd_pcm_pipewire_prepare,
.poll_revents = snd_pcm_pipewire_poll_revents,
.hw_params = snd_pcm_pipewire_hw_params,
.set_chmap = snd_pcm_pipewire_set_chmap,
.get_chmap = snd_pcm_pipewire_get_chmap,
};
static int pipewire_set_hw_constraint(snd_pcm_pipewire_t *pw)
@ -564,6 +677,8 @@ static int pipewire_set_hw_constraint(snd_pcm_pipewire_t *pw)
1, MAX_CHANNELS)) < 0 ||
(err = snd_pcm_ioplug_set_param_minmax(&pw->io, SND_PCM_IOPLUG_HW_RATE,
1, MAX_RATE)) < 0 ||
(err = snd_pcm_ioplug_set_param_minmax(&pw->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
128, 64*1024)) < 0 ||
(err = snd_pcm_ioplug_set_param_minmax(&pw->io, SND_PCM_IOPLUG_HW_PERIODS,
2, 64)) < 0)
return err;
@ -634,7 +749,9 @@ static int snd_pcm_pipewire_open(snd_pcm_t **pcmp, const char *name,
const char *node_name,
const char *playback_node,
const char *capture_node,
snd_pcm_stream_t stream, int mode)
snd_pcm_stream_t stream,
int mode,
uint32_t flags)
{
snd_pcm_pipewire_t *pw;
int err;
@ -648,10 +765,11 @@ static int snd_pcm_pipewire_open(snd_pcm_t **pcmp, const char *name,
str = getenv("PIPEWIRE_NODE");
pw_log_debug("open %s %d %d '%s'", name, stream, mode, str);
pw_log_debug("open %s %d %d %08x '%s'", name, stream, mode, flags, str);
pw->fd = -1;
pw->io.poll_fd = -1;
pw->flags = flags;
if (node_name == NULL)
err = asprintf(&pw->node_name,
@ -718,6 +836,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(pipewire)
const char *server_name = NULL;
const char *playback_node = NULL;
const char *capture_node = NULL;
uint32_t flags = 0;
int err;
pw_init(NULL, NULL);
@ -745,11 +864,16 @@ SND_PCM_PLUGIN_DEFINE_FUNC(pipewire)
snd_config_get_string(n, &capture_node);
continue;
}
if (strcmp(id, "exclusive") == 0) {
if (snd_config_get_bool(n))
flags |= PW_STREAM_FLAG_EXCLUSIVE;
continue;
}
SNDERR("Unknown field %s", id);
return -EINVAL;
}
err = snd_pcm_pipewire_open(pcmp, name, node_name, playback_node, capture_node, stream, mode);
err = snd_pcm_pipewire_open(pcmp, name, node_name, playback_node, capture_node, stream, mode, flags);
return err;
}