diff options
32 files changed, 844 insertions, 369 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d2917a0c..1910a82e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -862,6 +862,13 @@ libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMIN # Plug-in libraries # ################################### +if HAVE_DBUS +# Serveral module (e.g. libalsa-util.la) +modlibexec_LTLIBRARIES += \ + libdbus-util.la \ + module-console-kit.la +endif + modlibexec_LTLIBRARIES += \ module-cli.la \ module-cli-protocol-tcp.la \ @@ -989,21 +996,14 @@ endif if HAVE_HAL modlibexec_LTLIBRARIES += \ - libdbus-util.la \ module-hal-detect.la endif -if HAVE_DBUS -modlibexec_LTLIBRARIES += \ - libdbus-util.la \ - module-console-kit.la -endif - if HAVE_BLUEZ modlibexec_LTLIBRARIES += \ + libbluetooth-util.la \ module-bluetooth-proximity.la \ module-bluetooth-discover.la \ - libbluetooth-util.la \ libbluetooth-ipc.la \ libbluetooth-sbc.la \ module-bluetooth-device.la diff --git a/src/map-file b/src/map-file index 3aa1d7a8..ca4e7a72 100644 --- a/src/map-file +++ b/src/map-file @@ -1,5 +1,7 @@ PULSE_0 { global: +pa_ascii_filter; +pa_ascii_valid; pa_browser_new; pa_browser_new_full; pa_browser_ref; @@ -109,6 +111,7 @@ pa_context_unref; pa_cvolume_avg; pa_cvolume_channels_equal_to; pa_cvolume_compatible; +pa_cvolume_compatible_with_channel_map; pa_cvolume_equal; pa_cvolume_get_balance; pa_cvolume_get_fade; @@ -180,6 +183,7 @@ pa_proplist_unset_many; pa_proplist_update; pa_sample_format_to_string; pa_sample_size; +pa_sample_size_of_format; pa_sample_spec_equal; pa_sample_spec_init; pa_sample_spec_snprint; diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c index 02e973c4..4dffd365 100644 --- a/src/modules/module-card-restore.c +++ b/src/modules/module-card-restore.c @@ -68,7 +68,10 @@ struct userdata { GDBM_FILE gdbm_file; }; -struct entry { +#define ENTRY_VERSION 1 + +struct entry PA_GCC_PACKED { + uint8_t version; char profile[PA_NAME_MAX]; }; @@ -104,12 +107,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; if (data.dsize != sizeof(struct entry)) { - pa_log_warn("Database contains entry for card %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); goto fail; } e = (struct entry*) data.dptr; + if (e->version != ENTRY_VERSION) { + pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name); + goto fail; + } + if (!memchr(e->profile, 0, sizeof(e->profile))) { pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name); goto fail; @@ -148,6 +156,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if (!(card = pa_idxset_get_by_index(c->cards, idx))) return; diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 8e0cf92b..7c56c240 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -79,10 +79,13 @@ struct userdata { pa_bool_t restore_muted:1; }; -struct entry { +#define ENTRY_VERSION 1 + +struct entry PA_GCC_PACKED { + uint8_t version; + pa_bool_t muted:1; pa_channel_map channel_map; pa_cvolume volume; - pa_bool_t muted:1; }; static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) { @@ -117,12 +120,17 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; if (data.dsize != sizeof(struct entry)) { - pa_log_warn("Database contains entry for device %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); goto fail; } e = (struct entry*) data.dptr; + if (e->version != ENTRY_VERSION) { + pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name); + goto fail; + } + if (!(pa_cvolume_valid(&e->volume))) { pa_log_warn("Invalid volume stored in database for device %s", name); goto fail; @@ -173,6 +181,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { pa_sink *sink; diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c index 8e4f4c32..e17cbe80 100644 --- a/src/modules/module-position-event-sounds.c +++ b/src/modules/module-position-event-sounds.c @@ -80,16 +80,16 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f); - if (!data->virtual_volume_is_set) { - pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); - data->virtual_volume_is_set = TRUE; - data->virtual_volume_is_absolute = FALSE; + if (!data->volume_is_set) { + pa_cvolume_reset(&data->volume, data->sample_spec.channels); + data->volume_is_set = TRUE; + data->volume_is_absolute = FALSE; } - pa_cvolume_set_balance(&data->virtual_volume, &data->channel_map, f*2.0-1.0); + pa_cvolume_set_balance(&data->volume, &data->channel_map, f*2.0-1.0); data->save_volume = FALSE; - pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume)); + pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume)); return PA_HOOK_OK; } diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c index 74ee6122..1784b2cc 100644 --- a/src/modules/module-raop-sink.c +++ b/src/modules/module-raop-sink.c @@ -100,9 +100,6 @@ struct userdata { pa_usec_t latency; - pa_volume_t volume; - pa_bool_t muted; - /*esd_format_t format;*/ int32_t rate; @@ -133,6 +130,9 @@ enum { SINK_MESSAGE_RIP_SOCKET }; +/* Forward declaration */ +static void sink_set_volume_cb(pa_sink *); + static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { struct userdata *u = userdata; pa_assert(u); @@ -141,7 +141,7 @@ static void on_connection(PA_GCC_UNUSED int fd, void*userdata) { u->fd = fd; /* Set the initial volume */ - pa_raop_client_set_volume(u->raop, u->volume); + sink_set_volume_cb(u->sink); pa_log_debug("Connection authenticated, handing fd to IO thread..."); @@ -255,43 +255,36 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse return pa_sink_process_msg(o, code, data, offset, chunk); } -static void sink_get_volume_cb(pa_sink *s) { - struct userdata *u = s->userdata; - int i; - - pa_assert(u); - - for (i = 0; i < s->sample_spec.channels; i++) - s->virtual_volume.values[i] = u->volume; -} - static void sink_set_volume_cb(pa_sink *s) { struct userdata *u = s->userdata; - int rv; + pa_cvolume hw; + pa_volume_t v; + char t[PA_CVOLUME_SNPRINT_MAX]; pa_assert(u); - /* If we're muted, we fake it */ - if (u->muted) + /* If we're muted we don't need to do anything */ + if (s->muted) return; - pa_assert(s->sample_spec.channels > 0); + /* Calculate the max volume of all channels. + We'll use this as our (single) volume on the APEX device and emulate + any variation in channel volumes in software */ + v = pa_cvolume_max(&s->virtual_volume); - /* Avoid pointless volume sets */ - if (u->volume == s->virtual_volume.values[0]) - return; + /* Create a pa_cvolume version of our single value */ + pa_cvolume_set(&hw, s->sample_spec.channels, v); - rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]); - if (0 == rv) - u->volume = s->virtual_volume.values[0]; -} + /* Perform any software manipulation of the volume needed */ + pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw); -static void sink_get_mute_cb(pa_sink *s) { - struct userdata *u = s->userdata; + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw)); + pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); - pa_assert(u); - - s->muted = u->muted; + /* Any necessary software volume manipulateion is done so set + our hw volume (or v as a single value) on the device */ + pa_raop_client_set_volume(u->raop, v); } static void sink_set_mute_cb(pa_sink *s) { @@ -299,8 +292,11 @@ static void sink_set_mute_cb(pa_sink *s) { pa_assert(u); - pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume)); - u->muted = s->muted; + if (s->muted) { + pa_raop_client_set_volume(u->raop, PA_VOLUME_MUTED); + } else { + sink_set_volume_cb(s); + } } static void thread_func(void *userdata) { @@ -541,9 +537,6 @@ int pa__init(pa_module*m) { u->next_encoding_overhead = 0; u->encoding_ratio = 1.0; - u->volume = roundf(0.7 * PA_VOLUME_NORM); - u->muted = FALSE; - u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); u->rtpoll_item = NULL; @@ -583,9 +576,7 @@ int pa__init(pa_module*m) { u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; - u->sink->get_volume = sink_get_volume_cb; u->sink->set_volume = sink_set_volume_cb; - u->sink->get_mute = sink_get_mute_cb; u->sink->set_mute = sink_set_mute_cb; u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index 0c9bd4f9..b1630fe1 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -90,16 +90,18 @@ struct userdata { pa_idxset *subscribed; }; -struct entry { +#define ENTRY_VERSION 1 + +struct entry PA_GCC_PACKED { + uint8_t version; + pa_bool_t muted_valid:1, relative_volume_valid:1, absolute_volume_valid:1, device_valid:1; + pa_bool_t muted:1; pa_channel_map channel_map; - char device[PA_NAME_MAX]; pa_cvolume relative_volume; pa_cvolume absolute_volume; - pa_bool_t muted:1; - pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1; + char device[PA_NAME_MAX]; }; - enum { SUBCOMMAND_TEST, SUBCOMMAND_READ, @@ -161,19 +163,19 @@ static struct entry* read_entry(struct userdata *u, const char *name) { if (data.dsize != sizeof(struct entry)) { /* This is probably just a database upgrade, hence let's not * consider this more than a debug message */ - pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); + pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry)); goto fail; } e = (struct entry*) data.dptr; - if (!memchr(e->device, 0, sizeof(e->device))) { - pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name); + if (e->version != ENTRY_VERSION) { + pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name); goto fail; } - if (!(pa_channel_map_valid(&e->channel_map))) { - pa_log_warn("Invalid channel map stored in database for stream %s", name); + if (!memchr(e->device, 0, sizeof(e->device))) { + pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name); goto fail; } @@ -182,6 +184,11 @@ static struct entry* read_entry(struct userdata *u, const char *name) { goto fail; } + if ((e->relative_volume_valid || e->absolute_volume_valid) && !(pa_channel_map_valid(&e->channel_map))) { + pa_log_warn("Invalid channel map stored in database for stream %s", name); + goto fail; + } + if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) || (e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) { pa_log_warn("Invalid volume stored in database for stream %s", name); @@ -233,7 +240,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) { return FALSE; if (a->muted_valid != b->muted_valid || - (a->muted && (a->muted != b->muted))) + (a->muted_valid && (a->muted != b->muted))) return FALSE; t = b->relative_volume; @@ -265,6 +272,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 return; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) { pa_sink_input *sink_input; @@ -388,32 +396,33 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu if (u->restore_volume) { - if (!new_data->virtual_volume_is_set) { + if (!new_data->volume_is_set) { pa_cvolume v; pa_cvolume_init(&v); if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) { + /* We don't check for e->device_valid here because + that bit marks whether it is a good choice for + restoring, not just if the data is filled in. */ if (e->absolute_volume_valid && - e->device_valid && - pa_streq(new_data->sink->name, e->device)) { + (e->device[0] == 0 || pa_streq(new_data->sink->name, e->device))) { v = e->absolute_volume; - new_data->virtual_volume_is_absolute = TRUE; + new_data->volume_is_absolute = TRUE; } else if (e->relative_volume_valid) { - v = e->relative_volume; - new_data->virtual_volume_is_absolute = FALSE; + new_data->volume_is_absolute = FALSE; } } else if (e->relative_volume_valid) { v = e->relative_volume; - new_data->virtual_volume_is_absolute = FALSE; + new_data->volume_is_absolute = FALSE; } if (v.channels > 0) { pa_log_info("Restoring volume for sink input %s.", name); - pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map)); + pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map)); new_data->save_volume = TRUE; } } else @@ -523,11 +532,10 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { if (si->sink->flags & PA_SINK_FLAT_VOLUME) { if (e->absolute_volume_valid && - e->device_valid && - pa_streq(e->device, si->sink->name)) + (e->device[0] == 0 || pa_streq(e->device, si->sink->name))) v = e->absolute_volume; else if (e->relative_volume_valid) { - pa_cvolume t = si->sink->virtual_volume; + pa_cvolume t = *pa_sink_get_volume(si->sink, FALSE); pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map)); } } else if (e->relative_volume_valid) @@ -655,10 +663,11 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio if ((e = read_entry(u, name))) { pa_cvolume r; + pa_channel_map cm; pa_tagstruct_puts(reply, name); - pa_tagstruct_put_channel_map(reply, &e->channel_map); - pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r)); + pa_tagstruct_put_channel_map(reply, (e->relative_volume_valid || e->absolute_volume_valid) ? &e->channel_map : pa_channel_map_init(&cm)); + pa_tagstruct_put_cvolume(reply, e->absolute_volume_valid ? &e->absolute_volume : (e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r))); pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL); pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE); @@ -697,21 +706,25 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio int k; memset(&entry, 0, sizeof(entry)); + entry.version = ENTRY_VERSION; if (pa_tagstruct_gets(t, &name) < 0 || pa_tagstruct_get_channel_map(t, &entry.channel_map) || - pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 || + pa_tagstruct_get_cvolume(t, &entry.absolute_volume) < 0 || pa_tagstruct_gets(t, &device) < 0 || pa_tagstruct_get_boolean(t, &muted) < 0) goto fail; - entry.absolute_volume_valid = FALSE; - entry.relative_volume_valid = entry.relative_volume.channels > 0; - - if (entry.relative_volume_valid && - entry.channel_map.channels != entry.relative_volume.channels) + if (!name || !*name) goto fail; + entry.relative_volume = entry.absolute_volume; + entry.absolute_volume_valid = entry.relative_volume_valid = entry.relative_volume.channels > 0; + + if (entry.relative_volume_valid) + if (!pa_cvolume_compatible_with_channel_map(&entry.relative_volume, &entry.channel_map)) + goto fail; + entry.muted = muted; entry.muted_valid = TRUE; diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 6ff30c26..983b8977 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -434,11 +434,11 @@ const char* pa_channel_position_to_string(pa_channel_position_t pos) { const char* pa_channel_position_to_pretty_string(pa_channel_position_t pos) { - pa_init_i18n(); - if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX) return NULL; + pa_init_i18n(); + return _(pretty_table[pos]); } @@ -448,6 +448,9 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_channel_map_valid(a), 0); + pa_return_val_if_fail(pa_channel_map_valid(b), 0); + if (a->channels != b->channels) return 0; @@ -502,19 +505,19 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { * "mono" here explicitly, because that can be understood as * listing with one channel called "mono". */ - if (strcmp(s, "stereo") == 0) { + if (pa_streq(s, "stereo")) { map.channels = 2; map.map[0] = PA_CHANNEL_POSITION_LEFT; map.map[1] = PA_CHANNEL_POSITION_RIGHT; goto finish; - } else if (strcmp(s, "surround-40") == 0) { + } else if (pa_streq(s, "surround-40")) { map.channels = 4; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT; map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; goto finish; - } else if (strcmp(s, "surround-41") == 0) { + } else if (pa_streq(s, "surround-41")) { map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -522,7 +525,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_LFE; goto finish; - } else if (strcmp(s, "surround-50") == 0) { + } else if (pa_streq(s, "surround-50")) { map.channels = 5; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -530,7 +533,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT; map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; goto finish; - } else if (strcmp(s, "surround-51") == 0) { + } else if (pa_streq(s, "surround-51")) { map.channels = 6; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -539,7 +542,7 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER; map.map[5] = PA_CHANNEL_POSITION_LFE; goto finish; - } else if (strcmp(s, "surround-71") == 0) { + } else if (pa_streq(s, "surround-71")) { map.channels = 8; map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; @@ -563,13 +566,13 @@ pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s) { } /* Some special aliases */ - if (strcmp(p, "left") == 0) + if (pa_streq(p, "left")) map.map[map.channels++] = PA_CHANNEL_POSITION_LEFT; - else if (strcmp(p, "right") == 0) + else if (pa_streq(p, "right")) map.map[map.channels++] = PA_CHANNEL_POSITION_RIGHT; - else if (strcmp(p, "center") == 0) + else if (pa_streq(p, "center")) map.map[map.channels++] = PA_CHANNEL_POSITION_CENTER; - else if (strcmp(p, "subwoofer") == 0) + else if (pa_streq(p, "subwoofer")) map.map[map.channels++] = PA_CHANNEL_POSITION_SUBWOOFER; else { pa_channel_position_t i; @@ -617,11 +620,8 @@ int pa_channel_map_compatible(const pa_channel_map *map, const pa_sample_spec *s pa_assert(map); pa_assert(ss); - if (!pa_channel_map_valid(map)) - return 0; - - if (!pa_sample_spec_valid(ss)) - return 0; + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); return map->channels == ss->channels; } @@ -633,6 +633,9 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_channel_map_valid(a), 0); + pa_return_val_if_fail(pa_channel_map_valid(b), 0); + memset(in_a, 0, sizeof(in_a)); for (i = 0; i < a->channels; i++) @@ -651,6 +654,8 @@ int pa_channel_map_can_balance(const pa_channel_map *map) { pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + for (c = 0; c < map->channels; c++) { switch (map->map[c]) { @@ -687,6 +692,10 @@ int pa_channel_map_can_fade(const pa_channel_map *map) { unsigned c; pa_bool_t front = FALSE, rear = FALSE; + pa_assert(map); + + pa_return_val_if_fail(pa_channel_map_valid(map), 0); + for (c = 0; c < map->channels; c++) { switch (map->map[c]) { @@ -727,6 +736,8 @@ const char* pa_channel_map_to_name(const pa_channel_map *map) { pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + memset(in_map, 0, sizeof(in_map)); for (c = 0; c < map->channels; c++) @@ -779,11 +790,15 @@ const char* pa_channel_map_to_pretty_name(const pa_channel_map *map) { pa_assert(map); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + memset(in_map, 0, sizeof(in_map)); for (c = 0; c < map->channels; c++) pa_bitset_set(in_map, map->map[c], TRUE); + pa_init_i18n(); + if (pa_bitset_equals(in_map, PA_CHANNEL_POSITION_MAX, PA_CHANNEL_POSITION_MONO, -1)) return _("Mono"); diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c index 703179c5..469c822a 100644 --- a/src/pulse/ext-stream-restore.c +++ b/src/pulse/ext-stream-restore.c @@ -30,6 +30,7 @@ #include <pulsecore/pstream-util.h> #include "internal.h" +#include "operation.h" #include "ext-stream-restore.h" @@ -191,8 +192,8 @@ pa_operation *pa_ext_stream_restore_write( void *userdata) { uint32_t tag; - pa_operation *o; - pa_tagstruct *t; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; pa_assert(c); pa_assert(PA_REFCNT_VALUE(c) >= 1); @@ -213,7 +214,15 @@ pa_operation *pa_ext_stream_restore_write( pa_tagstruct_put_boolean(t, apply_immediately); for (; n > 0; n--, data++) { + if (!data->name || !*data->name) + goto fail; + pa_tagstruct_puts(t, data->name); + + if (data->volume.channels > 0 && + !pa_cvolume_compatible_with_channel_map(&data->volume, &data->channel_map)) + goto fail; + pa_tagstruct_put_channel_map(t, &data->channel_map); pa_tagstruct_put_cvolume(t, &data->volume); pa_tagstruct_puts(t, data->device); @@ -224,6 +233,18 @@ pa_operation *pa_ext_stream_restore_write( pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); return o; + +fail: + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + if (t) + pa_tagstruct_free(t); + + pa_context_set_error(c, PA_ERR_INVALID); + return NULL; } pa_operation *pa_ext_stream_restore_delete( @@ -233,8 +254,8 @@ pa_operation *pa_ext_stream_restore_delete( void *userdata) { uint32_t tag; - pa_operation *o; - pa_tagstruct *t; + pa_operation *o = NULL; + pa_tagstruct *t = NULL; const char *const *k; pa_assert(c); @@ -251,13 +272,29 @@ pa_operation *pa_ext_stream_restore_delete( pa_tagstruct_puts(t, "module-stream-restore"); pa_tagstruct_putu32(t, SUBCOMMAND_DELETE); - for (k = s; *k; k++) + for (k = s; *k; k++) { + if (!*k || !**k) + goto fail; + pa_tagstruct_puts(t, *k); + } pa_pstream_send_tagstruct(c->pstream, t); pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); return o; + +fail: + if (o) { + pa_operation_cancel(o); + pa_operation_unref(o); + } + + if (t) + pa_tagstruct_free(t); + + pa_context_set_error(c, PA_ERR_INVALID); + return NULL; } pa_operation *pa_ext_stream_restore_subscribe( @@ -322,5 +359,4 @@ void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) if (c->ext_stream_restore.callback) c->ext_stream_restore.callback(c, c->ext_stream_restore.userdata); - } diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h index ac01d235..cf9f4ccc 100644 --- a/src/pulse/ext-stream-restore.h +++ b/src/pulse/ext-stream-restore.h @@ -36,10 +36,10 @@ PA_C_DECL_BEGIN * maintained by module-stream-restore. \since 0.9.12 */ typedef struct pa_ext_stream_restore_info { const char *name; /**< Identifier string of the stream. A string like "sink-input-by-role:" or similar followed by some arbitrary property value. */ - pa_channel_map channel_map; /**< The channel map for the volume field */ - pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable */ - const char *device; /**< The sink/source of the stream when it was last seen */ - int mute; /**< The boolean mute state of the stream when it was last seen, if applicable */ + pa_channel_map channel_map; /**< The channel map for the volume field, if applicable */ + pa_cvolume volume; /**< The volume of the stream when it was seen last, if applicable and saved */ + const char *device; /**< The sink/source of the stream when it was last seen, if applicable and saved */ + int mute; /**< The boolean mute state of the stream when it was last seen, if applicable and saved */ } pa_ext_stream_restore_info; /** Callback prototype for pa_ext_stream_restore_test(). \since 0.9.12 */ diff --git a/src/pulse/operation.c b/src/pulse/operation.c index 13b470a8..aa2bbc05 100644 --- a/src/pulse/operation.c +++ b/src/pulse/operation.c @@ -62,6 +62,7 @@ pa_operation *pa_operation_ref(pa_operation *o) { PA_REFCNT_INC(o); return o; } + void pa_operation_unref(pa_operation *o) { pa_assert(o); pa_assert(PA_REFCNT_VALUE(o) >= 1); diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 282fe5cc..db4c9344 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -24,6 +24,7 @@ #endif #include <string.h> +#include <ctype.h> #include <pulse/xmalloc.h> #include <pulse/utf8.h> @@ -46,7 +47,7 @@ struct property { static pa_bool_t property_name_valid(const char *key) { - if (!pa_utf8_valid(key)) + if (!pa_ascii_valid(key)) return FALSE; if (strlen(key) <= 0) @@ -64,8 +65,6 @@ static void property_free(struct property *prop) { } pa_proplist* pa_proplist_new(void) { - pa_init_i18n(); - return MAKE_PROPLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func)); } @@ -83,6 +82,7 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { pa_assert(p); pa_assert(key); + pa_assert(value); if (!property_name_valid(key) || !pa_utf8_valid(value)) return -1; @@ -104,25 +104,130 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) { } /** Will accept only valid UTF-8 */ +static int proplist_setn(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) { + struct property *prop; + pa_bool_t add = FALSE; + char *k, *v; + + pa_assert(p); + pa_assert(key); + pa_assert(value); + + k = pa_xstrndup(key, key_length); + v = pa_xstrndup(value, value_length); + + if (!property_name_valid(k) || !pa_utf8_valid(v)) { + pa_xfree(k); + pa_xfree(v); + return -1; + } + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) { + prop = pa_xnew(struct property, 1); + prop->key = k; + add = TRUE; + } else { + pa_xfree(prop->value); + pa_xfree(k); + } + + prop->value = v; + prop->nbytes = strlen(v)+1; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; +} + +static int proplist_sethex(pa_proplist *p, const char *key, size_t key_length, const char *value, size_t value_length) { + struct property *prop; + pa_bool_t add = FALSE; + char *k, *v; + uint8_t *d; + size_t dn; + + pa_assert(p); + pa_assert(key); + pa_assert(value); + + k = pa_xstrndup(key, key_length); + + if (!property_name_valid(k)) { + pa_xfree(k); + return -1; + } + + v = pa_xstrndup(value, value_length); + d = pa_xmalloc(value_length*2+1); + + if ((dn = pa_parsehex(v, d, value_length*2)) == (size_t) -1) { + pa_xfree(k); + pa_xfree(v); + pa_xfree(d); + return -1; + } + + pa_xfree(v); + + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), k))) { + prop = pa_xnew(struct property, 1); + prop->key = k; + add = TRUE; + } else { + pa_xfree(prop->value); + pa_xfree(k); + } + + d[dn] = 0; + prop->value = d; + prop->nbytes = dn; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; +} + +/** Will accept only valid UTF-8 */ int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...) { + struct property *prop; + pa_bool_t add = FALSE; va_list ap; - int r; - char *t; + char *v; pa_assert(p); pa_assert(key); + pa_assert(format); if (!property_name_valid(key) || !pa_utf8_valid(format)) return -1; va_start(ap, format); - t = pa_vsprintf_malloc(format, ap); + v = pa_vsprintf_malloc(format, ap); va_end(ap); - r = pa_proplist_sets(p, key, t); + if (!pa_utf8_valid(v)) + goto fail; - pa_xfree(t); - return r; + if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) { + prop = pa_xnew(struct property, 1); + prop->key = pa_xstrdup(key); + add = TRUE; + } else + pa_xfree(prop->value); + + prop->value = v; + prop->nbytes = strlen(v)+1; + + if (add) + pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop); + + return 0; + +fail: + pa_xfree(v); + return -1; } int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nbytes) { @@ -131,6 +236,7 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb pa_assert(p); pa_assert(key); + pa_assert(data); if (!property_name_valid(key)) return -1; @@ -142,7 +248,9 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb } else pa_xfree(prop->value); - prop->value = pa_xmemdup(data, nbytes); + prop->value = pa_xmalloc(nbytes+1); + memcpy(prop->value, data, nbytes); + ((char*) prop->value)[nbytes] = 0; prop->nbytes = nbytes; if (add) @@ -183,6 +291,8 @@ int pa_proplist_get(pa_proplist *p, const char *key, const void **data, size_t * pa_assert(p); pa_assert(key); + pa_assert(data); + pa_assert(nbytes); if (!property_name_valid(key)) return -1; @@ -275,9 +385,32 @@ char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) { if (!pa_strbuf_isempty(buf)) pa_strbuf_puts(buf, sep); - if ((v = pa_proplist_gets(p, key))) - pa_strbuf_printf(buf, "%s = \"%s\"", key, v); - else { + if ((v = pa_proplist_gets(p, key))) { + const char *t; + + pa_strbuf_printf(buf, "%s = \"", key); + + for (t = v;;) { + size_t h; + + h = strcspn(t, "\""); + + if (h > 0) + pa_strbuf_putsn(buf, t, h); + + t += h; + + if (*t == 0) + break; + + pa_assert(*t == '"'); + pa_strbuf_puts(buf, "\\\""); + + t++; + } + + pa_strbuf_puts(buf, "\""); + } else { const void *value; size_t nbytes; char *c; @@ -304,88 +437,189 @@ char *pa_proplist_to_string(pa_proplist *p) { return t; } -/* Remove all whitepsapce from the beginning and the end of *s. *s may - * be modified. (from conf-parser.c) */ -#define WHITESPACE " \t\n" -#define in_string(c,s) (strchr(s,c) != NULL) - -static char *strip(char *s) { - char *b = s+strspn(s, WHITESPACE); - char *e, *l = NULL; - - for (e = b; *e; e++) - if (!in_string(*e, WHITESPACE)) - l = e; +pa_proplist *pa_proplist_from_string(const char *s) { + enum { + WHITESPACE, + KEY, + AFTER_KEY, + VALUE_START, + VALUE_SIMPLE, + VALUE_DOUBLE_QUOTES, + VALUE_DOUBLE_QUOTES_ESCAPE, + VALUE_TICKS, + VALUE_TICKS_ESCAPED, + VALUE_HEX + } state; + + pa_proplist *pl; + const char *p, *key = NULL, *value = NULL; + size_t key_len = 0, value_len = 0; + + pa_assert(s); + + pl = pa_proplist_new(); + + state = WHITESPACE; + + for (p = s;; p++) { + switch (state) { + + case WHITESPACE: + if (*p == 0) + goto success; + else if (*p == '=') + goto fail; + else if (!isspace(*p)) { + key = p; + state = KEY; + key_len = 1; + } + break; - if (l) - *(l+1) = 0; + case KEY: + if (*p == 0) + goto fail; + else if (*p == '=') + state = VALUE_START; + else if (isspace(*p)) + state = AFTER_KEY; + else + key_len++; + break; - return b; -} + case AFTER_KEY: + if (*p == 0) + goto fail; + else if (*p == '=') + state = VALUE_START; + else if (!isspace(*p)) + goto fail; + break; -pa_proplist *pa_proplist_from_string(const char *str) { - pa_proplist *p; - char *s, *v, *k, *e; + case VALUE_START: + if (*p == 0) + goto fail; + else if (strncmp(p, "hex:", 4) == 0) { + state = VALUE_HEX; + value = p+4; + value_len = 0; + p += 3; + } else if (*p == '\'') { + state = VALUE_TICKS; + value = p+1; + value_len = 0; + } else if (*p == '"') { + state = VALUE_DOUBLE_QUOTES; + value = p+1; + value_len = 0; + } else if (!isspace(*p)) { + state = VALUE_SIMPLE; + value = p; + value_len = 1; + } + break; - pa_assert(str); - pa_assert_se(p = pa_proplist_new()); - pa_assert_se(s = strdup(str)); + case VALUE_SIMPLE: + if (*p == 0 || isspace(*p)) { + if (proplist_setn(pl, key, key_len, value, value_len) < 0) + goto fail; - for (k = s; *k; k = e) { - k = k+strspn(k, WHITESPACE); + if (*p == 0) + goto success; - if (!*k) - break; + state = WHITESPACE; + } else + value_len++; + break; - if (!(v = strchr(k, '='))) { - pa_log("Missing '='."); - break; - } + case VALUE_DOUBLE_QUOTES: + if (*p == 0) + goto fail; + else if (*p == '"') { + char *v; + + v = pa_unescape(pa_xstrndup(value, value_len)); + + if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) { + pa_xfree(v); + goto fail; + } + + pa_xfree(v); + state = WHITESPACE; + } else if (*p == '\\') { + state = VALUE_DOUBLE_QUOTES_ESCAPE; + value_len++; + } else + value_len++; + break; - *v++ = '\0'; - k = strip(k); + case VALUE_DOUBLE_QUOTES_ESCAPE: + if (*p == 0) + goto fail; + else { + state = VALUE_DOUBLE_QUOTES; + value_len++; + } + break; - v = v+strspn(v, WHITESPACE); - if (*v == '"') { - v++; - if (!(e = strchr(v, '"'))) { /* FIXME: handle escape */ - pa_log("Missing '\"' at end of string value."); + case VALUE_TICKS: + if (*p == 0) + goto fail; + else if (*p == '\'') { + char *v; + + v = pa_unescape(pa_xstrndup(value, value_len)); + + if (proplist_setn(pl, key, key_len, v, strlen(v)) < 0) { + pa_xfree(v); + goto fail; + } + + pa_xfree(v); + state = WHITESPACE; + } else if (*p == '\\') { + state = VALUE_TICKS_ESCAPED; + value_len++; + } else + value_len++; break; - } - *e++ = '\0'; - pa_proplist_sets(p, k, v); - } else { - uint8_t *blob; - if (*v++ != 'h' || *v++ != 'e' || *v++ != 'x' || *v++ != ':') { - pa_log("Value must be a string or \"hex:\""); + case VALUE_TICKS_ESCAPED: + if (*p == 0) + goto fail; + else { + state = VALUE_TICKS; + value_len++; + } break; - } - e = v; - while (in_string(*e, "0123456789abcdefABCDEF")) - ++e; + case VALUE_HEX: + if ((*p >= '0' && *p <= '9') || + (*p >= 'A' && *p <= 'F') || + (*p >= 'a' && *p <= 'f')) { + value_len++; + } else if (*p == 0 || isspace(*p)) { - if ((e - v) % 2) { - pa_log("Invalid \"hex:\" value data"); - break; - } + if (proplist_sethex(pl, key, key_len, value, value_len) < 0) + goto fail; - blob = pa_xmalloc((size_t)(e-v)/2); - if (pa_parsehex(v, blob, (e-v)/2) != (size_t)((e-v)/2)) { - pa_log("Invalid \"hex:\" value data"); - pa_xfree(blob); - break; - } + if (*p == 0) + goto success; - pa_proplist_set(p, k, blob, (e-v)/2); - pa_xfree(blob); + state = WHITESPACE; + } else + goto fail; + break; } } - pa_xfree(s); +success: + return MAKE_PROPLIST(pl); - return p; +fail: + pa_proplist_free(pl); + return NULL; } int pa_proplist_contains(pa_proplist *p, const char *key) { diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 990ffd19..d6114062 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -136,7 +136,7 @@ PA_C_DECL_BEGIN #define PA_PROP_MODULE_USAGE "module.usage" #define PA_PROP_MODULE_VERSION "module.version" -/** A property list object. Basically a dictionary with UTF-8 strings +/** A property list object. Basically a dictionary with ASCII strings * as keys and arbitrary data as values. \since 0.9.11 */ typedef struct pa_proplist pa_proplist; diff --git a/src/pulse/sample.c b/src/pulse/sample.c index a6c77345..4b13a337 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -36,7 +36,7 @@ #include "sample.h" -size_t pa_sample_size(const pa_sample_spec *spec) { +size_t pa_sample_size_of_format(pa_sample_format_t f) { static const size_t table[] = { [PA_SAMPLE_U8] = 1, @@ -54,32 +54,44 @@ size_t pa_sample_size(const pa_sample_spec *spec) { [PA_SAMPLE_S24_32BE] = 4 }; + pa_assert(f >= 0); + pa_assert(f < PA_SAMPLE_MAX); + + return table[f]; +} + +size_t pa_sample_size(const pa_sample_spec *spec) { + pa_assert(spec); - pa_assert(spec->format >= 0); - pa_assert(spec->format < PA_SAMPLE_MAX); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); - return table[spec->format]; + return pa_sample_size_of_format(spec->format); } size_t pa_frame_size(const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); return pa_sample_size(spec) * spec->channels; } size_t pa_bytes_per_second(const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); + return spec->rate*pa_frame_size(spec); } pa_usec_t pa_bytes_to_usec(uint64_t length, const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); return (((pa_usec_t) (length / pa_frame_size(spec)) * PA_USEC_PER_SEC) / spec->rate); } size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) { pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * pa_frame_size(spec); } @@ -112,6 +124,9 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_sample_spec_valid(a), 0); + pa_return_val_if_fail(pa_sample_spec_valid(b), 0); + return (a->format == b->format) && (a->rate == b->rate) && @@ -143,7 +158,7 @@ const char *pa_sample_format_to_string(pa_sample_format_t f) { char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { pa_assert(s); - pa_assert(l); + pa_assert(l > 0); pa_assert(spec); pa_init_i18n(); @@ -151,22 +166,25 @@ char *pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec) { if (!pa_sample_spec_valid(spec)) pa_snprintf(s, l, _("(invalid)")); else - pa_snprintf(s, l, "%s %uch %uHz", pa_sample_format_to_string(spec->format), spec->channels, spec->rate); + pa_snprintf(s, l, _("%s %uch %uHz"), pa_sample_format_to_string(spec->format), spec->channels, spec->rate); return s; } char* pa_bytes_snprint(char *s, size_t l, unsigned v) { pa_assert(s); + pa_assert(l > 0); + + pa_init_i18n(); if (v >= ((unsigned) 1024)*1024*1024) - pa_snprintf(s, l, "%0.1f GiB", ((double) v)/1024/1024/1024); + pa_snprintf(s, l, _("%0.1f GiB"), ((double) v)/1024/1024/1024); else if (v >= ((unsigned) 1024)*1024) - pa_snprintf(s, l, "%0.1f MiB", ((double) v)/1024/1024); + pa_snprintf(s, l, _("%0.1f MiB"), ((double) v)/1024/1024); else if (v >= (unsigned) 1024) - pa_snprintf(s, l, "%0.1f KiB", ((double) v)/1024); + pa_snprintf(s, l, _("%0.1f KiB"), ((double) v)/1024); else - pa_snprintf(s, l, "%u B", (unsigned) v); + pa_snprintf(s, l, _("%u B"), (unsigned) v); return s; } diff --git a/src/pulse/sample.h b/src/pulse/sample.h index 45a481fe..3c05b54a 100644 --- a/src/pulse/sample.h +++ b/src/pulse/sample.h @@ -261,6 +261,10 @@ size_t pa_frame_size(const pa_sample_spec *spec) PA_GCC_PURE; /** Return the size of a sample with the specific sample type */ size_t pa_sample_size(const pa_sample_spec *spec) PA_GCC_PURE; +/** Similar to pa_sample_size() but take a sample format instead of a + * full sample spec. \since 0.9.15 */ +size_t pa_sample_size_of_format(pa_sample_format_t f) PA_GCC_PURE; + /** Calculate the time the specified bytes take to play with the * specified sample type. The return value will always be rounded * down for non-integral return values. */ diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c index 7671be46..6b58bde3 100644 --- a/src/pulse/utf8.c +++ b/src/pulse/utf8.c @@ -263,3 +263,29 @@ char* pa_locale_to_utf8 (const char *str) { } #endif + +char *pa_ascii_valid(const char *str) { + const char *p; + pa_assert(str); + + for (p = str; *p; p++) + if ((unsigned char) *p >= 128) + return NULL; + + return (char*) str; +} + +char *pa_ascii_filter(const char *str) { + char *r, *s, *d; + pa_assert(str); + + r = pa_xstrdup(str); + + for (s = r, d = r; *s; s++) + if ((unsigned char) *s < 128) + *(d++) = *s; + + *d = 0; + + return r; +} diff --git a/src/pulse/utf8.h b/src/pulse/utf8.h index 4d751953..b9f74952 100644 --- a/src/pulse/utf8.h +++ b/src/pulse/utf8.h @@ -36,9 +36,15 @@ PA_C_DECL_BEGIN /** Test if the specified strings qualifies as valid UTF8. Return the string if so, otherwise NULL */ char *pa_utf8_valid(const char *str) PA_GCC_PURE; +/** Test if the specified strings qualifies as valid 7-bit ASCII. Return the string if so, otherwise NULL. \since 0.9.15 */ +char *pa_ascii_valid(const char *str) PA_GCC_PURE; + /** Filter all invalid UTF8 characters from the specified string, returning a new fully UTF8 valid string. Don't forget to free the returned string with pa_xfree() */ char *pa_utf8_filter(const char *str); +/** Filter all invalid ASCII characters from the specified string, returning a new fully ASCII valid string. Don't forget to free the returned string with pa_xfree(). \since 0.9.15 */ +char *pa_ascii_filter(const char *str); + /** Convert a UTF-8 string to the current locale. Free the string using pa_xfree(). */ char* pa_utf8_to_locale (const char *str); diff --git a/src/pulse/volume.c b/src/pulse/volume.c index 01a28e83..54838e89 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -37,6 +37,9 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + pa_return_val_if_fail(pa_cvolume_valid(b), 0); + if (a->channels != b->channels) return 0; @@ -78,7 +81,9 @@ pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v) { pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { uint64_t sum = 0; int i; + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); for (i = 0; i < a->channels; i++) sum += a->values[i]; @@ -91,7 +96,9 @@ pa_volume_t pa_cvolume_avg(const pa_cvolume *a) { pa_volume_t pa_cvolume_max(const pa_cvolume *a) { pa_volume_t m = 0; int i; + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); for (i = 0; i < a->channels; i++) if (a->values[i] > m) @@ -247,11 +254,12 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) { return s; } -/** Return non-zero if the volume of all channels is equal to the specified value */ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) { unsigned c; pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), 0); + for (c = 0; c < a->channels; c++) if (a->values[c] != v) return 0; @@ -266,6 +274,9 @@ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]); @@ -281,6 +292,9 @@ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa pa_assert(a); pa_assert(b); + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + for (i = 0; i < a->channels && i < b->channels && i < PA_CHANNELS_MAX; i++) dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]); @@ -373,7 +387,11 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa pa_assert(v); pa_assert(from); pa_assert(to); - pa_assert(v->channels == from->channels); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(pa_channel_map_valid(from), NULL); + pa_return_val_if_fail(pa_channel_map_valid(to), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, from), NULL); if (pa_channel_map_equal(from, to)) return v; @@ -419,15 +437,22 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { pa_assert(v); pa_assert(ss); - if (!pa_cvolume_valid(v)) - return 0; - - if (!pa_sample_spec_valid(ss)) - return 0; + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_sample_spec_valid(ss), 0); return v->channels == ss->channels; } +int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) { + pa_assert(v); + pa_assert(cm); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0); + pa_return_val_if_fail(pa_channel_map_valid(cm), 0); + + return v->channels == cm->channels; +} + static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r) { int c; pa_volume_t left = 0, right = 0; @@ -465,7 +490,10 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) { pa_assert(v); pa_assert(map); - pa_assert(map->channels == v->channels); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f); + pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); if (!pa_channel_map_can_balance(map)) return 0.0f; @@ -494,12 +522,15 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo pa_volume_t left, nleft, right, nright, m; unsigned c; - pa_assert(map->channels == v->channels); pa_assert(map); pa_assert(v); pa_assert(new_balance >= -1.0f); pa_assert(new_balance <= 1.0f); + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + if (!pa_channel_map_can_balance(map)) return v; @@ -538,6 +569,9 @@ pa_cvolume* pa_cvolume_scale(pa_cvolume *v, pa_volume_t max) { pa_assert(v); + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(max != (pa_volume_t) -1, NULL); + for (c = 0; c < v->channels; c++) if (v->values[c] > t) t = v->values[c]; @@ -588,7 +622,10 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) { pa_assert(v); pa_assert(map); - pa_assert(map->channels == v->channels); + + pa_return_val_if_fail(pa_cvolume_valid(v), 0.0f); + pa_return_val_if_fail(pa_channel_map_valid(map), 0.0f); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), 0.0f); if (!pa_channel_map_can_fade(map)) return 0.0f; @@ -608,12 +645,15 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float pa_volume_t front, nfront, rear, nrear, m; unsigned c; - pa_assert(map->channels == v->channels); pa_assert(map); pa_assert(v); pa_assert(new_fade >= -1.0f); pa_assert(new_fade <= 1.0f); + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + pa_return_val_if_fail(pa_channel_map_valid(map), NULL); + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, map), NULL); + if (!pa_channel_map_can_fade(map)) return v; diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 9ad3e9bf..8bfd0687 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -230,10 +230,14 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST; /** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa_channel_map *to); -/** Return non-zero if the specified volume is compatible with - * the specified sample spec. \since 0.9.13 */ +/** Return non-zero if the specified volume is compatible with the + * specified sample spec. \since 0.9.13 */ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE; +/** Return non-zero if the specified volume is compatible with the + * specified sample spec. \since 0.9.15 */ +int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v, const pa_channel_map *cm) PA_GCC_PURE; + /** Calculate a 'balance' value for the specified volume with the * specified channel map. The return value will range from -1.0f * (left) to +1.0f (right). If no balance value is applicable to this diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c index e65b1796..b7ebdeb7 100644 --- a/src/pulsecore/core-util.c +++ b/src/pulsecore/core-util.c @@ -97,6 +97,7 @@ #include <pulsecore/log.h> #include <pulsecore/macro.h> #include <pulsecore/thread.h> +#include <pulsecore/strbuf.h> #include "core-util.h" @@ -2553,3 +2554,49 @@ unsigned pa_ncpus(void) { return ncpus <= 0 ? 1 : (unsigned) ncpus; } + +char *pa_replace(const char*s, const char*a, const char *b) { + pa_strbuf *sb; + size_t an; + + pa_assert(s); + pa_assert(a); + pa_assert(b); + + an = strlen(a); + sb = pa_strbuf_new(); + + for (;;) { + const char *p; + + if (!(p = strstr(s, a))) + break; + + pa_strbuf_putsn(sb, s, p-s); + pa_strbuf_puts(sb, b); + s = p + an; + } + + pa_strbuf_puts(sb, s); + + return pa_strbuf_tostring_free(sb); +} + +char *pa_unescape(char *p) { + char *s, *d; + pa_bool_t escaped = FALSE; + + for (s = p, d = p; *s; s++) { + if (!escaped && *s == '\\') { + escaped = TRUE; + continue; + } + + *(d++) = *s; + escaped = FALSE; + } + + *d = 0; + + return p; +} diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h index 18901f47..442815f1 100644 --- a/src/pulsecore/core-util.h +++ b/src/pulsecore/core-util.h @@ -215,4 +215,8 @@ void pa_reduce(unsigned *num, unsigned *den); unsigned pa_ncpus(void); +char *pa_replace(const char*s, const char*a, const char *b); + +char *pa_unescape(char *p); + #endif diff --git a/src/pulsecore/envelope.c b/src/pulsecore/envelope.c index b57b6435..fd6a9487 100644 --- a/src/pulsecore/envelope.c +++ b/src/pulsecore/envelope.c @@ -744,6 +744,13 @@ void pa_envelope_apply(pa_envelope *e, pa_memchunk *chunk) { break; } + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_S24_32BE: + /* FIXME */ + pa_assert_not_reached(); + case PA_SAMPLE_MAX: case PA_SAMPLE_INVALID: pa_assert_not_reached(); diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c index 9e60125e..866e6e0c 100644 --- a/src/pulsecore/modargs.c +++ b/src/pulsecore/modargs.c @@ -79,106 +79,111 @@ static int add_key_value(pa_hashmap *map, char *key, char *value, const char* co } pa_modargs *pa_modargs_new(const char *args, const char* const* valid_keys) { - pa_hashmap *map = NULL; + enum { + WHITESPACE, + KEY, + VALUE_START, + VALUE_SIMPLE, + VALUE_DOUBLE_QUOTES, + VALUE_TICKS + } state; + + const char *p, *key = NULL, *value = NULL; + size_t key_len = 0, value_len = 0; + pa_hashmap *map; map = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - if (args) { - enum { - WHITESPACE, - KEY, - VALUE_START, - VALUE_SIMPLE, - VALUE_DOUBLE_QUOTES, - VALUE_TICKS - } state; - - const char *p, *key, *value; - size_t key_len = 0, value_len = 0; - - key = value = NULL; - state = WHITESPACE; - for (p = args; *p; p++) { - switch (state) { - case WHITESPACE: - if (*p == '=') + if (!args) + return (pa_modargs*) map; + + state = WHITESPACE; + + for (p = args; *p; p++) { + switch (state) { + + case WHITESPACE: + if (*p == '=') + goto fail; + else if (!isspace(*p)) { + key = p; + state = KEY; + key_len = 1; + } + break; + + case KEY: + if (*p == '=') + state = VALUE_START; + else if (isspace(*p)) + goto fail; + else + key_len++; + break; + + case VALUE_START: + if (*p == '\'') { + state = VALUE_TICKS; + value = p+1; + value_len = 0; + } else if (*p == '"') { + state = VALUE_DOUBLE_QUOTES; + value = p+1; + value_len = 0; + } else if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) goto fail; - else if (!isspace(*p)) { - key = p; - state = KEY; - key_len = 1; - } - break; - case KEY: - if (*p == '=') - state = VALUE_START; - else if (isspace(*p)) + state = WHITESPACE; + } else { + state = VALUE_SIMPLE; + value = p; + value_len = 1; + } + break; + + case VALUE_SIMPLE: + if (isspace(*p)) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) goto fail; - else - key_len++; - break; - case VALUE_START: - if (*p == '\'') { - state = VALUE_TICKS; - value = p+1; - value_len = 0; - } else if (*p == '"') { - state = VALUE_DOUBLE_QUOTES; - value = p+1; - value_len = 0; - } else if (isspace(*p)) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else { - state = VALUE_SIMPLE; - value = p; - value_len = 1; - } - break; - case VALUE_SIMPLE: - if (isspace(*p)) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_DOUBLE_QUOTES: - if (*p == '"') { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - case VALUE_TICKS: - if (*p == '\'') { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) - goto fail; - state = WHITESPACE; - } else - value_len++; - break; - } + state = WHITESPACE; + } else + value_len++; + break; + + case VALUE_DOUBLE_QUOTES: + if (*p == '"') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; + + case VALUE_TICKS: + if (*p == '\'') { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrndup(value, value_len), valid_keys) < 0) + goto fail; + state = WHITESPACE; + } else + value_len++; + break; } + } - if (state == VALUE_START) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) - goto fail; - } else if (state == VALUE_SIMPLE) { - if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0) - goto fail; - } else if (state != WHITESPACE) + if (state == VALUE_START) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(""), valid_keys) < 0) goto fail; - } + } else if (state == VALUE_SIMPLE) { + if (add_key_value(map, pa_xstrndup(key, key_len), pa_xstrdup(value), valid_keys) < 0) + goto fail; + } else if (state != WHITESPACE) + goto fail; return (pa_modargs*) map; fail: - if (map) - pa_modargs_free((pa_modargs*) map); + pa_modargs_free((pa_modargs*) map); return NULL; } diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c index d1631264..44aa6bf0 100644 --- a/src/pulsecore/play-memblockq.c +++ b/src/pulsecore/play-memblockq.c @@ -197,7 +197,7 @@ pa_sink_input* pa_memblockq_sink_input_new( data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); - pa_sink_input_new_data_set_virtual_volume(&data, volume); + pa_sink_input_new_data_set_volume(&data, volume); pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); pa_sink_input_new(&u->sink_input, sink->core, &data, 0); diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c index 4920c27e..522c7afe 100644 --- a/src/pulsecore/proplist-util.c +++ b/src/pulsecore/proplist-util.c @@ -41,6 +41,7 @@ void pa_init_proplist(pa_proplist *p) { extern char **environ; #endif char **e; + const char *pp; pa_assert(p); @@ -75,6 +76,15 @@ void pa_init_proplist(pa_proplist *p) { } } + if ((pp = getenv("PULSE_PROP"))) { + pa_proplist *t; + + if ((t = pa_proplist_from_string(pp))) { + pa_proplist_update(p, PA_UPDATE_MERGE, t); + pa_proplist_free(t); + } + } + if (!pa_proplist_contains(p, PA_PROP_APPLICATION_PROCESS_ID)) { char t[32]; pa_snprintf(t, sizeof(t), "%lu", (unsigned long) getpid()); diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 21a2cfbc..39c834d8 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -595,7 +595,7 @@ static record_stream* record_stream_new( int *ret) { record_stream *s; - pa_source_output *source_output; + pa_source_output *source_output = NULL; size_t base; pa_source_output_new_data data; @@ -618,7 +618,7 @@ static record_stream* record_stream_new( if (peak_detect) data.resample_method = PA_RESAMPLER_PEAKS; - *ret = pa_source_output_new(&source_output, c->protocol->core, &data, flags); + *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags); pa_source_output_new_data_done(&data); @@ -971,7 +971,7 @@ static playback_stream* playback_stream_new( int *ret) { playback_stream *s, *ssync; - pa_sink_input *sink_input; + pa_sink_input *sink_input = NULL; pa_memchunk silence; uint32_t idx; int64_t start_index; @@ -1018,12 +1018,12 @@ static playback_stream* playback_stream_new( pa_sink_input_new_data_set_sample_spec(&data, ss); pa_sink_input_new_data_set_channel_map(&data, map); if (volume) - pa_sink_input_new_data_set_virtual_volume(&data, volume); + pa_sink_input_new_data_set_volume(&data, volume); if (muted_set) pa_sink_input_new_data_set_muted(&data, muted); data.sync_base = ssync ? ssync->sink_input : NULL; - *ret = pa_sink_input_new(&sink_input, c->protocol->core, &data, flags); + *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags); pa_sink_input_new_data_done(&data); diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index be390db7..78ad5530 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -156,16 +156,6 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_PEAKS] = peaks_init, }; -static inline size_t sample_size(pa_sample_format_t f) { - pa_sample_spec ss = { - .format = f, - .rate = 0, - .channels = 1 - }; - - return pa_sample_size(&ss); -} - pa_resampler* pa_resampler_new( pa_mempool *pool, const pa_sample_spec *a, @@ -275,7 +265,7 @@ pa_resampler* pa_resampler_new( pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format)); - r->w_sz = sample_size(r->work_format); + r->w_sz = pa_sample_size_of_format(r->work_format); if (r->i_ss.format == r->work_format) r->to_work_format_func = NULL; diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index fc87d5d8..df42cae8 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -72,18 +72,11 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const data->channel_map = *map; } -void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { +void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); - if ((data->soft_volume_is_set = !!volume)) - data->soft_volume = *volume; -} - -void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { - pa_assert(data); - - if ((data->virtual_volume_is_set = !!volume)) - data->virtual_volume = *volume; + if ((data->volume_is_set = !!volume)) + data->volume = *volume; } void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) { @@ -136,6 +129,9 @@ int pa_sink_input_new( pa_assert(core); pa_assert(data); + if (data->client) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0) return r; @@ -166,35 +162,20 @@ int pa_sink_input_new( pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID); pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); - if (!data->virtual_volume_is_set) { + if (!data->volume_is_set) { if (data->sink->flags & PA_SINK_FLAT_VOLUME) { - data->virtual_volume = data->sink->virtual_volume; - pa_cvolume_remap(&data->virtual_volume, &data->sink->channel_map, &data->channel_map); + data->volume = *pa_sink_get_volume(data->sink, FALSE); + pa_cvolume_remap(&data->volume, &data->sink->channel_map, &data->channel_map); } else - pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels); + pa_cvolume_reset(&data->volume, data->sample_spec.channels); data->save_volume = FALSE; - } else if (!data->virtual_volume_is_absolute) { - - /* When the 'absolute' bool is set then we'll treat the volume - * as relative to the sink volume even in flat volume mode */ - if (data->sink->flags & PA_SINK_FLAT_VOLUME) { - pa_cvolume t = data->sink->virtual_volume; - pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map); - pa_sw_cvolume_multiply(&data->virtual_volume, &data->virtual_volume, &t); - } } - pa_return_val_if_fail(pa_cvolume_valid(&data->virtual_volume), -PA_ERR_INVALID); - pa_return_val_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec), -PA_ERR_INVALID); - - if (!data->soft_volume_is_set) - data->soft_volume = data->virtual_volume; - - pa_return_val_if_fail(pa_cvolume_valid(&data->soft_volume), -PA_ERR_INVALID); - pa_return_val_if_fail(pa_cvolume_compatible(&data->soft_volume, &data->sample_spec), -PA_ERR_INVALID); + pa_return_val_if_fail(pa_cvolume_valid(&data->volume), -PA_ERR_INVALID); + pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID); if (!data->muted_is_set) data->muted = FALSE; @@ -216,17 +197,13 @@ int pa_sink_input_new( pa_assert(pa_channel_map_valid(&data->channel_map)); /* Due to the fixing of the sample spec the volume might not match anymore */ - pa_cvolume_remap(&data->soft_volume, &original_cm, &data->channel_map); - pa_cvolume_remap(&data->virtual_volume, &original_cm, &data->channel_map); + pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map); if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID); - if (data->client) - pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); - if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], data)) < 0) return r; @@ -271,8 +248,18 @@ int pa_sink_input_new( i->sample_spec = data->sample_spec; i->channel_map = data->channel_map; - i->virtual_volume = data->virtual_volume; - i->soft_volume = data->soft_volume; + if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) { + /* When the 'absolute' bool is not set then we'll treat the volume + * as relative to the sink volume even in flat volume mode */ + + pa_cvolume t = *pa_sink_get_volume(data->sink, FALSE); + pa_cvolume_remap(&t, &data->sink->channel_map, &data->channel_map); + + pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &t); + } else + i->virtual_volume = data->volume; + + pa_cvolume_init(&i->soft_volume); i->save_volume = data->save_volume; i->save_sink = data->save_sink; i->save_muted = data->save_muted; @@ -502,9 +489,6 @@ void pa_sink_input_put(pa_sink_input *i) { pa_assert(i->process_rewind); pa_assert(i->kill); - i->thread_info.soft_volume = i->soft_volume; - i->thread_info.muted = i->muted; - state = i->flags & PA_SINK_INPUT_START_CORKED ? PA_SINK_INPUT_CORKED : PA_SINK_INPUT_RUNNING; update_n_corked(i, state); @@ -515,7 +499,11 @@ void pa_sink_input_put(pa_sink_input *i) { pa_cvolume new_volume; pa_sink_update_flat_volume(i->sink, &new_volume); pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE); - } + } else + i->soft_volume = i->virtual_volume; + + i->thread_info.soft_volume = i->soft_volume; + i->thread_info.muted = i->muted; pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_ADD_INPUT, i, 0, NULL) == 0); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index d82a3a62..f6c5aa1d 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -233,16 +233,16 @@ typedef struct pa_sink_input_new_data { pa_sample_spec sample_spec; pa_channel_map channel_map; - pa_cvolume virtual_volume, soft_volume; + pa_cvolume volume; pa_bool_t muted:1; pa_bool_t sample_spec_is_set:1; pa_bool_t channel_map_is_set:1; - pa_bool_t virtual_volume_is_set:1, soft_volume_is_set:1; + pa_bool_t volume_is_set:1; pa_bool_t muted_is_set:1; - pa_bool_t virtual_volume_is_absolute:1; + pa_bool_t volume_is_absolute:1; pa_bool_t save_sink:1, save_volume:1, save_muted:1; } pa_sink_input_new_data; @@ -250,8 +250,7 @@ typedef struct pa_sink_input_new_data { pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); -void pa_sink_input_new_data_set_soft_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); -void pa_sink_input_new_data_set_virtual_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); +void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute); void pa_sink_input_new_data_done(pa_sink_input_new_data *data); diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c index 67b072b6..c3de3067 100644 --- a/src/pulsecore/sound-file-stream.c +++ b/src/pulsecore/sound-file-stream.c @@ -322,7 +322,7 @@ int pa_play_file( data.sink = sink; data.driver = __FILE__; pa_sink_input_new_data_set_sample_spec(&data, &ss); - pa_sink_input_new_data_set_virtual_volume(&data, volume); + pa_sink_input_new_data_set_volume(&data, volume); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_NAME, pa_path_get_filename(fname)); pa_proplist_sets(data.proplist, PA_PROP_MEDIA_FILENAME, fname); diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index c5cb16d8..63d56d5a 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -110,6 +110,9 @@ int pa_source_output_new( pa_assert(core); pa_assert(data); + if (data->client) + pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); + if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0) return r; @@ -159,9 +162,6 @@ int pa_source_output_new( pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID); - if (data->client) - pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); - if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0) return r; diff --git a/src/tests/proplist-test.c b/src/tests/proplist-test.c index f69fa686..5526bb7e 100644 --- a/src/tests/proplist-test.c +++ b/src/tests/proplist-test.c @@ -29,8 +29,9 @@ #include <pulsecore/core-util.h> int main(int argc, char*argv[]) { - pa_proplist *a, *b, *c; - char *s, *t, *u; + pa_proplist *a, *b, *c, *d; + char *s, *t, *u, *v; + const char *text; a = pa_proplist_new(); pa_assert_se(pa_proplist_sets(a, PA_PROP_MEDIA_TITLE, "Brandenburgische Konzerte") == 0); @@ -63,5 +64,19 @@ int main(int argc, char*argv[]) { pa_proplist_free(b); pa_proplist_free(c); + text = " eins = zwei drei = \"\\\"vier\\\"\" fuenf=sechs sieben ='\\a\\c\\h\\t\\'\\\"' neun= hex:0123456789abCDef "; + + printf("%s\n", text); + d = pa_proplist_from_string(text); + v = pa_proplist_to_string(d); + pa_proplist_free(d); + printf("%s\n", v); + d = pa_proplist_from_string(v); + pa_xfree(v); + v = pa_proplist_to_string(d); + pa_proplist_free(d); + printf("%s\n", v); + pa_xfree(v); + return 0; } |