diff --git a/src/daemon/pipewire-pulse.conf.in b/src/daemon/pipewire-pulse.conf.in index 132d03d53..af7fb233d 100644 --- a/src/daemon/pipewire-pulse.conf.in +++ b/src/daemon/pipewire-pulse.conf.in @@ -40,6 +40,8 @@ context.modules = [ #pulse.default.frag = 96000/48000 # 2 seconds #pulse.default.tlength = 96000/48000 # 2 seconds #pulse.min.quantum = 256/48000 # 5ms + #pulse.default.format = "float32ne" + #pulse.default.channel_map = "front-left,front-right" } } ] diff --git a/src/modules/module-protocol-pulse/format.c b/src/modules/module-protocol-pulse/format.c index 1528dda1a..4fc5ed79b 100644 --- a/src/modules/module-protocol-pulse/format.c +++ b/src/modules/module-protocol-pulse/format.c @@ -79,13 +79,26 @@ static const struct format audio_formats[] = { [SAMPLE_S24_32LE] = { SAMPLE_S24_32LE, SPA_AUDIO_FORMAT_S24_32_LE, "s24-32le", 4 }, [SAMPLE_S24_32BE] = { SAMPLE_S24_32BE, SPA_AUDIO_FORMAT_S24_32_BE, "s24-32be", 4 }, +#if __BYTE_ORDER == __BIG_ENDIAN + { SAMPLE_S16BE, SPA_AUDIO_FORMAT_S16_BE, "s16ne", 2 }, + { SAMPLE_FLOAT32BE, SPA_AUDIO_FORMAT_F32_BE, "float32ne", 4 }, + { SAMPLE_S32BE, SPA_AUDIO_FORMAT_S32_BE, "s32ne", 4 }, + { SAMPLE_S24BE, SPA_AUDIO_FORMAT_S24_BE, "s24ne", 3 }, + { SAMPLE_S24_32BE, SPA_AUDIO_FORMAT_S24_32_BE, "s24-32ne", 4 }, +#elif __BYTE_ORDER == __LITTLE_ENDIAN + { SAMPLE_S16LE, SPA_AUDIO_FORMAT_S16_LE, "s16ne", 2 }, + { SAMPLE_FLOAT32LE, SPA_AUDIO_FORMAT_F32_LE, "float32ne", 4 }, + { SAMPLE_S32LE, SPA_AUDIO_FORMAT_S32_LE, "s32ne", 4 }, + { SAMPLE_S24LE, SPA_AUDIO_FORMAT_S24_LE, "s24ne", 3 }, + { SAMPLE_S24_32LE, SPA_AUDIO_FORMAT_S24_32_LE, "s24-32ne", 4 }, +#endif /* planar formats, we just report them as inteleaved */ - { SAMPLE_U8, SPA_AUDIO_FORMAT_U8P, "u8", 1 }, - { SAMPLE_S16NE, SPA_AUDIO_FORMAT_S16P, "s16", 2 }, - { SAMPLE_S24_32NE, SPA_AUDIO_FORMAT_S24_32P, "s24-32", 4 }, - { SAMPLE_S32NE, SPA_AUDIO_FORMAT_S32P, "s32", 4 }, - { SAMPLE_S24NE, SPA_AUDIO_FORMAT_S24P, "s24", 3 }, - { SAMPLE_FLOAT32NE, SPA_AUDIO_FORMAT_F32P, "float32", 4 }, + { SAMPLE_U8, SPA_AUDIO_FORMAT_U8P, "u8ne", 1 }, + { SAMPLE_S16NE, SPA_AUDIO_FORMAT_S16P, "s16ne", 2 }, + { SAMPLE_S24_32NE, SPA_AUDIO_FORMAT_S24_32P, "s24-32ne", 4 }, + { SAMPLE_S32NE, SPA_AUDIO_FORMAT_S32P, "s32ne", 4 }, + { SAMPLE_S24NE, SPA_AUDIO_FORMAT_S24P, "s24ne", 3 }, + { SAMPLE_FLOAT32NE, SPA_AUDIO_FORMAT_F32P, "float32ne", 4 }, }; static inline uint32_t format_pa2id(enum sample_format format) @@ -108,8 +121,9 @@ static inline const char *format_id2name(uint32_t format) static inline uint32_t format_paname2id(const char *name, size_t size) { size_t i; - for (i = 0; i < SAMPLE_MAX; i++) { - if (strncmp(name, audio_formats[i].name, size) == 0) + for (i = 0; i < SPA_N_ELEMENTS(audio_formats); i++) { + if (audio_formats[i].name != NULL && + strncmp(name, audio_formats[i].name, size) == 0) return audio_formats[i].id; } return SPA_AUDIO_FORMAT_UNKNOWN; @@ -146,11 +160,6 @@ struct sample_spec { .rate = 0, \ .channels = 0, \ } -#define SAMPLE_SPEC_DEFAULT (struct sample_spec) { \ - .format = SPA_AUDIO_FORMAT_F32, \ - .rate = 44100, \ - .channels = 2, \ - } static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss) { @@ -325,11 +334,6 @@ struct channel_map { #define CHANNEL_MAP_INIT (struct channel_map) { \ .channels = 0, \ } -#define CHANNEL_MAP_DEFAULT (struct channel_map) { \ - .channels = 2, \ - .map[0] = SPA_AUDIO_CHANNEL_FL, \ - .map[1] = SPA_AUDIO_CHANNEL_FR, \ - } static inline uint32_t channel_pa2id(enum channel_position channel) { diff --git a/src/modules/module-protocol-pulse/module-loopback.c b/src/modules/module-protocol-pulse/module-loopback.c index c7b1f2315..b31c3f0fb 100644 --- a/src/modules/module-protocol-pulse/module-loopback.c +++ b/src/modules/module-protocol-pulse/module-loopback.c @@ -264,7 +264,7 @@ static struct module *create_module_loopback(struct impl *impl, const char *argu info.channels = pw_properties_parse_int(str); pw_properties_set(props, "channels", NULL); } else { - info.channels = 2; + info.channels = impl->defs.sample_spec.channels; } if ((str = pw_properties_get(props, "rate")) != NULL) { info.rate = pw_properties_parse_int(str); @@ -282,14 +282,15 @@ static struct module *create_module_loopback(struct impl *impl, const char *argu channel_map_to_positions(&map, info.position); pw_properties_set(props, "channel_map", NULL); } else { - if (info.channels > 2) - ERROR_RETURN("Mismatched channel map"); - - if (info.channels == 1) { + if (info.channels == impl->defs.channel_map.channels) { + channel_map_to_positions(&impl->defs.channel_map, info.position); + } else if (info.channels == 1) { info.position[0] = SPA_AUDIO_CHANNEL_MONO; - } else { + } else if (info.channels == 2) { info.position[0] = SPA_AUDIO_CHANNEL_FL; info.position[1] = SPA_AUDIO_CHANNEL_FR; + } else { + ERROR_RETURN("Mismatched channel map"); } /* TODO: pull in all of pa_channel_map_init_auto() */ } diff --git a/src/modules/module-protocol-pulse/module-null-sink.c b/src/modules/module-protocol-pulse/module-null-sink.c index 6cdfa68f0..403367218 100644 --- a/src/modules/module-protocol-pulse/module-null-sink.c +++ b/src/modules/module-protocol-pulse/module-null-sink.c @@ -122,6 +122,7 @@ static struct module *create_module_null_sink(struct impl *impl, const char *arg struct module_null_sink_data *d; struct pw_properties *props = NULL; const char *str; + struct channel_map map = CHANNEL_MAP_INIT; int res; props = pw_properties_new_dict(&SPA_DICT_INIT_ARRAY(module_null_sink_info)); @@ -145,27 +146,30 @@ static struct module *create_module_null_sink(struct impl *impl, const char *arg if ((str = pw_properties_get(props, "channels")) != NULL) { pw_properties_set(props, SPA_KEY_AUDIO_CHANNELS, str); pw_properties_set(props, "channels", NULL); + } else { + pw_properties_setf(props, SPA_KEY_AUDIO_CHANNELS, "%u", + impl->defs.sample_spec.channels); } if ((str = pw_properties_get(props, "rate")) != NULL) { pw_properties_set(props, SPA_KEY_AUDIO_RATE, str); pw_properties_set(props, "rate", NULL); } if ((str = pw_properties_get(props, "channel_map")) != NULL) { - struct channel_map map = CHANNEL_MAP_INIT; + channel_map_parse(str, &map); + pw_properties_set(props, "channel_map", NULL); + } else { + map = impl->defs.channel_map; + } + if (map.channels > 0) { uint32_t i; char *s, *p; - - channel_map_parse(str, &map); p = s = alloca(map.channels * 6); - for (i = 0; i < map.channels; i++) p += snprintf(p, 6, "%s%s", i == 0 ? "" : ",", channel_id2name(map.map[i])); pw_properties_set(props, SPA_KEY_AUDIO_POSITION, s); - pw_properties_set(props, "channel_map", NULL); - } else if (pw_properties_get(props, SPA_KEY_AUDIO_POSITION) == NULL) { - pw_properties_set(props, SPA_KEY_AUDIO_POSITION, "FL,FR"); } + if ((str = pw_properties_get(props, PW_KEY_MEDIA_CLASS)) == NULL) pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Audio/Sink"); diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index e64396805..35bf2babb 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -92,9 +92,17 @@ struct stats { #define DEFAULT_DEFAULT_FRAG "96000/48000" #define DEFAULT_DEFAULT_TLENGTH "96000/48000" #define DEFAULT_MIN_QUANTUM "256/48000" +#define DEFAULT_FORMAT "float32le" +#define DEFAULT_CHANNEL_MAP "front-left,front-right" #define MAX_FORMATS 32 +#include "format.c" +#include "volume.c" +#include "message.c" +#include "manager.h" +#include "dbus-name.c" + struct defs { struct spa_fraction min_req; struct spa_fraction default_req; @@ -102,14 +110,10 @@ struct defs { struct spa_fraction default_frag; struct spa_fraction default_tlength; struct spa_fraction min_quantum; + struct sample_spec sample_spec; + struct channel_map channel_map; }; -#include "format.c" -#include "volume.c" -#include "message.c" -#include "manager.h" -#include "dbus-name.c" - #define NAME "pulse-server" static bool debug_messages = false; @@ -3669,19 +3673,15 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t char name[256]; const char *str; struct message *reply; - struct sample_spec ss; - struct channel_map map; uint32_t cookie; pw_log_info(NAME" %p: [%s] GET_SERVER_INFO tag:%u", impl, client->name, tag); - ss = SAMPLE_SPEC_DEFAULT; - map = CHANNEL_MAP_DEFAULT; if (info != NULL) { if (info->props && (str = spa_dict_lookup(info->props, "default.clock.rate")) != NULL) - ss.rate = atoi(str); + impl->defs.sample_spec.rate = atoi(str); cookie = info->cookie; } else { cookie = 0; @@ -3695,7 +3695,7 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t TAG_STRING, "14.0.0", TAG_STRING, pw_get_user_name(), TAG_STRING, pw_get_host_name(), - TAG_SAMPLE_SPEC, &ss, + TAG_SAMPLE_SPEC, &impl->defs.sample_spec, TAG_STRING, get_default(client, true), /* default sink name */ TAG_STRING, get_default(client, false), /* default source name */ TAG_U32, cookie, /* cookie */ @@ -3703,7 +3703,7 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t if (client->version >= 15) { message_put(reply, - TAG_CHANNEL_MAP, &map, + TAG_CHANNEL_MAP, &impl->defs.channel_map, TAG_INVALID); } return send_message(client, reply); @@ -6357,6 +6357,30 @@ static int parse_frac(struct pw_properties *props, const char *key, const char * pw_log_info(NAME": defaults: %s = %u/%u", key, res->num, res->denom); return 0; } +static int parse_channel_map(struct pw_properties *props, const char *key, const char *def, + struct channel_map *res) +{ + const char *str; + if (props == NULL || + (str = pw_properties_get(props, key)) == NULL) + str = def; + channel_map_parse(str, res); + pw_log_info(NAME": defaults: %s = %s", key, str); + return 0; +} +static int parse_format(struct pw_properties *props, const char *key, const char *def, + struct sample_spec *res) +{ + const char *str; + if (props == NULL || + (str = pw_properties_get(props, key)) == NULL) + str = def; + res->format = format_paname2id(str, strlen(str)); + if (res->format == SPA_AUDIO_FORMAT_UNKNOWN) + res->format = SPA_AUDIO_FORMAT_F32; + pw_log_info(NAME": defaults: %s = %s", key, str); + return 0; +} static void load_defaults(struct defs *def, struct pw_properties *props) { @@ -6366,6 +6390,9 @@ static void load_defaults(struct defs *def, struct pw_properties *props) parse_frac(props, "pulse.default.frag", DEFAULT_DEFAULT_FRAG, &def->default_frag); parse_frac(props, "pulse.default.tlength", DEFAULT_DEFAULT_TLENGTH, &def->default_tlength); parse_frac(props, "pulse.min.quantum", DEFAULT_MIN_QUANTUM, &def->min_quantum); + parse_format(props, "pulse.default.format", DEFAULT_FORMAT, &def->sample_spec); + parse_channel_map(props, "pulse.default.channel_map", DEFAULT_CHANNEL_MAP, &def->channel_map); + def->sample_spec.channels = def->channel_map.channels; } struct pw_protocol_pulse *pw_protocol_pulse_new(struct pw_context *context,