alsa: implement set_format for DSD formats

This commit is contained in:
Wim Taymans 2021-09-17 18:05:52 +02:00
parent af6e887077
commit 670efe2811

View file

@ -829,22 +829,61 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num,
return res; return res;
} }
static int set_pcm_format(struct state *state, struct spa_audio_info_raw *info, uint32_t flags, bool spdif) int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_t flags)
{ {
unsigned int rrate, rchannels; unsigned int rrate, rchannels, val;
snd_pcm_uframes_t period_size; snd_pcm_uframes_t period_size;
int err, dir; int err, dir;
snd_pcm_hw_params_t *params; snd_pcm_hw_params_t *params;
snd_pcm_format_t format; snd_pcm_format_t rformat;
snd_pcm_access_mask_t *amask; snd_pcm_access_mask_t *amask;
snd_pcm_t *hndl; snd_pcm_t *hndl;
unsigned int periods; unsigned int periods;
bool match = true, planar, is_batch; bool match = true, planar = false, is_batch;
char spdif_params[128] = ""; char spdif_params[128] = "";
if (spdif) { state->use_mmap = !state->disable_mmap;
switch (fmt->media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
{
struct spa_audio_info_raw *f = &fmt->info.raw;
rrate = f->rate;
rchannels = f->channels;
rformat = spa_format_to_alsa(f->format, &planar);
break;
}
case SPA_MEDIA_SUBTYPE_iec958:
{
struct spa_audio_info_iec958 *f = &fmt->info.iec958;
unsigned aes3; unsigned aes3;
switch (info->rate) {
spa_log_info(state->log, "using IEC958 Codec:%s rate:%d",
spa_debug_type_find_short_name(spa_type_audio_iec958_codec, f->codec),
f->rate);
rformat = SND_PCM_FORMAT_S16_LE;
rchannels = 2;
rrate = f->rate;
switch (f->codec) {
case SPA_AUDIO_IEC958_CODEC_PCM:
case SPA_AUDIO_IEC958_CODEC_DTS:
case SPA_AUDIO_IEC958_CODEC_AC3:
case SPA_AUDIO_IEC958_CODEC_MPEG:
case SPA_AUDIO_IEC958_CODEC_MPEG2_AAC:
break;
case SPA_AUDIO_IEC958_CODEC_EAC3:
rrate *= 4;
break;
case SPA_AUDIO_IEC958_CODEC_TRUEHD:
case SPA_AUDIO_IEC958_CODEC_DTSHD:
rchannels = 8;
break;
default:
return -ENOTSUP;
}
switch (rrate) {
case 22050: aes3 = IEC958_AES3_CON_FS_22050; break; case 22050: aes3 = IEC958_AES3_CON_FS_22050; break;
case 24000: aes3 = IEC958_AES3_CON_FS_24000; break; case 24000: aes3 = IEC958_AES3_CON_FS_24000; break;
case 32000: aes3 = IEC958_AES3_CON_FS_32000; break; case 32000: aes3 = IEC958_AES3_CON_FS_32000; break;
@ -862,7 +901,50 @@ static int set_pcm_format(struct state *state, struct spa_audio_info_raw *info,
IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO, IEC958_AES0_CON_EMPHASIS_NONE | IEC958_AES0_NONAUDIO,
IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER, IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
0, aes3); 0, aes3);
break;
} }
case SPA_MEDIA_SUBTYPE_dsd:
{
struct spa_audio_info_dsd *f = &fmt->info.dsd;
rrate = f->rate;
rchannels = f->channels;
switch (f->interleave) {
case 4:
rformat = SND_PCM_FORMAT_DSD_U32_BE;
rrate /= 4;
break;
case -4:
rformat = SND_PCM_FORMAT_DSD_U32_LE;
rrate /= 4;
break;
case 2:
rformat = SND_PCM_FORMAT_DSD_U16_BE;
rrate /= 2;
break;
case -2:
rformat = SND_PCM_FORMAT_DSD_U16_LE;
rrate /= 2;
break;
case 1:
rformat = SND_PCM_FORMAT_DSD_U8;
break;
default:
return -ENOTSUP;
}
break;
}
default:
return -ENOTSUP;
}
if (rformat == SND_PCM_FORMAT_UNKNOWN) {
spa_log_warn(state->log, NAME" %s: unknown format",
state->props.device);
return -EINVAL;
}
if ((err = spa_alsa_open(state, spdif_params)) < 0) if ((err = spa_alsa_open(state, spdif_params)) < 0)
return err; return err;
@ -874,19 +956,10 @@ static int set_pcm_format(struct state *state, struct spa_audio_info_raw *info,
/* set hardware resampling, no resample */ /* set hardware resampling, no resample */
CHECK(snd_pcm_hw_params_set_rate_resample(hndl, params, 0), "set_rate_resample"); CHECK(snd_pcm_hw_params_set_rate_resample(hndl, params, 0), "set_rate_resample");
/* get format info */
format = spa_format_to_alsa(info->format, &planar);
if (format == SND_PCM_FORMAT_UNKNOWN) {
spa_log_warn(state->log, NAME" %s: unknown format %u",
state->props.device, info->format);
return -EINVAL;
}
/* set the interleaved/planar read/write format */ /* set the interleaved/planar read/write format */
snd_pcm_access_mask_alloca(&amask); snd_pcm_access_mask_alloca(&amask);
snd_pcm_hw_params_get_access_mask(params, amask); snd_pcm_hw_params_get_access_mask(params, amask);
state->use_mmap = !state->disable_mmap;
if (state->use_mmap) { if (state->use_mmap) {
if ((err = snd_pcm_hw_params_set_access(hndl, params, if ((err = snd_pcm_hw_params_set_access(hndl, params,
planar ? SND_PCM_ACCESS_MMAP_NONINTERLEAVED planar ? SND_PCM_ACCESS_MMAP_NONINTERLEAVED
@ -906,48 +979,47 @@ static int set_pcm_format(struct state *state, struct spa_audio_info_raw *info,
} }
} }
/* set the sample format */ /* set the sample format */
spa_log_debug(state->log, NAME" %p: Stream parameters are %iHz fmt:%s access:%s-%s channels:%i", spa_log_debug(state->log, NAME" %p: Stream parameters are %iHz fmt:%s access:%s-%s channels:%i",
state, info->rate, snd_pcm_format_name(format), state, rrate, snd_pcm_format_name(rformat),
state->use_mmap ? "mmap" : "rw", state->use_mmap ? "mmap" : "rw",
planar ? "planar" : "interleaved", info->channels); planar ? "planar" : "interleaved", rchannels);
CHECK(snd_pcm_hw_params_set_format(hndl, params, format), "set_format"); CHECK(snd_pcm_hw_params_set_format(hndl, params, rformat), "set_format");
/* set the count of channels */ /* set the count of channels */
rchannels = info->channels; val = rchannels;
CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &rchannels), "set_channels"); CHECK(snd_pcm_hw_params_set_channels_near(hndl, params, &val), "set_channels");
if (rchannels != info->channels) { if (rchannels != val) {
spa_log_warn(state->log, NAME" %s: Channels doesn't match (requested %u, got %u)", spa_log_warn(state->log, NAME" %s: Channels doesn't match (requested %u, got %u)",
state->props.device, info->channels, rchannels); state->props.device, rchannels, val);
if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST))
return -EINVAL; return -EINVAL;
info->channels = rchannels; rchannels = val;
match = false; match = false;
} }
/* set the stream rate */ /* set the stream rate */
rrate = info->rate; val = rrate;
CHECK(snd_pcm_hw_params_set_rate_near(hndl, params, &rrate, 0), "set_rate_near"); CHECK(snd_pcm_hw_params_set_rate_near(hndl, params, &val, 0), "set_rate_near");
if (rrate != info->rate) { if (rrate != val) {
spa_log_warn(state->log, NAME" %s: Rate doesn't match (requested %iHz, got %iHz)", spa_log_warn(state->log, NAME" %s: Rate doesn't match (requested %iHz, got %iHz)",
state->props.device, info->rate, rrate); state->props.device, rrate, val);
if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST)) if (!SPA_FLAG_IS_SET(flags, SPA_NODE_PARAM_FLAG_NEAREST))
return -EINVAL; return -EINVAL;
info->rate = rrate; rrate = val;
match = false; match = false;
} }
state->format = format; state->format = rformat;
state->channels = info->channels; state->channels = rchannels;
state->rate = info->rate; state->rate = rrate;
state->frame_size = snd_pcm_format_physical_width(format) / 8; state->frame_size = snd_pcm_format_physical_width(rformat) / 8;
state->planar = planar; state->planar = planar;
state->blocks = 1; state->blocks = 1;
if (planar) if (planar)
state->blocks *= info->channels; state->blocks *= rchannels;
else else
state->frame_size *= info->channels; state->frame_size *= rchannels;
dir = 0; dir = 0;
period_size = state->default_period_size ? state->default_period_size : 1024; period_size = state->default_period_size ? state->default_period_size : 1024;
@ -1000,55 +1072,6 @@ static int set_pcm_format(struct state *state, struct spa_audio_info_raw *info,
return match ? 0 : 1; return match ? 0 : 1;
} }
static int set_iec958_format(struct state *state, struct spa_audio_info_iec958 *info, uint32_t flags)
{
struct spa_audio_info_raw fmt;
spa_log_info(state->log, "using IEC958 Codec:%s rate:%d",
spa_debug_type_find_short_name(spa_type_audio_iec958_codec, info->codec),
info->rate);
fmt.format = SPA_AUDIO_FORMAT_S16_LE;
fmt.channels = 2;
fmt.rate = info->rate;
switch (info->codec) {
case SPA_AUDIO_IEC958_CODEC_PCM:
case SPA_AUDIO_IEC958_CODEC_DTS:
case SPA_AUDIO_IEC958_CODEC_AC3:
case SPA_AUDIO_IEC958_CODEC_MPEG:
case SPA_AUDIO_IEC958_CODEC_MPEG2_AAC:
break;
case SPA_AUDIO_IEC958_CODEC_EAC3:
fmt.rate *= 4;
break;
case SPA_AUDIO_IEC958_CODEC_TRUEHD:
case SPA_AUDIO_IEC958_CODEC_DTSHD:
fmt.channels = 8;
break;
default:
return -ENOTSUP;
}
return set_pcm_format(state, &fmt, flags, true);
}
int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_t flags)
{
int res;
switch (fmt->media_subtype) {
case SPA_MEDIA_SUBTYPE_raw:
res = set_pcm_format(state, &fmt->info.raw, flags, false);
break;
case SPA_MEDIA_SUBTYPE_iec958:
res = set_iec958_format(state, &fmt->info.iec958, flags);
break;
default:
return -ENOTSUP;
}
return res;
}
static int set_swparams(struct state *state) static int set_swparams(struct state *state)
{ {
snd_pcm_t *hndl = state->hndl; snd_pcm_t *hndl = state->hndl;