pw-cat: also set rate/latency for pipe

Move the latency and rate properties to a separate function so that
we can call it in all cases and not just for sndfile io.
Simplify format handling.
This commit is contained in:
Wim Taymans 2022-06-15 12:35:56 +02:00
parent 3695611b20
commit b9fa0e6f28

View file

@ -126,7 +126,6 @@ struct data {
unsigned int rate; unsigned int rate;
int channels; int channels;
struct channelmap channelmap; struct channelmap channelmap;
unsigned int samplesize;
unsigned int stride; unsigned int stride;
enum unit latency_unit; enum unit latency_unit;
unsigned int latency_value; unsigned int latency_value;
@ -154,125 +153,42 @@ struct data {
} dsf; } dsf;
}; };
static inline int #define STR_FMTS "(ulaw|alaw|u8|s8|s16|s32|f32|f64)"
sf_str_to_fmt(const char *str)
static const struct format_info {
const char *name;
int sf_format;
uint32_t spa_format;
uint32_t width;
} format_info[] = {
{ "ulaw", SF_FORMAT_ULAW, SPA_AUDIO_FORMAT_ULAW, 1 },
{ "alaw", SF_FORMAT_ULAW, SPA_AUDIO_FORMAT_ALAW, 1 },
{ "s8", SF_FORMAT_PCM_S8, SPA_AUDIO_FORMAT_S8, 1 },
{ "u8", SF_FORMAT_PCM_U8, SPA_AUDIO_FORMAT_U8, 1 },
{ "s16", SF_FORMAT_PCM_16, SPA_AUDIO_FORMAT_S16, 2 },
{ "s24", SF_FORMAT_PCM_24, SPA_AUDIO_FORMAT_S24, 3 },
{ "s32", SF_FORMAT_PCM_32, SPA_AUDIO_FORMAT_S32, 4 },
{ "f32", SF_FORMAT_FLOAT, SPA_AUDIO_FORMAT_F32, 4 },
{ "f64", SF_FORMAT_DOUBLE, SPA_AUDIO_FORMAT_F32, 8 },
};
static const struct format_info *format_info_by_name(const char *str)
{ {
if (!str) uint32_t i;
return -1; for (i = 0; i < SPA_N_ELEMENTS(format_info); i++)
if (spa_streq(str, format_info[i].name))
if (spa_streq(str, "s8")) return &format_info[i];
return SF_FORMAT_PCM_S8; return NULL;
if (spa_streq(str, "u8"))
return SF_FORMAT_PCM_U8;
if (spa_streq(str, "s16"))
return SF_FORMAT_PCM_16;
if (spa_streq(str, "s24"))
return SF_FORMAT_PCM_24;
if (spa_streq(str, "s32"))
return SF_FORMAT_PCM_32;
if (spa_streq(str, "f32"))
return SF_FORMAT_FLOAT;
if (spa_streq(str, "f64"))
return SF_FORMAT_DOUBLE;
return -1;
} }
static inline const char * static const struct format_info *format_info_by_sf_format(int format)
sf_fmt_to_str(int format)
{ {
uint32_t i;
int sub_type = (format & SF_FORMAT_SUBMASK); int sub_type = (format & SF_FORMAT_SUBMASK);
for (i = 0; i < SPA_N_ELEMENTS(format_info); i++)
if (sub_type == SF_FORMAT_PCM_U8) if (format_info[i].sf_format == sub_type)
return "u8"; return &format_info[i];
if (sub_type == SF_FORMAT_PCM_S8) return NULL;
return "s8";
if (sub_type == SF_FORMAT_PCM_16)
return "s16";
if (sub_type == SF_FORMAT_PCM_24)
return "s24";
if (sub_type == SF_FORMAT_PCM_32)
return "s32";
if (sub_type == SF_FORMAT_FLOAT)
return "f32";
if (sub_type == SF_FORMAT_DOUBLE)
return "f64";
return "(invalid)";
}
#define STR_FMTS "(u8|s8|s16|s32|f32|f64)"
/* 0 = native, 1 = le, 2 = be */
static inline int
sf_format_endianess(int format)
{
return 0; /* native */
}
static inline enum spa_audio_format
sf_format_to_pw(int format)
{
int endianness;
endianness = sf_format_endianess(format);
if (endianness < 0)
return SPA_AUDIO_FORMAT_UNKNOWN;
switch (format & SF_FORMAT_SUBMASK) {
case SF_FORMAT_PCM_U8:
return SPA_AUDIO_FORMAT_U8;
case SF_FORMAT_PCM_S8:
return SPA_AUDIO_FORMAT_S8;
case SF_FORMAT_ULAW:
return SPA_AUDIO_FORMAT_ULAW;
case SF_FORMAT_ALAW:
return SPA_AUDIO_FORMAT_ALAW;
case SF_FORMAT_PCM_16:
return endianness == 1 ? SPA_AUDIO_FORMAT_S16_LE :
endianness == 2 ? SPA_AUDIO_FORMAT_S16_BE :
SPA_AUDIO_FORMAT_S16;
case SF_FORMAT_PCM_24:
case SF_FORMAT_PCM_32:
return endianness == 1 ? SPA_AUDIO_FORMAT_S32_LE :
endianness == 2 ? SPA_AUDIO_FORMAT_S32_BE :
SPA_AUDIO_FORMAT_S32;
case SF_FORMAT_DOUBLE:
return endianness == 1 ? SPA_AUDIO_FORMAT_F64_LE :
endianness == 2 ? SPA_AUDIO_FORMAT_F64_BE :
SPA_AUDIO_FORMAT_F64;
case SF_FORMAT_FLOAT:
default:
return endianness == 1 ? SPA_AUDIO_FORMAT_F32_LE :
endianness == 2 ? SPA_AUDIO_FORMAT_F32_BE :
SPA_AUDIO_FORMAT_F32;
break;
}
return SPA_AUDIO_FORMAT_UNKNOWN;
}
static inline int
sf_format_samplesize(int format)
{
int sub_type = (format & SF_FORMAT_SUBMASK);
switch (sub_type) {
case SF_FORMAT_PCM_S8:
case SF_FORMAT_PCM_U8:
case SF_FORMAT_ULAW:
case SF_FORMAT_ALAW:
return 1;
case SF_FORMAT_PCM_16:
return 2;
case SF_FORMAT_PCM_32:
return 4;
case SF_FORMAT_DOUBLE:
return 8;
case SF_FORMAT_FLOAT:
default:
return 4;
}
return -1;
} }
static int sf_playback_fill_x8(struct data *d, void *dest, unsigned int n_frames) static int sf_playback_fill_x8(struct data *d, void *dest, unsigned int n_frames)
@ -320,10 +236,8 @@ static int sf_playback_fill_f64(struct data *d, void *dest, unsigned int n_frame
} }
static inline fill_fn static inline fill_fn
sf_fmt_playback_fill_fn(int format) playback_fill_fn(uint32_t fmt)
{ {
enum spa_audio_format fmt = sf_format_to_pw(format);
switch (fmt) { switch (fmt) {
case SPA_AUDIO_FORMAT_S8: case SPA_AUDIO_FORMAT_S8:
case SPA_AUDIO_FORMAT_U8: case SPA_AUDIO_FORMAT_U8:
@ -404,10 +318,8 @@ static int sf_record_fill_f64(struct data *d, void *src, unsigned int n_frames)
} }
static inline fill_fn static inline fill_fn
sf_fmt_record_fill_fn(int format) record_fill_fn(uint32_t fmt)
{ {
enum spa_audio_format fmt = sf_format_to_pw(format);
switch (fmt) { switch (fmt) {
case SPA_AUDIO_FORMAT_S8: case SPA_AUDIO_FORMAT_S8:
case SPA_AUDIO_FORMAT_U8: case SPA_AUDIO_FORMAT_U8:
@ -1076,29 +988,6 @@ static int setup_dsffile(struct data *data)
return 0; return 0;
} }
struct format_info {
const char *name;
uint32_t spa_format;
uint32_t width;
} format_info[] = {
{ "s8", SPA_AUDIO_FORMAT_S8, 1 },
{ "u8", SPA_AUDIO_FORMAT_U8, 1 },
{ "s16", SPA_AUDIO_FORMAT_S16, 2 },
{ "s24", SPA_AUDIO_FORMAT_S24, 3 },
{ "s32", SPA_AUDIO_FORMAT_S32, 4 },
{ "f32", SPA_AUDIO_FORMAT_F32, 4 },
{ "f64", SPA_AUDIO_FORMAT_F32, 8 },
};
static struct format_info *format_info_by_name(const char *str)
{
uint32_t i;
for (i = 0; i < SPA_N_ELEMENTS(format_info); i++)
if (spa_streq(str, format_info[i].name))
return &format_info[i];
return NULL;
}
static int stdout_record(struct data *d, void *src, unsigned int n_frames) static int stdout_record(struct data *d, void *src, unsigned int n_frames)
{ {
return fwrite(src, d->stride, n_frames, stdout); return fwrite(src, d->stride, n_frames, stdout);
@ -1111,7 +1000,7 @@ static int stdin_play(struct data *d, void *src, unsigned int n_frames)
static int setup_pipe(struct data *data) static int setup_pipe(struct data *data)
{ {
struct format_info *info; const struct format_info *info;
if (data->format == NULL) if (data->format == NULL)
data->format = DEFAULT_FORMAT; data->format = DEFAULT_FORMAT;
@ -1129,6 +1018,12 @@ static int setup_pipe(struct data *data)
data->spa_format = info->spa_format; data->spa_format = info->spa_format;
data->stride = info->width * data->channels; data->stride = info->width * data->channels;
data->fill = data->mode == mode_playback ? stdin_play : stdout_record; data->fill = data->mode == mode_playback ? stdin_play : stdout_record;
if (data->verbose)
printf("PIPE: rate=%u channels=%u fmt=%s samplesize=%u stride=%u\n",
data->rate, data->channels,
info->name, info->width, data->stride);
return 0; return 0;
} }
@ -1221,9 +1116,8 @@ static void format_from_filename(SF_INFO *info, const char *filename)
static int setup_sndfile(struct data *data) static int setup_sndfile(struct data *data)
{ {
const struct format_info *fi = NULL;
SF_INFO info; SF_INFO info;
const char *s;
unsigned int nom = 0;
spa_zero(info); spa_zero(info);
/* for record, you fill in the info first */ /* for record, you fill in the info first */
@ -1237,14 +1131,14 @@ static int setup_sndfile(struct data *data)
if (data->channelmap.n_channels == 0) if (data->channelmap.n_channels == 0)
channelmap_default(&data->channelmap, data->channels); channelmap_default(&data->channelmap, data->channels);
memset(&info, 0, sizeof(info)); if ((fi = format_info_by_name(data->format)) == NULL) {
info.samplerate = data->rate;
info.channels = data->channels;
info.format = sf_str_to_fmt(data->format);
if (info.format == -1) {
fprintf(stderr, "error: unknown format \"%s\"\n", data->format); fprintf(stderr, "error: unknown format \"%s\"\n", data->format);
return -EINVAL; return -EINVAL;
} }
memset(&info, 0, sizeof(info));
info.samplerate = data->rate;
info.channels = data->channels;
info.format = fi->sf_format;
format_from_filename(&info, data->filename); format_from_filename(&info, data->filename);
} }
@ -1291,13 +1185,46 @@ static int setup_sndfile(struct data *data)
} }
} }
fill_properties(data); fill_properties(data);
/* try native format first, else decode to float */
if ((fi = format_info_by_sf_format(info.format)) == NULL)
fi = format_info_by_sf_format(SF_FORMAT_FLOAT);
} }
data->samplesize = sf_format_samplesize(info.format); if (fi == NULL)
data->stride = data->samplesize * data->channels; return -EIO;
data->spa_format = sf_format_to_pw(info.format);
if (data->verbose)
printf("PCM: fmt:%s rate:%u channels:%u width:%u\n",
fi->name, data->rate, data->channels, fi->width);
/* we read and write S24 as S32 with sndfile */
if (fi->spa_format == SPA_AUDIO_FORMAT_S24)
fi = format_info_by_sf_format(SF_FORMAT_PCM_32);
data->spa_format = fi->spa_format;
data->stride = fi->width * data->channels;
data->fill = data->mode == mode_playback ? data->fill = data->mode == mode_playback ?
sf_fmt_playback_fill_fn(info.format) : playback_fill_fn(data->spa_format) :
sf_fmt_record_fill_fn(info.format); record_fill_fn(data->spa_format);
if (data->fill == NULL) {
fprintf(stderr, "PCM: unhandled format %d\n", data->spa_format);
return -EINVAL;
}
return 0;
}
static int setup_properties(struct data *data)
{
const char *s;
unsigned int nom = 0;
if (data->quality >= 0)
pw_properties_setf(data->props, "resample.quality", "%d", data->quality);
if (data->rate)
pw_properties_setf(data->props, PW_KEY_NODE_RATE, "1/%u", data->rate);
data->latency_unit = unit_none; data->latency_unit = unit_none;
@ -1348,20 +1275,11 @@ static int setup_sndfile(struct data *data)
} }
if (data->verbose) if (data->verbose)
printf("PCM: rate=%u channels=%u fmt=%s samplesize=%u stride=%u latency=%u (%.3fs)\n", printf("rate:%d latency:%u (%.3fs)\n",
data->rate, data->channels, data->rate, nom, data->rate ? (double)nom/data->rate : 0.0f);
sf_fmt_to_str(info.format),
data->samplesize,
data->stride, nom, (double)nom/data->rate);
if (nom) if (nom)
pw_properties_setf(data->props, PW_KEY_NODE_LATENCY, "%u/%u", nom, data->rate); pw_properties_setf(data->props, PW_KEY_NODE_LATENCY, "%u/%u", nom, data->rate);
pw_properties_setf(data->props, PW_KEY_NODE_RATE, "1/%u", data->rate);
if (data->quality >= 0)
pw_properties_setf(data->props, "resample.quality", "%d", data->quality);
return 0; return 0;
} }
@ -1634,7 +1552,6 @@ int main(int argc, char *argv[])
break; break;
} }
} }
if (ret < 0) { if (ret < 0) {
fprintf(stderr, "error: open failed: %s\n", spa_strerror(ret)); fprintf(stderr, "error: open failed: %s\n", spa_strerror(ret));
switch (ret) { switch (ret) {
@ -1645,6 +1562,8 @@ int main(int argc, char *argv[])
goto error_usage; goto error_usage;
} }
} }
ret = setup_properties(&data);
switch (data.data_type) { switch (data.data_type) {
case TYPE_PCM: case TYPE_PCM:
{ {