summaryrefslogtreecommitdiffstats
path: root/src/pulse
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulse')
-rw-r--r--src/pulse/channelmap.c18
-rw-r--r--src/pulse/channelmap.h18
-rw-r--r--src/pulse/client-conf.c21
-rw-r--r--src/pulse/client.conf.in2
-rw-r--r--src/pulse/context.c46
-rw-r--r--src/pulse/context.h7
-rw-r--r--src/pulse/def.h1
-rw-r--r--src/pulse/error.c4
-rw-r--r--src/pulse/ext-stream-restore.c18
-rw-r--r--src/pulse/glib-mainloop.h4
-rw-r--r--src/pulse/internal.h5
-rw-r--r--src/pulse/introspect.c2
-rw-r--r--src/pulse/introspect.h12
-rw-r--r--src/pulse/mainloop.c13
-rw-r--r--src/pulse/mainloop.h4
-rw-r--r--src/pulse/operation.h6
-rw-r--r--src/pulse/proplist.c5
-rw-r--r--src/pulse/proplist.h20
-rw-r--r--src/pulse/pulseaudio.h16
-rw-r--r--src/pulse/sample.c53
-rw-r--r--src/pulse/sample.h7
-rw-r--r--src/pulse/scache.c14
-rw-r--r--src/pulse/scache.h4
-rw-r--r--src/pulse/simple.c63
-rw-r--r--src/pulse/stream.c162
-rw-r--r--src/pulse/stream.h87
-rw-r--r--src/pulse/thread-mainloop.c13
-rw-r--r--src/pulse/thread-mainloop.h22
-rw-r--r--src/pulse/utf8.c9
-rw-r--r--src/pulse/util.c52
-rw-r--r--src/pulse/volume.c172
-rw-r--r--src/pulse/volume.h44
32 files changed, 655 insertions, 269 deletions
diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c
index 88823012..9b516262 100644
--- a/src/pulse/channelmap.c
+++ b/src/pulse/channelmap.c
@@ -219,11 +219,11 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case 6:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
- m->map[1] = PA_CHANNEL_POSITION_REAR_LEFT;
+ m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT;
- m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
- m->map[5] = PA_CHANNEL_POSITION_LFE;
+ m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+ m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
case 5:
@@ -247,7 +247,7 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
m->map[0] = PA_CHANNEL_POSITION_LEFT;
m->map[1] = PA_CHANNEL_POSITION_CENTER;
m->map[2] = PA_CHANNEL_POSITION_RIGHT;
- m->map[3] = PA_CHANNEL_POSITION_LFE;
+ m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER;
return m;
default:
@@ -299,6 +299,8 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p
case PA_CHANNEL_MAP_WAVEEX:
+ /* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */
+
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
@@ -451,6 +453,10 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) {
pa_assert(b);
pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+
+ if (PA_UNLIKELY(a == b))
+ return 1;
+
pa_return_val_if_fail(pa_channel_map_valid(b), 0);
if (a->channels != b->channels)
@@ -639,6 +645,10 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
pa_assert(b);
pa_return_val_if_fail(pa_channel_map_valid(a), 0);
+
+ if (PA_UNLIKELY(a == b))
+ return 1;
+
pa_return_val_if_fail(pa_channel_map_valid(b), 0);
am = pa_channel_map_mask(a);
diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h
index d7901ac2..469effc8 100644
--- a/src/pulse/channelmap.h
+++ b/src/pulse/channelmap.h
@@ -216,17 +216,27 @@ typedef enum pa_channel_map_def {
PA_CHANNEL_MAP_AIFF,
/**< The mapping from RFC3551, which is based on AIFF-C */
+/** \cond fulldocs */
PA_CHANNEL_MAP_ALSA,
- /**< The default mapping used by ALSA */
+ /**< The default mapping used by ALSA. This mapping is probably
+ * not too useful since ALSA's default channel mapping depends on
+ * the device string used. */
+/** \endcond */
PA_CHANNEL_MAP_AUX,
/**< Only aux channels */
PA_CHANNEL_MAP_WAVEEX,
- /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */
+ /**< Microsoft's WAVEFORMATEXTENSIBLE mapping. This mapping works
+ * as if all LSBs of dwChannelMask are set. */
+/** \cond fulldocs */
PA_CHANNEL_MAP_OSS,
- /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */
+ /**< The default channel mapping used by OSS as defined in the OSS
+ * 4.0 API specs. This mapping is probably not too useful since
+ * the OSS API has changed in this respect and no longer knows a
+ * default channel mapping based on the number of channels. */
+/** \endcond */
/**< Upper limit of valid channel mapping definitions */
PA_CHANNEL_MAP_DEF_MAX,
@@ -282,7 +292,7 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels,
/** Return a text label for the specified channel position */
const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE;
-/* The inverse of pa_channel_position_to_string(). \since 0.9.16 */
+/** The inverse of pa_channel_position_to_string(). \since 0.9.16 */
pa_channel_position_t pa_channel_position_from_string(const char *s) PA_GCC_PURE;
/** Return a human readable text label for the specified channel position. \since 0.9.7 */
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index 940d0b67..4aa4ba1f 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -92,16 +92,17 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
/* Prepare the configuration parse table */
pa_config_item table[] = {
- { "daemon-binary", pa_config_parse_string, &c->daemon_binary, NULL },
- { "extra-arguments", pa_config_parse_string, &c->extra_arguments, NULL },
- { "default-sink", pa_config_parse_string, &c->default_sink, NULL },
- { "default-source", pa_config_parse_string, &c->default_source, NULL },
- { "default-server", pa_config_parse_string, &c->default_server, NULL },
- { "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
- { "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
- { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
- { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
- { NULL, NULL, NULL, NULL },
+ { "daemon-binary", pa_config_parse_string, &c->daemon_binary, NULL },
+ { "extra-arguments", pa_config_parse_string, &c->extra_arguments, NULL },
+ { "default-sink", pa_config_parse_string, &c->default_sink, NULL },
+ { "default-source", pa_config_parse_string, &c->default_source, NULL },
+ { "default-server", pa_config_parse_string, &c->default_server, NULL },
+ { "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
+ { "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
+ { "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
+ { "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
+ { "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
+ { NULL, NULL, NULL, NULL },
};
if (filename) {
diff --git a/src/pulse/client.conf.in b/src/pulse/client.conf.in
index 579bcc20..6c8d371c 100644
--- a/src/pulse/client.conf.in
+++ b/src/pulse/client.conf.in
@@ -29,5 +29,5 @@
; cookie-file =
-; disable-shm = no
+; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 4ded5565..894ab2e0 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -668,11 +668,24 @@ static pa_strlist *prepend_per_user(pa_strlist *l) {
static int context_autospawn(pa_context *c) {
pid_t pid;
int status, r;
-
- pa_log_debug("Trying to autospawn...");
+ struct sigaction sa;
pa_context_ref(c);
+ if (sigaction(SIGCHLD, NULL, &sa) < 0) {
+ pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
+ pa_context_fail(c, PA_ERR_INTERNAL);
+ goto fail;
+ }
+
+ if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
+ pa_log_debug("Process disabled waitpid(), cannot autospawn.");
+ pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
+ goto fail;
+ }
+
+ pa_log_debug("Trying to autospawn...");
+
if (c->spawn_api.prefork)
c->spawn_api.prefork();
@@ -688,23 +701,23 @@ static int context_autospawn(pa_context *c) {
/* Child */
const char *state = NULL;
-#define MAX_ARGS 64
- const char * argv[MAX_ARGS+1];
- int n;
+ const char * argv[32];
+ unsigned n = 0;
if (c->spawn_api.atfork)
c->spawn_api.atfork();
+ /* We leave most of the cleaning up of the process environment
+ * to the executable. We only clean up the file descriptors to
+ * make sure the executable can actually be loaded
+ * correctly. */
pa_close_all(-1);
/* Setup argv */
-
- n = 0;
-
argv[n++] = c->conf->daemon_binary;
argv[n++] = "--start";
- while (n < MAX_ARGS) {
+ while (n < PA_ELEMENTSOF(argv)-1) {
char *a;
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
@@ -714,10 +727,10 @@ static int context_autospawn(pa_context *c) {
}
argv[n++] = NULL;
+ pa_assert(n <= PA_ELEMENTSOF(argv));
execv(argv[0], (char * const *) argv);
_exit(1);
-#undef MAX_ARGS
}
/* Parent */
@@ -730,9 +743,16 @@ static int context_autospawn(pa_context *c) {
} while (r < 0 && errno == EINTR);
if (r < 0) {
- pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
- pa_context_fail(c, PA_ERR_INTERNAL);
- goto fail;
+
+ if (errno != ESRCH) {
+ pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
+ pa_context_fail(c, PA_ERR_INTERNAL);
+ goto fail;
+ }
+
+ /* hmm, something already reaped our child, so we assume
+ * startup worked, even if we cannot know */
+
} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
goto fail;
diff --git a/src/pulse/context.h b/src/pulse/context.h
index cd129313..ecff58df 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -147,12 +147,6 @@
* server. A pa_context object wraps a connection to a PulseAudio
* server using its native protocol. */
-/** \example pacat.c
- * A playback and recording tool using the asynchronous API */
-
-/** \example paplay.c
- * A sound file playback tool using the asynchronous API, based on libsndfile */
-
PA_C_DECL_BEGIN
/** An opaque connection context to a daemon */
@@ -267,7 +261,6 @@ pa_time_event* pa_context_rttime_new(pa_context *c, pa_usec_t usec, pa_time_even
for mainloop->time_restart). \since 0.9.16 */
void pa_context_rttime_restart(pa_context *c, pa_time_event *e, pa_usec_t usec);
-
PA_C_DECL_END
#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 08399ca8..1a7da974 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -394,6 +394,7 @@ enum {
PA_ERR_NOTIMPLEMENTED, /**< Missing implementation. \since 0.9.15 */
PA_ERR_FORKED, /**< The caller forked without calling execve() and tried to reuse the context. \since 0.9.15 */
PA_ERR_IO, /**< An IO error happened. \since 0.9.16 */
+ PA_ERR_BUSY, /**< Device or resource busy. \since 0.9.17 */
PA_ERR_MAX /**< Not really an error but the first invalid error code */
};
diff --git a/src/pulse/error.c b/src/pulse/error.c
index 93a13fc6..e8276990 100644
--- a/src/pulse/error.c
+++ b/src/pulse/error.c
@@ -64,7 +64,9 @@ const char*pa_strerror(int error) {
[PA_ERR_NOEXTENSION] = N_("No such extension"),
[PA_ERR_OBSOLETE] = N_("Obsolete functionality"),
[PA_ERR_NOTIMPLEMENTED] = N_("Missing implementation"),
- [PA_ERR_FORKED] = N_("Client forked")
+ [PA_ERR_FORKED] = N_("Client forked"),
+ [PA_ERR_IO] = N_("Input/Output error"),
+ [PA_ERR_BUSY] = N_("Device or resource busy")
};
pa_init_i18n();
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
index 63c911f8..10e9fd5d 100644
--- a/src/pulse/ext-stream-restore.c
+++ b/src/pulse/ext-stream-restore.c
@@ -239,13 +239,10 @@ pa_operation *pa_ext_stream_restore_write(
return o;
fail:
- if (o) {
- pa_operation_cancel(o);
- pa_operation_unref(o);
- }
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
- if (t)
- pa_tagstruct_free(t);
+ pa_tagstruct_free(t);
pa_context_set_error(c, PA_ERR_INVALID);
return NULL;
@@ -290,13 +287,10 @@ pa_operation *pa_ext_stream_restore_delete(
return o;
fail:
- if (o) {
- pa_operation_cancel(o);
- pa_operation_unref(o);
- }
+ pa_operation_cancel(o);
+ pa_operation_unref(o);
- if (t)
- pa_tagstruct_free(t);
+ pa_tagstruct_free(t);
pa_context_set_error(c, PA_ERR_INVALID);
return NULL;
diff --git a/src/pulse/glib-mainloop.h b/src/pulse/glib-mainloop.h
index 189513a8..67aba27d 100644
--- a/src/pulse/glib-mainloop.h
+++ b/src/pulse/glib-mainloop.h
@@ -56,7 +56,9 @@ pa_glib_mainloop *pa_glib_mainloop_new(GMainContext *c);
/** Free the GLIB main loop object */
void pa_glib_mainloop_free(pa_glib_mainloop* g);
-/** Return the abstract main loop API vtable for the GLIB main loop object */
+/** Return the abstract main loop API vtable for the GLIB main loop
+ object. No need of freeing the API as it is owned by the loop and
+ it is destroyed when this dies */
pa_mainloop_api* pa_glib_mainloop_get_api(pa_glib_mainloop *g);
PA_C_DECL_END
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index ec2da85b..e069c9e9 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -151,6 +151,11 @@ struct pa_stream {
uint32_t device_index;
char *device_name;
+ /* playback */
+ pa_memblock *write_memblock;
+ void *write_data;
+
+ /* recording */
pa_memchunk peek_memchunk;
void *peek_data;
pa_memblockq *record_memblockq;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 27a587cb..100413ee 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -834,6 +834,8 @@ pa_operation* pa_context_get_card_info_by_name(pa_context *c, const char*name, p
}
pa_operation* pa_context_get_card_info_list(pa_context *c, pa_card_info_cb_t cb, void *userdata) {
+ PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15, PA_ERR_NOTSUPPORTED);
+
return pa_context_send_simple_command(c, PA_COMMAND_GET_CARD_INFO_LIST, context_get_card_info_callback, (pa_operation_cb_t) cb, userdata);
}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index ee982100..68cfc874 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -331,6 +331,12 @@ pa_operation* pa_context_set_source_mute_by_index(pa_context *c, uint32_t idx, i
/** Set the mute switch of a source device specified by its name */
pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name, int mute, pa_context_success_cb_t cb, void *userdata);
+/** Suspend/Resume a source. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
+
+/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */
+pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
+
/** Change the profile of a source. \since 0.9.16 */
pa_operation* pa_context_set_source_port_by_index(pa_context *c, uint32_t idx, const char*port, pa_context_success_cb_t cb, void *userdata);
@@ -557,12 +563,6 @@ pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx,
/** Move the specified source output to a different source. \since 0.9.5 */
pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
-/** Suspend/Resume a source. \since 0.9.7 */
-pa_operation* pa_context_suspend_source_by_name(pa_context *c, const char *source_name, int suspend, pa_context_success_cb_t cb, void* userdata);
-
-/** Suspend/Resume a source. If idx is PA_INVALID_INDEX all sources will be suspended. \since 0.9.7 */
-pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, int suspend, pa_context_success_cb_t cb, void* userdata);
-
/** Kill a source output. */
pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index c418d108..93a4742d 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -765,23 +765,22 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) {
static int calc_next_timeout(pa_mainloop *m) {
pa_time_event *t;
- pa_usec_t usec;
+ pa_usec_t clock_now;
if (!m->n_enabled_time_events)
return -1;
- t = find_next_time_event(m);
- pa_assert(t);
+ pa_assert_se(t = find_next_time_event(m));
- if (t->time == 0)
+ if (t->time <= 0)
return 0;
- usec = t->time - pa_rtclock_now();
+ clock_now = pa_rtclock_now();
- if (usec <= 0)
+ if (t->time <= clock_now)
return 0;
- return (int) (usec / 1000); /* in milliseconds */
+ return (int) ((t->time - clock_now) / 1000); /* in milliseconds */
}
static int dispatch_timeout(pa_mainloop *m) {
diff --git a/src/pulse/mainloop.h b/src/pulse/mainloop.h
index 4a83ebe8..63abd588 100644
--- a/src/pulse/mainloop.h
+++ b/src/pulse/mainloop.h
@@ -108,7 +108,9 @@ int pa_mainloop_iterate(pa_mainloop *m, int block, int *retval);
/** Run unlimited iterations of the main loop object until the main loop's quit() routine is called. */
int pa_mainloop_run(pa_mainloop *m, int *retval);
-/** Return the abstract main loop abstraction layer vtable for this main loop. */
+/** Return the abstract main loop abstraction layer vtable for this
+ main loop. No need of freeing the API as it is owned by the loop
+ and it is destroyed when this dies */
pa_mainloop_api* pa_mainloop_get_api(pa_mainloop*m);
/** Shutdown the main loop */
diff --git a/src/pulse/operation.h b/src/pulse/operation.h
index 7b0dabdd..b6b5691d 100644
--- a/src/pulse/operation.h
+++ b/src/pulse/operation.h
@@ -40,7 +40,11 @@ pa_operation *pa_operation_ref(pa_operation *o);
/** Decrease the reference count by one */
void pa_operation_unref(pa_operation *o);
-/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
+/** Cancel the operation. Beware! This will not necessarily cancel the
+ * execution of the operation on the server side. However it will make
+ * sure that the callback associated with this operation will not be
+ * called anymore, effectively disabling the operation from the client
+ * side's view. */
void pa_operation_cancel(pa_operation *o);
/** Return the current status of the operation */
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index c904f533..048b241a 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -251,7 +251,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);
+ pa_assert(data || nbytes == 0);
if (!property_name_valid(key))
return -1;
@@ -264,7 +264,8 @@ int pa_proplist_set(pa_proplist *p, const char *key, const void *data, size_t nb
pa_xfree(prop->value);
prop->value = pa_xmalloc(nbytes+1);
- memcpy(prop->value, data, nbytes);
+ if (nbytes > 0)
+ memcpy(prop->value, data, nbytes);
((char*) prop->value)[nbytes] = 0;
prop->nbytes = nbytes;
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index bc4dbd8a..8bf9c482 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -97,6 +97,24 @@ PA_C_DECL_BEGIN
/** For streams that belong to a window on the screen: an XDG icon name for the window. e.g. "totem" */
#define PA_PROP_WINDOW_ICON_NAME "window.icon_name"
+/** For streams that belong to a window on the screen: absolute horizontal window position on the screen, integer formatted as text string. e.g. "865". \since 0.9.17 */
+#define PA_PROP_WINDOW_X "window.x"
+
+/** For streams that belong to a window on the screen: absolute vertical window position on the screen, integer formatted as text string. e.g. "343". \since 0.9.17 */
+#define PA_PROP_WINDOW_Y "window.y"
+
+/** For streams that belong to a window on the screen: window width on the screen, integer formatted as text string. e.g. "365". \since 0.9.17 */
+#define PA_PROP_WINDOW_WIDTH "window.width"
+
+/** For streams that belong to a window on the screen: window height on the screen, integer formatted as text string. e.g. "643". \since 0.9.17 */
+#define PA_PROP_WINDOW_HEIGHT "window.height"
+
+/** For streams that belong to a window on the screen: relative position of the window center on the screen, float formatted as text string, ranging from 0.0 (left side of the screen) to 1.0 (right side of the screen). e.g. "0.65". \since 0.9.17 */
+#define PA_PROP_WINDOW_HPOS "window.hpos"
+
+/** For streams that belong to a window on the screen: relative position of the window center on the screen, float formatted as text string, ranging from 0.0 (top of the screen) to 1.0 (bottom of the screen). e.g. "0.43". \since 0.9.17 */
+#define PA_PROP_WINDOW_VPOS "window.vpos"
+
/** For streams that belong to an X11 window on the screen: the X11 display string. e.g. ":0.0" */
#define PA_PROP_WINDOW_X11_DISPLAY "window.x11.display"
@@ -197,7 +215,7 @@ PA_C_DECL_BEGIN
/** For filter devices: master device id if applicable. */
#define PA_PROP_DEVICE_MASTER_DEVICE "device.master_device"
-/** For devices: buffer size in bytes, integer formatted as string.. */
+/** For devices: buffer size in bytes, integer formatted as string. */
#define PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE "device.buffering.buffer_size"
/** For devices: fragment size in bytes, integer formatted as string. */
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
index aa369e69..793ba9b1 100644
--- a/src/pulse/pulseaudio.h
+++ b/src/pulse/pulseaudio.h
@@ -44,15 +44,17 @@
#include <pulse/util.h>
#include <pulse/timeval.h>
#include <pulse/proplist.h>
+#include <pulse/rtclock.h>
/** \file
- * Include all libpulse header files at once. The following
- * files are included: \ref mainloop-api.h, \ref sample.h, \ref def.h,
- * \ref context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h,
- * \ref scache.h, \ref version.h, \ref error.h, \ref channelmap.h,
- * \ref operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref
- * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref proplist.h, \ref timeval.h and
- * \ref mainloop-signal.h at once */
+ * Include all libpulse header files at once. The following files are
+ * included: \ref mainloop-api.h, \ref sample.h, \ref def.h, \ref
+ * context.h, \ref stream.h, \ref introspect.h, \ref subscribe.h, \ref
+ * scache.h, \ref version.h, \ref error.h, \ref channelmap.h, \ref
+ * operation.h,\ref volume.h, \ref xmalloc.h, \ref utf8.h, \ref
+ * thread-mainloop.h, \ref mainloop.h, \ref util.h, \ref proplist.h,
+ * \ref timeval.h, \ref rtclock.h and \ref mainloop-signal.h at
+ * once */
/** \mainpage
*
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 0f19f8eb..9698d8a5 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -36,28 +36,27 @@
#include "sample.h"
-size_t pa_sample_size_of_format(pa_sample_format_t f) {
-
- static const size_t table[] = {
- [PA_SAMPLE_U8] = 1,
- [PA_SAMPLE_ULAW] = 1,
- [PA_SAMPLE_ALAW] = 1,
- [PA_SAMPLE_S16LE] = 2,
- [PA_SAMPLE_S16BE] = 2,
- [PA_SAMPLE_FLOAT32LE] = 4,
- [PA_SAMPLE_FLOAT32BE] = 4,
- [PA_SAMPLE_S32LE] = 4,
- [PA_SAMPLE_S32BE] = 4,
- [PA_SAMPLE_S24LE] = 3,
- [PA_SAMPLE_S24BE] = 3,
- [PA_SAMPLE_S24_32LE] = 4,
- [PA_SAMPLE_S24_32BE] = 4
- };
+static const size_t size_table[] = {
+ [PA_SAMPLE_U8] = 1,
+ [PA_SAMPLE_ULAW] = 1,
+ [PA_SAMPLE_ALAW] = 1,
+ [PA_SAMPLE_S16LE] = 2,
+ [PA_SAMPLE_S16BE] = 2,
+ [PA_SAMPLE_FLOAT32LE] = 4,
+ [PA_SAMPLE_FLOAT32BE] = 4,
+ [PA_SAMPLE_S32LE] = 4,
+ [PA_SAMPLE_S32BE] = 4,
+ [PA_SAMPLE_S24LE] = 3,
+ [PA_SAMPLE_S24BE] = 3,
+ [PA_SAMPLE_S24_32LE] = 4,
+ [PA_SAMPLE_S24_32BE] = 4
+};
+size_t pa_sample_size_of_format(pa_sample_format_t f) {
pa_assert(f >= 0);
pa_assert(f < PA_SAMPLE_MAX);
- return table[f];
+ return size_table[f];
}
size_t pa_sample_size(const pa_sample_spec *spec) {
@@ -65,35 +64,35 @@ size_t pa_sample_size(const pa_sample_spec *spec) {
pa_assert(spec);
pa_return_val_if_fail(pa_sample_spec_valid(spec), 0);
- return pa_sample_size_of_format(spec->format);
+ return size_table[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;
+ return size_table[spec->format] * 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);
+ return spec->rate * size_table[spec->format] * spec->channels;
}
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);
+ return (((pa_usec_t) (length / (size_table[spec->format] * spec->channels)) * 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);
+ return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) * (size_table[spec->format] * spec->channels);
}
pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec) {
@@ -109,12 +108,12 @@ pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec) {
int pa_sample_spec_valid(const pa_sample_spec *spec) {
pa_assert(spec);
- if (spec->rate <= 0 ||
+ if (PA_UNLIKELY (spec->rate <= 0 ||
spec->rate > PA_RATE_MAX ||
spec->channels <= 0 ||
spec->channels > PA_CHANNELS_MAX ||
spec->format >= PA_SAMPLE_MAX ||
- spec->format < 0)
+ spec->format < 0))
return 0;
return 1;
@@ -125,6 +124,10 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) {
pa_assert(b);
pa_return_val_if_fail(pa_sample_spec_valid(a), 0);
+
+ if (PA_UNLIKELY(a == b))
+ return 1;
+
pa_return_val_if_fail(pa_sample_spec_valid(b), 0);
return
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 53d7dea3..7a4a55a0 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -302,6 +302,13 @@ pa_sample_format_t pa_parse_sample_format(const char *format) PA_GCC_PURE;
/** Pretty print a sample type specification to a string */
char* pa_sample_spec_snprint(char *s, size_t l, const pa_sample_spec *spec);
+/** Maximum required string length for pa_bytes_snprint(). Please note
+ * that this value can change with any release without warning and
+ * without being considered API or ABI breakage. You should not use
+ * this definition anywhere where it might become part of an
+ * ABI. \since 0.9.16 */
+#define PA_BYTES_SNPRINT_MAX 11
+
/** Pretty print a byte size value. (i.e. "2.5 MiB") */
char* pa_bytes_snprint(char *s, size_t l, unsigned v);
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index 77f60d72..27da6887 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -187,7 +187,7 @@ pa_operation *pa_context_play_sample(pa_context *c, const char *name, const char
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
- if (volume == (pa_volume_t) -1 && c->version < 15)
+ if (volume == PA_VOLUME_INVALID && c->version < 15)
volume = PA_VOLUME_NORM;
pa_tagstruct_putu32(t, volume);
@@ -216,7 +216,6 @@ pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *na
PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY_RETURN_NULL(c, name && *name, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, !dev || *dev, PA_ERR_INVALID);
- PA_CHECK_VALIDITY_RETURN_NULL(c, p, PA_ERR_INVALID);
PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 13, PA_ERR_NOTSUPPORTED);
o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
@@ -228,12 +227,19 @@ pa_operation *pa_context_play_sample_with_proplist(pa_context *c, const char *na
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
pa_tagstruct_puts(t, dev);
- if (volume == (pa_volume_t) -1 && c->version < 15)
+ if (volume == PA_VOLUME_INVALID && c->version < 15)
volume = PA_VOLUME_NORM;
pa_tagstruct_putu32(t, volume);
pa_tagstruct_puts(t, name);
- pa_tagstruct_put_proplist(t, p);
+
+ if (p)
+ pa_tagstruct_put_proplist(t, p);
+ else {
+ p = pa_proplist_new();
+ pa_tagstruct_put_proplist(t, p);
+ pa_proplist_free(p);
+ }
pa_pstream_send_tagstruct(c->pstream, t);
pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, play_sample_with_proplist_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
diff --git a/src/pulse/scache.h b/src/pulse/scache.h
index cd579d2e..31cf7b01 100644
--- a/src/pulse/scache.h
+++ b/src/pulse/scache.h
@@ -101,7 +101,7 @@ pa_operation* pa_context_play_sample(
pa_context *c /**< Context */,
const char *name /**< Name of the sample to play */,
const char *dev /**< Sink to play this sample on */,
- pa_volume_t volume /**< Volume to play this sample with. Starting with 0.9.15 you may pass here (pa_volume_t) -1 which will leave the decision about the volume to the server side which is a good idea. */ ,
+ pa_volume_t volume /**< Volume to play this sample with. Starting with 0.9.15 you may pass here PA_VOLUME_INVALID which will leave the decision about the volume to the server side which is a good idea. */ ,
pa_context_success_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
@@ -113,7 +113,7 @@ pa_operation* pa_context_play_sample_with_proplist(
pa_context *c /**< Context */,
const char *name /**< Name of the sample to play */,
const char *dev /**< Sink to play this sample on */,
- pa_volume_t volume /**< Volume to play this sample with. Starting with 0.9.15 you may pass here (pa_volume_t) -1 which will leave the decision about the volume to the server side which is a good idea. */ ,
+ pa_volume_t volume /**< Volume to play this sample with. Starting with 0.9.15 you may pass here PA_VOLUME_INVALID which will leave the decision about the volume to the server side which is a good idea. */ ,
pa_proplist *proplist /**< Property list for this sound. The property list of the cached entry will be merged into this property list */,
pa_context_play_sample_cb_t cb /**< Call this function after successfully starting playback, or NULL */,
void *userdata /**< Userdata to pass to the callback */);
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
index f4481fc3..9ed7a653 100644
--- a/src/pulse/simple.c
+++ b/src/pulse/simple.c
@@ -70,8 +70,8 @@ struct pa_simple {
#define CHECK_DEAD_GOTO(p, rerror, label) \
do { \
- if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
- !(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
+ if (!(p)->context || !PA_CONTEXT_IS_GOOD(pa_context_get_state((p)->context)) || \
+ !(p)->stream || !PA_STREAM_IS_GOOD(pa_stream_get_state((p)->stream))) { \
if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \
((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \
if (rerror) \
@@ -157,12 +157,8 @@ pa_simple* pa_simple_new(
CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
- p = pa_xnew(pa_simple, 1);
- p->context = NULL;
- p->stream = NULL;
+ p = pa_xnew0(pa_simple, 1);
p->direction = dir;
- p->read_data = NULL;
- p->read_index = p->read_length = 0;
if (!(p->mainloop = pa_threaded_mainloop_new()))
goto fail;
@@ -182,12 +178,21 @@ pa_simple* pa_simple_new(
if (pa_threaded_mainloop_start(p->mainloop) < 0)
goto unlock_and_fail;
- /* Wait until the context is ready */
- pa_threaded_mainloop_wait(p->mainloop);
+ for (;;) {
+ pa_context_state_t state;
- if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
- error = pa_context_errno(p->context);
- goto unlock_and_fail;
+ state = pa_context_get_state(p->context);
+
+ if (state == PA_CONTEXT_READY)
+ break;
+
+ if (!PA_CONTEXT_IS_GOOD(state)) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
}
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
@@ -216,13 +221,21 @@ pa_simple* pa_simple_new(
goto unlock_and_fail;
}
- /* Wait until the stream is ready */
- pa_threaded_mainloop_wait(p->mainloop);
+ for (;;) {
+ pa_stream_state_t state;
- /* Wait until the stream is ready */
- if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
- error = pa_context_errno(p->context);
- goto unlock_and_fail;
+ state = pa_stream_get_state(p->stream);
+
+ if (state == PA_STREAM_READY)
+ break;
+
+ if (!PA_STREAM_IS_GOOD(state)) {
+ error = pa_context_errno(p->context);
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(p->mainloop);
}
pa_threaded_mainloop_unlock(p->mainloop);
@@ -248,8 +261,10 @@ void pa_simple_free(pa_simple *s) {
if (s->stream)
pa_stream_unref(s->stream);
- if (s->context)
+ if (s->context) {
+ pa_context_disconnect(s->context);
pa_context_unref(s->context);
+ }
if (s->mainloop)
pa_threaded_mainloop_free(s->mainloop);
@@ -261,7 +276,8 @@ int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
- CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@@ -300,7 +316,8 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
- CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
+ CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@@ -375,7 +392,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
@@ -411,7 +428,7 @@ int pa_simple_flush(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
- while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
+ while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 40556329..2bc2b1e4 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -144,12 +144,13 @@ pa_stream *pa_stream_new_with_proplist(
s->suspended = FALSE;
s->corked = FALSE;
+ s->write_memblock = NULL;
+ s->write_data = NULL;
+
pa_memchunk_reset(&s->peek_memchunk);
s->peek_data = NULL;
-
s->record_memblockq = NULL;
-
memset(&s->timing_info, 0, sizeof(s->timing_info));
s->timing_info_valid = FALSE;
@@ -221,6 +222,11 @@ static void stream_free(pa_stream *s) {
stream_unlink(s);
+ if (s->write_memblock) {
+ pa_memblock_release(s->write_memblock);
+ pa_memblock_unref(s->write_data);
+ }
+
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@@ -821,7 +827,7 @@ static void create_stream_complete(pa_stream *s) {
if (s->flags & PA_STREAM_AUTO_TIMING_UPDATE) {
s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
pa_assert(!s->auto_timing_update_event);
- s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+ s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
request_auto_timing_update(s, TRUE);
}
@@ -861,7 +867,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
- uint32_t requested_bytes;
+ uint32_t requested_bytes = 0;
pa_assert(pd);
pa_assert(s);
@@ -1166,7 +1172,7 @@ int pa_stream_connect_playback(
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
- pa_cvolume *volume,
+ const pa_cvolume *volume,
pa_stream *sync_stream) {
pa_assert(s);
@@ -1187,20 +1193,71 @@ int pa_stream_connect_record(
return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
}
+int pa_stream_begin_write(
+ pa_stream *s,
+ void **data,
+ size_t *nbytes) {
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
+
+ if (*nbytes != (size_t) -1) {
+ size_t m, fs;
+
+ m = pa_mempool_block_size_max(s->context->mempool);
+ fs = pa_frame_size(&s->sample_spec);
+
+ m = (m / fs) * fs;
+ if (*nbytes > m)
+ *nbytes = m;
+ }
+
+ if (!s->write_memblock) {
+ s->write_memblock = pa_memblock_new(s->context->mempool, *nbytes);
+ s->write_data = pa_memblock_acquire(s->write_memblock);
+ }
+
+ *data = s->write_data;
+ *nbytes = pa_memblock_get_length(s->write_memblock);
+
+ return 0;
+}
+
+int pa_stream_cancel_write(
+ pa_stream *s) {
+
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+ PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
+ PA_CHECK_VALIDITY(s->context, s->write_memblock, PA_ERR_BADSTATE);
+
+ pa_assert(s->write_data);
+
+ pa_memblock_release(s->write_memblock);
+ pa_memblock_unref(s->write_memblock);
+ s->write_memblock = NULL;
+ s->write_data = NULL;
+
+ return 0;
+}
+
int pa_stream_write(
pa_stream *s,
const void *data,
size_t length,
- void (*free_cb)(void *p),
+ pa_free_cb_t free_cb,
int64_t offset,
pa_seek_mode_t seek) {
- pa_memchunk chunk;
- pa_seek_mode_t t_seek;
- int64_t t_offset;
- size_t t_length;
- const void *t_data;
-
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_assert(data);
@@ -1210,46 +1267,71 @@ int pa_stream_write(
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context,
+ !s->write_memblock ||
+ ((data >= s->write_data) &&
+ ((const char*) data + length <= (const char*) s->write_data + pa_memblock_get_length(s->write_memblock))),
+ PA_ERR_INVALID);
+ PA_CHECK_VALIDITY(s->context, !free_cb || !s->write_memblock, PA_ERR_INVALID);
- if (length <= 0)
- return 0;
+ if (s->write_memblock) {
+ pa_memchunk chunk;
- t_seek = seek;
- t_offset = offset;
- t_length = length;
- t_data = data;
+ /* pa_stream_write_begin() was called before */
- while (t_length > 0) {
+ pa_memblock_release(s->write_memblock);
- chunk.index = 0;
+ chunk.memblock = s->write_memblock;
+ chunk.index = (const char *) data - (const char *) s->write_data;
+ chunk.length = length;
- if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
- chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
- chunk.length = t_length;
- } else {
- void *d;
+ s->write_memblock = NULL;
+ s->write_data = NULL;
- chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
- chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+ pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
+ pa_memblock_unref(chunk.memblock);
- d = pa_memblock_acquire(chunk.memblock);
- memcpy(d, t_data, chunk.length);
- pa_memblock_release(chunk.memblock);
- }
+ } else {
+ pa_seek_mode_t t_seek = seek;
+ int64_t t_offset = offset;
+ size_t t_length = length;
+ const void *t_data = data;
- pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+ /* pa_stream_write_begin() was not called before */
- t_offset = 0;
- t_seek = PA_SEEK_RELATIVE;
+ while (t_length > 0) {
+ pa_memchunk chunk;
- t_data = (const uint8_t*) t_data + chunk.length;
- t_length -= chunk.length;
+ chunk.index = 0;
- pa_memblock_unref(chunk.memblock);
- }
+ if (free_cb && !pa_pstream_get_shm(s->context->pstream)) {
+ chunk.memblock = pa_memblock_new_user(s->context->mempool, (void*) t_data, t_length, free_cb, 1);
+ chunk.length = t_length;
+ } else {
+ void *d;
+
+ chunk.length = PA_MIN(t_length, pa_mempool_block_size_max(s->context->mempool));
+ chunk.memblock = pa_memblock_new(s->context->mempool, chunk.length);
+
+ d = pa_memblock_acquire(chunk.memblock);
+ memcpy(d, t_data, chunk.length);
+ pa_memblock_release(chunk.memblock);
+ }
- if (free_cb && pa_pstream_get_shm(s->context->pstream))
- free_cb((void*) data);
+ pa_pstream_send_memblock(s->context->pstream, s->channel, t_offset, t_seek, &chunk);
+
+ t_offset = 0;
+ t_seek = PA_SEEK_RELATIVE;
+
+ t_data = (const uint8_t*) t_data + chunk.length;
+ t_length -= chunk.length;
+
+ pa_memblock_unref(chunk.memblock);
+ }
+
+ if (free_cb && pa_pstream_get_shm(s->context->pstream))
+ free_cb((void*) data);
+ }
/* This is obviously wrong since we ignore the seeking index . But
* that's OK, the server side applies the same error */
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 49c132a2..21dd0a85 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -319,7 +319,7 @@ typedef struct pa_stream pa_stream;
typedef void (*pa_stream_success_cb_t) (pa_stream*s, int success, void *userdata);
/** A generic request callback */
-typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdata);
+typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t nbytes, void *userdata);
/** A generic notification callback */
typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
@@ -405,7 +405,7 @@ int pa_stream_connect_playback(
const char *dev /**< Name of the sink to connect to, or NULL for default */ ,
const pa_buffer_attr *attr /**< Buffering attributes, or NULL for default */,
pa_stream_flags_t flags /**< Additional flags, or 0 for default */,
- pa_cvolume *volume /**< Initial volume, or NULL for default */,
+ const pa_cvolume *volume /**< Initial volume, or NULL for default */,
pa_stream *sync_stream /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/);
/** Connect the stream to a source */
@@ -418,15 +418,71 @@ int pa_stream_connect_record(
/** Disconnect a stream from a source/sink */
int pa_stream_disconnect(pa_stream *s);
-/** Write some data to the server (for playback sinks), if free_cb is
- * non-NULL this routine is called when all data has been written out
- * and an internal reference to the specified data is kept, the data
- * is not copied. If NULL, the data is copied into an internal
- * buffer. The client my freely seek around in the output buffer. For
+/** Prepare writing data to the server (for playback streams). This
+ * function may be used to optimize the number of memory copies when
+ * doing playback ("zero-copy"). It is recommended to call this
+ * function before each call to pa_stream_write(). Pass in the address
+ * to a pointer and an address of the number of bytes you want to
+ * write. On return the two values will contain a pointer where you
+ * can place the data to write and the maximum number of bytes you can
+ * write. On return *nbytes can be smaller or have the same value as
+ * you passed in. You need to be able to handle both cases. Accessing
+ * memory beyond the returned *nbytes value is invalid. Acessing the
+ * memory returned after the following pa_stream_write() or
+ * pa_stream_cancel_write() is invalid. On invocation only *nbytes
+ * needs to be initialized, on return both *data and *nbytes will be
+ * valid. If you place (size_t) -1 in *nbytes on invocation the memory
+ * size will be chosen automatically (which is recommended to
+ * do). After placing your data in the memory area returned call
+ * pa_stream_write() with data set to an address within this memory
+ * area and an nbytes value that is smaller or equal to what was
+ * returned by this function to actually execute the write. An
+ * invocation of pa_stream_write() should follow "quickly" on
+ * pa_stream_begin_write(). It is not recommended letting an unbounded
+ * amount of time pass after calling pa_stream_begin_write() and
+ * before calling pa_stream_write(). If you want to cancel a
+ * previously called pa_stream_begin_write() without calling
+ * pa_stream_write() use pa_stream_cancel_write(). Calling
+ * pa_stream_begin_write() twice without calling pa_stream_write() or
+ * pa_stream_cancel_write() in between will return exactly the same
+ * pointer/nbytes values.\since 0.9.16 */
+int pa_stream_begin_write(
+ pa_stream *p,
+ void **data,
+ size_t *nbytes);
+
+/** Reverses the effect of pa_stream_begin_write() dropping all data
+ * that has already been placed in the memory area returned by
+ * pa_stream_begin_write(). Only valid to call if
+ * pa_stream_begin_write() was called before and neither
+ * pa_stream_cancel_write() nor pa_stream_write() have been called
+ * yet. Accessing the memory previously returned by
+ * pa_stream_begin_write() after this call is invalid. Any further
+ * explicit freeing of the memory area is not necessary. \since
+ * 0.9.16 */
+int pa_stream_cancel_write(
+ pa_stream *p);
+
+/** Write some data to the server (for playback streams), if free_cb
+ * is non-NULL this routine is called when all data has been written
+ * out and an internal reference to the specified data is kept, the
+ * data is not copied. If NULL, the data is copied into an internal
+ * buffer. The client may freely seek around in the output buffer. For
* most applications passing 0 and PA_SEEK_RELATIVE as arguments for
* offset and seek should be useful. Afte ther write call succeeded
* the write index will be a the position after where this chunk of
- * data has been written to. */
+ * data has been written to.
+ *
+ * As an optimization for avoiding needless memory copies you may call
+ * pa_stream_begin_write() before this call and then place your audio
+ * data directly in the memory area returned by that call. Then, pass
+ * a pointer to that memory area to pa_stream_write(). After the
+ * invocation of pa_stream_write() the memory area may no longer be
+ * accessed. Any further explicit freeing of the memory area is not
+ * necessary. It is OK to write the memory area returned by
+ * pa_stream_begin_write() only partially with this call, skipping
+ * bytes both at the end and at the beginning of the reserved memory
+ * area.*/
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
@@ -435,11 +491,12 @@ int pa_stream_write(
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
-/** Read the next fragment from the buffer (for recording).
- * data will point to the actual data and length will contain the size
- * of the data in bytes (which can be less than a complete framgnet).
- * Use pa_stream_drop() to actually remove the data from the
- * buffer. If no data is available will return a NULL pointer */
+/** Read the next fragment from the buffer (for recording streams).
+ * data will point to the actual data and nbytes will contain the size
+ * of the data in bytes (which can be less or more than a complete
+ * fragment). Use pa_stream_drop() to actually remove the data from
+ * the buffer. If no data is available this will return a NULL
+ * pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
@@ -455,7 +512,9 @@ size_t pa_stream_writable_size(pa_stream *p);
/** Return the number of bytes that may be read using pa_stream_peek()*/
size_t pa_stream_readable_size(pa_stream *p);
-/** Drain a playback stream. Use this for notification when the buffer is empty */
+/** Drain a playback stream. Use this for notification when the buffer
+ * is empty. Please note that only one drain operation per stream may
+ * be issued at a time. */
pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Request a timing info structure update for a stream. Use
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
index 6916d867..a2b98ce1 100644
--- a/src/pulse/thread-mainloop.c
+++ b/src/pulse/thread-mainloop.c
@@ -51,7 +51,7 @@
struct pa_threaded_mainloop {
pa_mainloop *real_mainloop;
- int n_waiting;
+ int n_waiting, n_waiting_for_accept;
pa_thread* thread;
pa_mutex* mutex;
@@ -190,8 +190,12 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
pa_cond_signal(m->cond, 1);
- if (wait_for_accept && m->n_waiting > 0)
- pa_cond_wait(m->accept_cond, m->mutex);
+ if (wait_for_accept) {
+ m->n_waiting_for_accept ++;
+
+ while (m->n_waiting_for_accept > 0)
+ pa_cond_wait(m->accept_cond, m->mutex);
+ }
}
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
@@ -214,6 +218,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
/* Make sure that this function is not called from the helper thread */
pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
+ pa_assert(m->n_waiting_for_accept > 0);
+ m->n_waiting_for_accept --;
+
pa_cond_signal(m->accept_cond, 0);
}
diff --git a/src/pulse/thread-mainloop.h b/src/pulse/thread-mainloop.h
index 8eddce4c..2cf496e1 100644
--- a/src/pulse/thread-mainloop.h
+++ b/src/pulse/thread-mainloop.h
@@ -137,15 +137,19 @@ PA_C_DECL_BEGIN
* The main function, my_drain_stream_func(), will wait for the callback to
* be called using pa_threaded_mainloop_wait().
*
- * If your application is multi-threaded, then this waiting must be done
- * inside a while loop. The reason for this is that multiple threads might be
- * using pa_threaded_mainloop_wait() at the same time. Each thread must
- * therefore verify that it was its callback that was invoked.
+ * If your application is multi-threaded, then this waiting must be
+ * done inside a while loop. The reason for this is that multiple
+ * threads might be using pa_threaded_mainloop_wait() at the same
+ * time. Each thread must therefore verify that it was its callback
+ * that was invoked. Also the underlying OS synchronization primitives
+ * are usually not free of spurious wake-ups, so a
+ * pa_threaded_mainloop_wait() must be called within a loop even if
+ * you have only one thread waiting.
*
* The callback, my_drain_callback(), indicates to the main function that it
* has been called using pa_threaded_mainloop_signal().
*
- * As you can see, both pa_threaded_mainloop_wait() may only be called with
+ * As you can see, pa_threaded_mainloop_wait() may only be called with
* the lock held. The same thing is true for pa_threaded_mainloop_signal(),
* but as the lock is held before the callback is invoked, you do not have to
* deal with that.
@@ -274,7 +278,9 @@ void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
* inside the event loop thread. Prior to this call the event loop
* object needs to be locked using pa_threaded_mainloop_lock(). While
* waiting the lock will be released, immediately before returning it
- * will be acquired again. */
+ * will be acquired again. This function may spuriously wake up even
+ * without _signal() being called. You need to make sure to handle
+ * that! */
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m);
/** Signal all threads waiting for a signalling event in
@@ -293,7 +299,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m);
/** Return the return value as specified with the main loop's quit() routine. */
int pa_threaded_mainloop_get_retval(pa_threaded_mainloop *m);
-/** Return the abstract main loop abstraction layer vtable for this main loop. */
+/** Return the abstract main loop abstraction layer vtable for this
+ main loop. No need of freeing the API as it is owned by the loop
+ and it is destroyed when this dies */
pa_mainloop_api* pa_threaded_mainloop_get_api(pa_threaded_mainloop*m);
/** Returns non-zero when called from withing the event loop thread. \since 0.9.7 */
diff --git a/src/pulse/utf8.c b/src/pulse/utf8.c
index 6b58bde3..fe7bcd26 100644
--- a/src/pulse/utf8.c
+++ b/src/pulse/utf8.c
@@ -120,10 +120,8 @@ static char* utf8_validate(const char *str, char *output) {
size = 4;
min = (1 << 16);
val = (uint32_t) (*p & 0x07);
- } else {
- size = 1;
+ } else
goto error;
- }
p++;
if (!is_continuation_char(*p))
@@ -150,12 +148,9 @@ ONE_REMAINING:
if (o) {
memcpy(o, last, (size_t) size);
- o += size - 1;
+ o += size;
}
- if (o)
- o++;
-
continue;
error:
diff --git a/src/pulse/util.c b/src/pulse/util.c
index 6f1e40a9..9440f5de 100644
--- a/src/pulse/util.c
+++ b/src/pulse/util.c
@@ -61,38 +61,40 @@
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
+#include <pulsecore/usergroup.h>
#include "util.h"
char *pa_get_user_name(char *s, size_t l) {
const char *p;
+ char *name = NULL;
+#ifdef OS_IS_WIN32
char buf[1024];
+#endif
#ifdef HAVE_PWD_H
- struct passwd pw, *r;
+ struct passwd *r;
#endif
pa_assert(s);
pa_assert(l > 0);
- if (!(p = (getuid() == 0 ? "root" : NULL)) &&
- !(p = getenv("USER")) &&
- !(p = getenv("LOGNAME")) &&
- !(p = getenv("USERNAME"))) {
+ if ((p = (getuid() == 0 ? "root" : NULL)) ||
+ (p = getenv("USER")) ||
+ (p = getenv("LOGNAME")) ||
+ (p = getenv("USERNAME")))
+ {
+ name = pa_strlcpy(s, p, l);
+ } else {
#ifdef HAVE_PWD_H
-#ifdef HAVE_GETPWUID_R
- if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
-#else
- /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
- * that do not support getpwuid_r. */
- if ((r = getpwuid(getuid())) == NULL) {
-#endif
+ if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
pa_snprintf(s, l, "%lu", (unsigned long) getuid());
return s;
}
- p = r->pw_name;
+ name = pa_strlcpy(s, r->pw_name, l);
+ pa_getpwuid_free(r);
#elif defined(OS_IS_WIN32) /* HAVE_PWD_H */
DWORD size = sizeof(buf);
@@ -102,7 +104,7 @@ char *pa_get_user_name(char *s, size_t l) {
return NULL;
}
- p = buf;
+ name = pa_strlcpy(s, buf, l);
#else /* HAVE_PWD_H */
@@ -110,7 +112,7 @@ char *pa_get_user_name(char *s, size_t l) {
#endif /* HAVE_PWD_H */
}
- return pa_strlcpy(s, p, l);
+ return name;
}
char *pa_get_host_name(char *s, size_t l) {
@@ -126,11 +128,10 @@ char *pa_get_host_name(char *s, size_t l) {
}
char *pa_get_home_dir(char *s, size_t l) {
- char *e;
+ char *e, *dir;
#ifdef HAVE_PWD_H
- char buf[1024];
- struct passwd pw, *r;
+ struct passwd *r;
#endif
pa_assert(s);
@@ -143,22 +144,19 @@ char *pa_get_home_dir(char *s, size_t l) {
return pa_strlcpy(s, e, l);
#ifdef HAVE_PWD_H
-
errno = 0;
-#ifdef HAVE_GETPWUID_R
- if (getpwuid_r(getuid(), &pw, buf, sizeof(buf), &r) != 0 || !r) {
-#else
- /* XXX Not thread-safe, but needed on OSes (e.g. FreeBSD 4.X)
- * that do not support getpwuid_r. */
- if ((r = getpwuid(getuid())) == NULL) {
-#endif
+ if ((r = pa_getpwuid_malloc(getuid())) == NULL) {
if (!errno)
errno = ENOENT;
return NULL;
}
- return pa_strlcpy(s, r->pw_dir, l);
+ dir = pa_strlcpy(s, r->pw_dir, l);
+
+ pa_getpwuid_free(r);
+
+ return dir;
#else /* HAVE_PWD_H */
errno = ENOENT;
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 42cde5b9..2d2bba25 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -40,6 +40,10 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) {
pa_assert(b);
pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+
+ if (PA_UNLIKELY(a == b))
+ return 1;
+
pa_return_val_if_fail(pa_cvolume_valid(b), 0);
if (a->channels != b->channels)
@@ -60,7 +64,7 @@ pa_cvolume* pa_cvolume_init(pa_cvolume *a) {
a->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
- a->values[c] = (pa_volume_t) -1;
+ a->values[c] = PA_VOLUME_INVALID;
return a;
}
@@ -122,7 +126,7 @@ pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, p
}
pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
- pa_volume_t m = 0;
+ pa_volume_t m = PA_VOLUME_MUTED;
unsigned c;
pa_assert(a);
@@ -135,9 +139,23 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) {
return m;
}
+pa_volume_t pa_cvolume_min(const pa_cvolume *a) {
+ pa_volume_t m = PA_VOLUME_MAX;
+ unsigned c;
+
+ pa_assert(a);
+ pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED);
+
+ for (c = 0; c < a->channels; c++)
+ if (a->values[c] < m)
+ m = a->values[c];
+
+ return m;
+}
+
pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
- pa_volume_t m = 0;
- unsigned c, n;
+ pa_volume_t m = PA_VOLUME_MUTED;
+ unsigned c;
pa_assert(a);
@@ -146,7 +164,7 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
- for (c = n = 0; c < a->channels; c++) {
+ for (c = 0; c < a->channels; c++) {
if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
continue;
@@ -158,17 +176,48 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p
return m;
}
+pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) {
+ pa_volume_t m = PA_VOLUME_MAX;
+ unsigned c;
+
+ pa_assert(a);
+
+ if (!cm)
+ return pa_cvolume_min(a);
+
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED);
+
+ for (c = 0; c < a->channels; c++) {
+
+ if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask))
+ continue;
+
+ if (a->values[c] < m)
+ m = a->values[c];
+ }
+
+ return m;
+}
+
pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) {
- return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) * pa_sw_volume_to_linear(b));
+
+ pa_return_val_if_fail(a != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+
+ /* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */
+
+ return (pa_volume_t) (((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM);
}
pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) {
- double v = pa_sw_volume_to_linear(b);
- if (v <= 0)
+ pa_return_val_if_fail(a != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, PA_VOLUME_INVALID);
+
+ if (b <= PA_VOLUME_MUTED)
return 0;
- return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) / v);
+ return (pa_volume_t) (((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b);
}
/* Amplitude, not power */
@@ -189,6 +238,8 @@ pa_volume_t pa_sw_volume_from_dB(double dB) {
double pa_sw_volume_to_dB(pa_volume_t v) {
+ pa_return_val_if_fail(v != PA_VOLUME_INVALID, PA_DECIBEL_MININFTY);
+
if (v <= PA_VOLUME_MUTED)
return PA_DECIBEL_MININFTY;
@@ -205,14 +256,19 @@ pa_volume_t pa_sw_volume_from_linear(double v) {
*
* http://www.robotplanet.dk/audio/audio_gui_design/
* http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151
+ *
+ * We make sure that the conversion to linear and back yields the
+ * same volume value! That's why we need the lround() below!
*/
- return (pa_volume_t) (cbrt(v) * PA_VOLUME_NORM);
+ return (pa_volume_t) lround(cbrt(v) * PA_VOLUME_NORM);
}
double pa_sw_volume_to_linear(pa_volume_t v) {
double f;
+ pa_return_val_if_fail(v != PA_VOLUME_INVALID, 0.0);
+
if (v <= PA_VOLUME_MUTED)
return 0.0;
@@ -246,7 +302,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) {
l -= pa_snprintf(e, l, "%s%u: %3u%%",
first ? "" : " ",
channel,
- (c->values[channel]*100)/PA_VOLUME_NORM);
+ (c->values[channel]*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM);
e = strchr(e, 0);
first = FALSE;
@@ -261,12 +317,12 @@ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) {
pa_init_i18n();
- if (v == (pa_volume_t) -1) {
+ if (v == PA_VOLUME_INVALID) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
- pa_snprintf(s, l, "%3u%%", (v*100)/PA_VOLUME_NORM);
+ pa_snprintf(s, l, "%3u%%", (v*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM);
return s;
}
@@ -311,7 +367,7 @@ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v) {
pa_init_i18n();
- if (v == (pa_volume_t) -1) {
+ if (v == PA_VOLUME_INVALID) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
@@ -328,6 +384,7 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) {
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), 0);
+ pa_return_val_if_fail(v != PA_VOLUME_INVALID, 0);
for (c = 0; c < a->channels; c++)
if (a->values[c] != v)
@@ -361,6 +418,7 @@ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a,
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b);
@@ -395,6 +453,7 @@ pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, p
pa_assert(a);
pa_return_val_if_fail(pa_cvolume_valid(a), NULL);
+ pa_return_val_if_fail(b != PA_VOLUME_INVALID, NULL);
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b);
@@ -413,7 +472,7 @@ int pa_cvolume_valid(const pa_cvolume *v) {
return 0;
for (c = 0; c < v->channels; c++)
- if (v->values[c] == (pa_volume_t) -1)
+ if (v->values[c] == PA_VOLUME_INVALID)
return 0;
return 1;
@@ -451,8 +510,6 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, const pa_channel_map *from, const pa
pa_assert(from);
pa_assert(to);
- 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);
@@ -554,8 +611,6 @@ float pa_cvolume_get_balance(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- 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))
@@ -587,12 +642,10 @@ pa_cvolume* pa_cvolume_set_balance(pa_cvolume *v, const pa_channel_map *map, flo
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);
+ pa_return_val_if_fail(new_balance >= -1.0f, NULL);
+ pa_return_val_if_fail(new_balance <= 1.0f, NULL);
if (!pa_channel_map_can_balance(map))
return v;
@@ -633,7 +686,7 @@ 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);
+ pa_return_val_if_fail(max != PA_VOLUME_INVALID, NULL);
t = pa_cvolume_max(v);
@@ -652,8 +705,12 @@ pa_cvolume* pa_cvolume_scale_mask(pa_cvolume *v, pa_volume_t max, pa_channel_map
pa_assert(v);
- pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
- pa_return_val_if_fail(max != (pa_volume_t) -1, NULL);
+ pa_return_val_if_fail(max != PA_VOLUME_INVALID, NULL);
+
+ if (!cm)
+ return pa_cvolume_scale(v, max);
+
+ pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(v, cm), NULL);
t = pa_cvolume_max_mask(v, cm, mask);
@@ -704,8 +761,6 @@ float pa_cvolume_get_fade(const pa_cvolume *v, const pa_channel_map *map) {
pa_assert(v);
pa_assert(map);
- 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))
@@ -728,12 +783,10 @@ pa_cvolume* pa_cvolume_set_fade(pa_cvolume *v, const pa_channel_map *map, float
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);
+ pa_return_val_if_fail(new_fade >= -1.0f, NULL);
+ pa_return_val_if_fail(new_fade <= 1.0f, NULL);
if (!pa_channel_map_can_fade(map))
return v;
@@ -781,6 +834,7 @@ pa_cvolume* pa_cvolume_set_position(
pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(cv, map), NULL);
pa_return_val_if_fail(t < PA_CHANNEL_POSITION_MAX, NULL);
+ pa_return_val_if_fail(v != PA_VOLUME_INVALID, NULL);
for (c = 0; c < map->channels; c++)
if (map->map[c] == t) {
@@ -812,3 +866,57 @@ pa_volume_t pa_cvolume_get_position(
return v;
}
+
+pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) {
+ unsigned i;
+
+ pa_assert(dest);
+ 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++)
+ dest->values[i] = PA_MAX(a->values[i], b->values[i]);
+
+ dest->channels = (uint8_t) i;
+
+ return dest;
+}
+
+pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) {
+ pa_volume_t m;
+
+ pa_assert(v);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(inc != PA_VOLUME_INVALID, NULL);
+
+ m = pa_cvolume_max(v);
+
+ if (m >= PA_VOLUME_MAX - inc)
+ m = PA_VOLUME_MAX;
+ else
+ m += inc;
+
+ return pa_cvolume_scale(v, m);
+}
+
+pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) {
+ pa_volume_t m;
+
+ pa_assert(v);
+
+ pa_return_val_if_fail(pa_cvolume_valid(v), NULL);
+ pa_return_val_if_fail(dec != PA_VOLUME_INVALID, NULL);
+
+ m = pa_cvolume_max(v);
+
+ if (m <= PA_VOLUME_MUTED + dec)
+ m = PA_VOLUME_MUTED;
+ else
+ m -= dec;
+
+ return pa_cvolume_scale(v, m);
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 05b7ebb4..c964020a 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -106,11 +106,14 @@ typedef uint32_t pa_volume_t;
/** Normal volume (100%, 0 dB) */
#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
-/** Muted volume (0%, -inf dB) */
+/** Muted (minimal valid) volume (0%, -inf dB) */
#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
-/** Maximum volume we can store. \since 0.9.15 */
-#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX)
+/** Maximum valid volume we can store. \since 0.9.15 */
+#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX-1)
+
+/** Special 'invalid' volume. \since 0.9.16 */
+#define PA_VOLUME_INVALID ((pa_volume_t) UINT32_MAX)
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {
@@ -195,6 +198,16 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) PA_GCC_PURE;
* \since 0.9.16 */
pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE;
+/** Return the minimum volume of all channels. \since 0.9.16 */
+pa_volume_t pa_cvolume_min(const pa_cvolume *a) PA_GCC_PURE;
+
+/** Return the minimum volume of all channels that are included in the
+ * specified channel map with the specified channel position mask. If
+ * cm is NULL this call is identical to pa_cvolume_min(). If no
+ * channel is selected the returned value will be PA_VOLUME_MUTED.
+ * \since 0.9.16 */
+pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE;
+
/** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */
int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE;
@@ -213,11 +226,13 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE
pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
/** Multiply two per-channel volumes and return the result in
- * *dest. This is only valid for software volumes! */
+ * *dest. This is only valid for software volumes! a, b and dest may
+ * point to the same structure. */
pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
/** Multiply a per-channel volume with a scalar volume and return the
- * result in *dest. This is only valid for software volumes! \since
+ * result in *dest. This is only valid for software volumes! a
+ * and dest may point to the same structure. \since
* 0.9.16 */
pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b);
@@ -228,11 +243,13 @@ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a,
pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST;
/** Divide two per-channel volumes and return the result in
- * *dest. This is only valid for software volumes! \since 0.9.13 */
+ * *dest. This is only valid for software volumes! a, b
+ * and dest may point to the same structure. \since 0.9.13 */
pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
/** Divide a per-channel volume by a scalar volume and return the
- * result in *dest. This is only valid for software volumes! \since
+ * result in *dest. This is only valid for software volumes! a
+ * and dest may point to the same structure. \since
* 0.9.16 */
pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b);
@@ -326,6 +343,19 @@ pa_cvolume* pa_cvolume_set_position(pa_cvolume *cv, const pa_channel_map *map, p
* position by calling pa_channel_map_has_position(). \since 0.9.16 */
pa_volume_t pa_cvolume_get_position(pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) PA_GCC_PURE;
+/** This goes through all channels in a and b and sets the
+ * corresponding channel in dest to the greater volume of both. a, b
+ * and dest may point to the same structure. \since 0.9.16 */
+pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b);
+
+/** Increase the volume passed in by 'inc'. The proportions between
+ * the channels are kept. \since 0.9.16 */
+pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc);
+
+/** Increase the volume passed in by 'inc'. The proportions between
+ * the channels are kept. \since 0.9.16 */
+pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec);
+
PA_C_DECL_END
#endif