From d7fd6a45e50475cddf0b8bad8baab01b33cf3c1f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 26 May 2008 22:00:19 +0000 Subject: move sources to src/ subdir git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@12 01b60673-d06a-42c0-afdd-89cb8e0f78ac --- canberra.c | 1 - canberra.h | 210 ------------ common.c | 380 --------------------- common.h | 50 --- driver.h | 38 --- llist.h | 108 ------ macro.h | 239 ------------- malloc.h | 41 --- mutex-posix.c | 80 ----- mutex.h | 37 -- proplist.c | 317 ----------------- proplist.h | 53 --- pulse.c | 903 ------------------------------------------------- read-sound-file.c | 175 ---------- read-sound-file.h | 48 --- read-vorbis.c | 148 -------- read-vorbis.h | 38 --- read-wav.c | 247 -------------- read-wav.h | 42 --- sound-theme-spec.c | 632 ---------------------------------- sound-theme-spec.h | 34 -- src/canberra.c | 1 + src/canberra.h | 210 ++++++++++++ src/common.c | 380 +++++++++++++++++++++ src/common.h | 50 +++ src/driver.h | 38 +++ src/llist.h | 108 ++++++ src/macro.h | 239 +++++++++++++ src/malloc.h | 41 +++ src/mutex-posix.c | 80 +++++ src/mutex.h | 37 ++ src/proplist.c | 317 +++++++++++++++++ src/proplist.h | 53 +++ src/pulse.c | 903 +++++++++++++++++++++++++++++++++++++++++++++++++ src/read-sound-file.c | 175 ++++++++++ src/read-sound-file.h | 48 +++ src/read-vorbis.c | 148 ++++++++ src/read-vorbis.h | 38 +++ src/read-wav.c | 247 ++++++++++++++ src/read-wav.h | 42 +++ src/sound-theme-spec.c | 632 ++++++++++++++++++++++++++++++++++ src/sound-theme-spec.h | 34 ++ src/test-gtk.c | 18 + src/test.c | 79 +++++ test-gtk.c | 18 - test.c | 79 ----- 46 files changed, 3918 insertions(+), 3918 deletions(-) delete mode 100644 canberra.c delete mode 100644 canberra.h delete mode 100644 common.c delete mode 100644 common.h delete mode 100644 driver.h delete mode 100644 llist.h delete mode 100644 macro.h delete mode 100644 malloc.h delete mode 100644 mutex-posix.c delete mode 100644 mutex.h delete mode 100644 proplist.c delete mode 100644 proplist.h delete mode 100644 pulse.c delete mode 100644 read-sound-file.c delete mode 100644 read-sound-file.h delete mode 100644 read-vorbis.c delete mode 100644 read-vorbis.h delete mode 100644 read-wav.c delete mode 100644 read-wav.h delete mode 100644 sound-theme-spec.c delete mode 100644 sound-theme-spec.h create mode 100644 src/canberra.c create mode 100644 src/canberra.h create mode 100644 src/common.c create mode 100644 src/common.h create mode 100644 src/driver.h create mode 100644 src/llist.h create mode 100644 src/macro.h create mode 100644 src/malloc.h create mode 100644 src/mutex-posix.c create mode 100644 src/mutex.h create mode 100644 src/proplist.c create mode 100644 src/proplist.h create mode 100644 src/pulse.c create mode 100644 src/read-sound-file.c create mode 100644 src/read-sound-file.h create mode 100644 src/read-vorbis.c create mode 100644 src/read-vorbis.h create mode 100644 src/read-wav.c create mode 100644 src/read-wav.h create mode 100644 src/sound-theme-spec.c create mode 100644 src/sound-theme-spec.h create mode 100644 src/test-gtk.c create mode 100644 src/test.c delete mode 100644 test-gtk.c delete mode 100644 test.c diff --git a/canberra.c b/canberra.c deleted file mode 100644 index 8b13789..0000000 --- a/canberra.c +++ /dev/null @@ -1 +0,0 @@ - diff --git a/canberra.h b/canberra.h deleted file mode 100644 index 2d48cd8..0000000 --- a/canberra.h +++ /dev/null @@ -1,210 +0,0 @@ -#ifndef foocanberrahfoo -#define foocanberrahfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include -#include -#include - -/* - - Requirements & General observations: - - - Property set extensible. To be kept in sync with PulseAudio and libsydney. - - Property keys need to be valid UTF-8, text values, too. - - Will warn if application.name or application.id not set. - - Will fail if event.id not set - - Fully thread safe, not async-signal safe - - Error codes are returned immediately, as negative integers - - If the control.cache property is set it will control whether the - specific sample will be cached in the server: - - * permanent: install the sample permanently in the server (for usage in gnome-session) - * volatile: install the sample temporarily in the server (will be expelled from cache on cache pressure or after timeout) - * never: never cache the sample in the server, always stream - - control.cache will default to "volatile" for ca_context_cache() and "never" for ca_context_play(). - control.cache is only a hint, the server may ignore this value - - application.process.* will be filled in automatically but may be overwritten by the client. - They thus should not be used for authentication purposes. - - The property list attached to the client object in the sound - server will be those specified via ca_context_prop_xx(). - - The property list attached to cached samples in the sound server - will be those specified via ca_context_prop_xx() at sample upload time, - combined with those specified directly at the _cache() function call - (the latter potentially overwriting the former). - - The property list attached to sample streams in the sound server - will be those attached to the cached sample (only if the event - sound is cached, of course) combined (i.e. potentially - overwritten by) those set via ca_context_prop_xx() at play time, - combined (i.e. potentially overwritten by) those specified - directly at the _play() function call. - - It is recommended to set application.* once before calling - _open(), and media.* event.* at both cache and play time. - -*/ - -#ifdef __GNUC__ -#define CA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) -#else -/** If we're in GNU C, use some magic for detecting invalid format strings */ -#define CA_GCC_PRINTF_ATTR(a,b) -#endif - -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define CA_GCC_SENTINEL __attribute__ ((sentinel)) -#else -/** Macro for usage of GCC's sentinel compilation warnings */ -#define CA_GCC_SENTINEL -#endif - -/** Properties for the media that is being played, about the event - * that caused the media to play, the window on which behalf this - * media is being played, the application that this window belongs to - * and finally properties for libcanberra specific usage. */ -#define CA_PROP_MEDIA_NAME "media.name" -#define CA_PROP_MEDIA_TITLE "media.title" -#define CA_PROP_MEDIA_ARTIST "media.artist" -#define CA_PROP_MEDIA_LANGUAGE "media.language" -#define CA_PROP_MEDIA_FILENAME "media.filename" -#define CA_PROP_MEDIA_ICON "media.icon" -#define CA_PROP_MEDIA_ICON_NAME "media.icon_name" -#define CA_PROP_MEDIA_ROLE "media.role" -#define CA_PROP_EVENT_ID "event.id" -#define CA_PROP_EVENT_MOUSE_X "event.mouse.x" -#define CA_PROP_EVENT_MOUSE_Y "event.mouse.y" -#define CA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button" -#define CA_PROP_WINDOW_NAME "window.name" -#define CA_PROP_WINDOW_ID "window.id" -#define CA_PROP_WINDOW_ICON "window.icon" -#define CA_PROP_WINDOW_ICON_NAME "window.icon_name" -#define CA_PROP_WINDOW_X11_DISPLAY "window.x11.display" -#define CA_PROP_WINDOW_X11_XID "window.x11.xid" -#define CA_PROP_APPLICATION_NAME "application.name" -#define CA_PROP_APPLICATION_ID "application.id" -#define CA_PROP_APPLICATION_VERSION "application.version" -#define CA_PROP_APPLICATION_ICON "application.icon" -#define CA_PROP_APPLICATION_ICON_NAME "application.icon_name" -#define CA_PROP_APPLICATION_LANGUAGE "application.language" -#define CA_PROP_APPLICATION_PROCESS_ID "application.process.id" -#define CA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary" -#define CA_PROP_APPLICATION_PROCESS_USER "application.process.user" -#define CA_PROP_APPLICATION_PROCESS_HOST "application.process.host" -#define CA_PROP_CANBERRA_CACHE_CONTROL "canberra.cache-control" /* permanent, volatile, never */ -#define CA_PROP_CANBERRA_VOLUME "canberra.volume" /* decibel */ -#define CA_PROP_CANBERRA_XDG_THEME_NAME "canberra.xdg-theme.name" /* XDG theme name */ -#define CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE "canberra.xdg-theme.output-profile" /* XDG theme profile */ - -/* Context object */ -typedef struct ca_context ca_context; - -/** Playback completion event callback. This callback will be called - * from a background thread. */ -typedef void ca_finish_callback_t(ca_context *c, uint32_t id, int error_code, void *userdata); - -/** Error codes */ -enum { - CA_SUCCESS = 0, - CA_ERROR_NOTSUPPORTED = -1, - CA_ERROR_INVALID = -2, - CA_ERROR_STATE = -3, - CA_ERROR_OOM = -4, - CA_ERROR_NODRIVER = -5, - CA_ERROR_SYSTEM = -6, - CA_ERROR_CORRUPT = -7, - CA_ERROR_TOOBIG = -8, - CA_ERROR_NOTFOUND = -9, - CA_ERROR_DESTROYED = -10, - CA_ERROR_CANCELED = -11, - CA_ERROR_NOTAVAILABLE = -12, - CA_ERROR_ACCESS = -13, - CA_ERROR_IO = -14, - _CA_ERROR_MAX = -15 -}; - -typedef struct ca_proplist ca_proplist; - -int ca_proplist_create(ca_proplist **c); -int ca_proplist_destroy(ca_proplist *c); -int ca_proplist_sets(ca_proplist *p, const char *key, const char *value); -int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) CA_GCC_PRINTF_ATTR(3,4); -int ca_proplist_set(ca_proplist *p, const char *key, const void *data, size_t nbytes); - -/** Create an (unconnected) context object */ -int ca_context_create(ca_context **c); - -int ca_context_set_driver(ca_context **c, const char *driver); - -int ca_context_change_device(ca_context **c, const char *device); - -/** Connect the context. This call is implicitly called if necessary. It - * is recommended to initialize the application.* properties before - * issuing this call */ -int ca_context_open(ca_context *c); - -/** Destroy a (connected or unconnected) cntext object. */ -int ca_context_destroy(ca_context *c); - -/** Write one or more string properties to the context - * object. Requires final NULL sentinel. Properties set like this will - * be attached to both the client object of the sound server and to - * all event sounds played or cached. */ -int ca_context_change_props(ca_context *c, ...) CA_GCC_SENTINEL; - -/** Write an arbitrary data property to the context object. */ -int ca_context_change_props_full(ca_context *c, ca_proplist *p); - -/** Play one event sound. id can be any numeric value which later can - * be used to cancel an event sound that is currently being - * played. You may use the same id twice or more times if you want to - * cancel multiple event sounds with a single ca_context_cancel() call - * at once. It is recommended to pass 0 for the id if the event sound - * shall never be canceled. If the requested sound is not cached in - * the server yet this call might result in the sample being uploaded - * temporarily or permanently. This function will only start playback - * in the background. It will not wait until playback completed. */ -int ca_context_play(ca_context *c, uint32_t id, ...) CA_GCC_SENTINEL; - -/** Play one event sound, and call the specified callback function - when completed. The callback will be called from a background - thread. Other arguments identical to ca_context_play(). */ -int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata); - -/** Upload the specified sample into the server and attach the - * specified properties to it. This function will only return after - * the sample upload was finished. */ -int ca_context_cache(ca_context *c, ...) CA_GCC_SENTINEL; - -/** Upload the specified sample into the server and attach the - * specified properties to it */ -int ca_context_cache_full(ca_context *c, ca_proplist *p); - -/** Cancel one or more event sounds that have been started via - * ca_context_play(). */ -int ca_context_cancel(ca_context *c, uint32_t id); - -/** Return a human readable error string */ -const char *ca_strerror(int code); - -#endif diff --git a/common.c b/common.c deleted file mode 100644 index bf11450..0000000 --- a/common.c +++ /dev/null @@ -1,380 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include - -#include "canberra.h" -#include "common.h" -#include "malloc.h" -#include "driver.h" -#include "proplist.h" - -int ca_context_create(ca_context **_c) { - ca_context *c; - int ret; - - ca_return_val_if_fail(_c, CA_ERROR_INVALID); - - if (!(c = ca_new0(ca_context, 1))) - return CA_ERROR_OOM; - - if (!(c->mutex = ca_mutex_new())) { - ca_free(c); - return CA_ERROR_OOM; - } - - if ((ret = ca_proplist_create(&c->props)) < 0) { - ca_mutex_free(c->mutex); - ca_free(c); - return ret; - } - - *_c = c; - return CA_SUCCESS; -} - -int ca_context_destroy(ca_context *c) { - int ret = CA_SUCCESS; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - /* There's no locking necessary here, because the application is - * broken anyway if it destructs this object in one thread and - * still is calling a method of it in another. */ - - if (c->opened) - ret = driver_destroy(c); - - if (c->props) - ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS); - - ca_mutex_free(c->mutex); - ca_free(c->driver); - ca_free(c->device); - ca_free(c); - - return ret; -} - -int ca_context_set_driver(ca_context *c, char *driver) { - char *n; - int ret; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_mutex_lock(c->mutex); - ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex); - - if (!(n = ca_strdup(driver))) { - ret = CA_ERROR_OOM; - goto fail; - } - - ca_free(c->driver); - c->driver = n; - - ret = CA_SUCCESS; - -fail: - ca_mutex_unlock(c->mutex); - - return ret; -} - -int ca_context_change_device(ca_context *c, char *device) { - char *n; - int ret; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_mutex_lock(c->mutex); - - if (!(n = ca_strdup(device))) { - ret = CA_ERROR_OOM; - goto fail; - } - - ret = c->opened ? driver_change_device(c, n) : CA_SUCCESS; - - if (ret == CA_SUCCESS) { - ca_free(c->device); - c->device = n; - } else - ca_free(n); - -fail: - ca_mutex_unlock(c->mutex); - - return ret; -} - -static int context_open_unlocked(ca_context *c) { - int ret; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - if (c->opened) - return CA_SUCCESS; - - if ((ret = driver_open(c)) == CA_SUCCESS) - c->opened = TRUE; - - return ret; -} - -int ca_context_open(ca_context *c) { - int ret; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_mutex_lock(c->mutex); - ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex); - - ret = context_open_unlocked(c); - - ca_mutex_unlock(c->mutex); - - return ret; -} - -static int ca_proplist_from_ap(ca_proplist **_p, va_list ap) { - int ret; - ca_proplist *p; - - ca_assert(_p); - - if ((ret = ca_proplist_create(&p)) < 0) - return ret; - - for (;;) { - const char *key, *value; - int ret; - - if (!(key = va_arg(ap, const char*))) - break; - - if (!(value = va_arg(ap, const char*))) { - ret = CA_ERROR_INVALID; - goto fail; - } - - if ((ret = ca_proplist_sets(p, key, value)) < 0) - goto fail; - } - - *_p = p; - - return CA_SUCCESS; - -fail: - ca_assert_se(ca_proplist_destroy(p) == CA_SUCCESS); - - return ret; -} - -int ca_context_change_props(ca_context *c, ...) { - va_list ap; - int ret; - ca_proplist *p = NULL; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - va_start(ap, c); - ret = ca_proplist_from_ap(&p, ap); - va_end(ap); - - if (ret < 0) - return ret; - - ret = ca_context_change_props_full(c, p); - - ca_assert_se(ca_proplist_destroy(p) == 0); - - return ret; -} - -int ca_context_change_props_full(ca_context *c, ca_proplist *p) { - int ret; - ca_proplist *merged; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(p, CA_ERROR_INVALID); - - ca_mutex_lock(c->mutex); - - if ((ret = ca_proplist_merge(&merged, c->props, p)) < 0) - goto finish; - - ret = c->opened ? driver_change_props(c, p, merged) : CA_SUCCESS; - - if (ret == CA_SUCCESS) { - ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS); - c->props = merged; - } else - ca_assert_se(ca_proplist_destroy(merged) == CA_SUCCESS); - -finish: - - ca_mutex_unlock(c->mutex); - - return ret; -} - -int ca_context_play(ca_context *c, uint32_t id, ...) { - int ret; - va_list ap; - ca_proplist *p = NULL; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - va_start(ap, id); - ret = ca_proplist_from_ap(&p, ap); - va_end(ap); - - if (ret < 0) - return ret; - - ret = ca_context_play_full(c, id, p, NULL, NULL); - - ca_assert_se(ca_proplist_destroy(p) == 0); - - return ret; -} - -int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata) { - int ret; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(p, CA_ERROR_INVALID); - ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID); - - ca_mutex_lock(c->mutex); - - ca_return_val_if_fail_unlock(ca_proplist_contains(p, CA_PROP_EVENT_ID) || - ca_proplist_contains(c->props, CA_PROP_EVENT_ID), CA_ERROR_INVALID, c->mutex); - - if ((ret = context_open_unlocked(c)) < 0) - goto finish; - - ca_assert(c->opened); - - ret = driver_play(c, id, p, cb, userdata); - -finish: - - ca_mutex_unlock(c->mutex); - - return ret; -} - -int ca_context_cancel(ca_context *c, uint32_t id) { - int ret; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_mutex_lock(c->mutex); - ca_return_val_if_fail_unlock(c->opened, CA_ERROR_STATE, c->mutex); - - ret = driver_cancel(c, id); - - ca_mutex_unlock(c->mutex); - - return ret; -} - -int ca_context_cache(ca_context *c, ...) { - int ret; - va_list ap; - ca_proplist *p = NULL; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - va_start(ap, c); - ret = ca_proplist_from_ap(&p, ap); - va_end(ap); - - if (ret < 0) - return ret; - - ret = ca_context_cache_full(c, p); - - ca_assert_se(ca_proplist_destroy(p) == 0); - - return ret; -} - -int ca_context_cache_full(ca_context *c, ca_proplist *p) { - int ret; - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(p, CA_ERROR_INVALID); - - ca_mutex_lock(c->mutex); - - ca_return_val_if_fail_unlock(ca_proplist_contains(p, CA_PROP_EVENT_ID) || - ca_proplist_contains(c->props, CA_PROP_EVENT_ID), CA_ERROR_INVALID, c->mutex); - - if ((ret = context_open_unlocked(c)) < 0) - goto finish; - - ca_assert(c->opened); - - ret = driver_cache(c, p); - -finish: - - ca_mutex_unlock(c->mutex); - - return ret; -} - -/** Return a human readable error */ -const char *ca_strerror(int code) { - - const char * const error_table[-_CA_ERROR_MAX] = { - [-CA_SUCCESS] = "Success", - [-CA_ERROR_NOT_SUPPORTED] = "Operation not supported", - [-CA_ERROR_INVALID] = "Invalid argument", - [-CA_ERROR_STATE] = "Invalid state", - [-CA_ERROR_OOM] = "Out of memory", - [-CA_ERROR_NO_DRIVER] = "No such driver", - [-CA_ERROR_SYSTEM] = "System error" - }; - - ca_return_val_if_fail(code <= 0, NULL); - ca_return_val_if_fail(code > _CA_ERROR_MAX, NULL); - - return error_table[-code]; -} - -/* Not exported */ -int ca_parse_cache_control(ca_cache_control_t *control, const char *c) { - ca_return_val_if_fail(control, CA_ERROR_INVALID); - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - if (streq(control, "never")) - *control = CA_CACHE_CONTROL_NEVER; - else if (streq(control, "permanent")) - *control = CA_CACHE_CONTROL_PERMANENT; - else if (streq(control, "volatile")) - *control = CA_CACHE_CONTROL_VOLATILE; - else - return CA_ERROR_INVALID; - - return CA_SUCCESS; -} diff --git a/common.h b/common.h deleted file mode 100644 index 13139b3..0000000 --- a/common.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef foocacommonh -#define foocacommonh - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "canberra.h" -#include "macro.h" -#include "mutex.h" - -struct ca_context { - ca_bool_t opened; - ca_mutex *mutex; - - ca_proplist *props; - - char *driver; - char *device; - - void *private; -}; - -typedef enum ca_cache_control { - CA_CACHE_CONTROL_NEVER, - CA_CACHE_CONTROL_PERMANENT, - CA_CACHE_CONTROL_VOLATILE -} ca_cache_control_t; - -int ca_parse_cache_control(ca_cache_control_t *control, const char *c); - -#endif diff --git a/driver.h b/driver.h deleted file mode 100644 index 732a5d4..0000000 --- a/driver.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef foocanberradriverhfoo -#define foocanberradriverhfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "canberra.h" - -int driver_open(ca_context *c); -int driver_destroy(ca_context *c); - -int driver_change_device(ca_context *c, char *device); -int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged); - -int driver_play(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata); -int driver_cancel(ca_context *c, uint32_t id); -int driver_cache(ca_context *c, ca_proplist *p); - -#endif diff --git a/llist.h b/llist.h deleted file mode 100644 index e9122ff..0000000 --- a/llist.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef foocallistfoo -#define foocallistfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "macro.h" - -/* Some macros for maintaining doubly linked lists */ - -/* The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define CA_LLIST_HEAD(t,name) \ - t *name - -/* The pointers in the linked list's items. Use this in the item structure */ -#define CA_LLIST_FIELDS(t) \ - t *next, *prev - -/* Initialize the list's head */ -#define CA_LLIST_HEAD_INIT(t,item) \ - do { \ - (item) = (t*) NULL; } \ - while(0) - -/* Initialize a list item */ -#define CA_LLIST_INIT(t,item) \ - do { \ - t *_item = (item); \ - ca_assert(_item); \ - _item->prev = _item->next = NULL; \ - } while(0) - -/* Prepend an item to the list */ -#define CA_LLIST_PREPEND(t,head,item) \ - do { \ - t **_head = &(head), *_item = (item); \ - ca_assert(_item); \ - if ((_item->next = *_head)) \ - _item->next->prev = _item; \ - _item->prev = NULL; \ - *_head = _item; \ - } while (0) - -/* Remove an item from the list */ -#define CA_LLIST_REMOVE(t,head,item) \ - do { \ - t **_head = &(head), *_item = (item); \ - ca_assert(_item); \ - if (_item->next) \ - _item->next->prev = _item->prev; \ - if (_item->prev) \ - _item->prev->next = _item->next; \ - else { \ - ca_assert(*_head == _item); \ - *_head = _item->next; \ - } \ - _item->next = _item->prev = NULL; \ - } while(0) - -/* Find the head of the list */ -#define CA_LLIST_FIND_HEAD(t,item,head) \ - do { \ - t **_head = (head), *_item = (item); \ - *_head = _item; \ - ca_assert(_head); \ - while ((*_head)->prev) \ - *_head = (*_head)->prev; \ - } while (0) - -/* Insert an item after another one (a = where, b = what) */ -#define CA_LLIST_INSERT_AFTER(t,head,a,b) \ - do { \ - t **_head = &(head), *_a = (a), *_b = (b); \ - ca_assert(_b); \ - if (!_a) { \ - if ((_b->next = *_head)) \ - _b->next->prev = _b; \ - _b->prev = NULL; \ - *_head = _b; \ - } else { \ - if ((_b->next = _a->next)) \ - _b->next->prev = _b; \ - _b->prev = _a; \ - _a->next = _b; \ - } \ - } while (0) - -#endif diff --git a/macro.h b/macro.h deleted file mode 100644 index 2f37bc1..0000000 --- a/macro.h +++ /dev/null @@ -1,239 +0,0 @@ -#ifndef foocanberramacrohfoo -#define foocanberramacrohfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include -#include - -#ifndef PACKAGE -#error "Please include config.h before including this file!" -#endif - -#ifdef __GNUC__ -#define CA_LIKELY(x) (__builtin_expect(!!(x),1)) -#define CA_UNLIKELY(x) (__builtin_expect((x),0)) -#else -#define CA_LIKELY(x) (x) -#define CA_UNLIKELY(x) (x) -#endif - -#ifdef __GNUC__ -#define CA_PRETTY_FUNCTION __PRETTY_FUNCTION__ -#else -#define CA_PRETTY_FUNCTION "" -#endif - -#define ca_return_if_fail(expr) \ - do { \ - if (CA_UNLIKELY(!(expr))) { \ - fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ - return; \ - } \ - } while(FALSE) - -#define ca_return_val_if_fail(expr, val) \ - do { \ - if (CA_UNLIKELY(!(expr))) { \ - fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ - return (val); \ - } \ - } while(FALSE) - -#define ca_return_null_if_fail(expr) ca_return_val_if_fail(expr, NULL) - -#define ca_return_if_fail_unlock(expr, mutex) \ - do { \ - if (CA_UNLIKELY(!(expr))) { \ - fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ - ca_mutex_unlock(mutex); \ - return; \ - } \ - } while(FALSE) - -#define ca_return_val_if_fail_unlock(expr, val, mutex) \ - do { \ - if (CA_UNLIKELY(!(expr))) { \ - fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ - ca_mutex_unlock(mutex); \ - return (val); \ - } \ - } while(FALSE) - -#define ca_return_null_if_fail_unlock(expr, mutex) ca_return_val_if_fail_unlock(expr, NULL, mutex) - -/* An assert which guarantees side effects of x, i.e. is never - * optimized away */ -#define ca_assert_se(expr) \ - do { \ - if (CA_UNLIKELY(!(expr))) { \ - fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ - abort(); \ - } \ - } while (FALSE) - -/* An assert that may be optimized away by defining NDEBUG */ -#ifdef NDEBUG -#define ca_assert(expr) do {} while (FALSE) -#else -#define ca_assert(expr) ca_assert_se(expr) -#endif - -#define ca_assert_not_reached() \ - do { \ - fprintf(stderr, "Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ - abort(); \ - } while (FALSE) - -#define CA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) - -#ifdef __GNUC__ -#define CA_MAX(a,b) \ - __extension__ ({ typeof(a) _a = (a); \ - typeof(b) _b = (b); \ - _a > _b ? _a : _b; \ - }) -#else -#define CA_MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif - -#ifdef __GNUC__ -#define CA_MIN(a,b) \ - __extension__ ({ typeof(a) _a = (a); \ - typeof(b) _b = (b); \ - _a < _b ? _a : _b; \ - }) -#else -#define CA_MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifdef __GNUC__ -#define CA_CLAMP(x, low, high) \ - __extension__ ({ typeof(x) _x = (x); \ - typeof(low) _low = (low); \ - typeof(high) _high = (high); \ - ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ - }) -#else -#define CA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) -#endif - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -#define CA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p)) -#define CA_UINT_TO_PTR(u) ((void*) (unsigned long) (u)) - -#define CA_PTR_TO_UINT32(p) ((uint32_t) CA_PTR_TO_UINT(p)) -#define CA_UINT32_TO_PTR(u) CA_UINT_TO_PTR((uint32_t) u) - -#define CA_PTR_TO_INT(p) ((int) CA_PTR_TO_UINT(p)) -#define CA_INT_TO_PTR(u) CA_UINT_TO_PTR((int) u) - -#define CA_PTR_TO_INT32(p) ((int32_t) CA_PTR_TO_UINT(p)) -#define CA_INT32_TO_PTR(u) CA_UINT_TO_PTR((int32_t) u) - - - -static inline size_t ca_align(size_t l) { - return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*)); -} - -#define CA_ALIGN(x) (ca_align(x)) - -typedef void (*ca_free_cb_t)(void *); - -typedef int ca_bool_t; - -#ifdef HAVE_BYTESWAP_H -#include -#endif - -#ifdef HAVE_BYTESWAP_H -#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x)) -#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x)) -#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x)) -#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x)) -#else -#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) -#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) -#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) -#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) -#endif - -#ifdef WORDS_BIGENDIAN - #define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x) - #define PA_INT16_FROM_BE(x) ((int16_t)(x)) - - #define PA_INT16_TO_LE(x) PA_INT16_SWAP(x) - #define PA_INT16_TO_BE(x) ((int16_t)(x)) - - #define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x) - #define PA_UINT16_FROM_BE(x) ((uint16_t)(x)) - - #define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x) - #define PA_UINT16_TO_BE(x) ((uint16_t)(x)) - - #define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x) - #define PA_INT32_FROM_BE(x) ((int32_t)(x)) - - #define PA_INT32_TO_LE(x) PA_INT32_SWAP(x) - #define PA_INT32_TO_BE(x) ((int32_t)(x)) - - #define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x) - #define PA_UINT32_FROM_BE(x) ((uint32_t)(x)) - - #define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x) - #define PA_UINT32_TO_BE(x) ((uint32_t)(x)) -#else - #define PA_INT16_FROM_LE(x) ((int16_t)(x)) - #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x) - - #define PA_INT16_TO_LE(x) ((int16_t)(x)) - #define PA_INT16_TO_BE(x) PA_INT16_SWAP(x) - - #define PA_UINT16_FROM_LE(x) ((uint16_t)(x)) - #define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x) - - #define PA_UINT16_TO_LE(x) ((uint16_t)(x)) - #define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x) - - #define PA_INT32_FROM_LE(x) ((int32_t)(x)) - #define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x) - - #define PA_INT32_TO_LE(x) ((int32_t)(x)) - #define PA_INT32_TO_BE(x) PA_INT32_SWAP(x) - - #define PA_UINT32_FROM_LE(x) ((uint32_t)(x)) - #define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x) - - #define PA_UINT32_TO_LE(x) ((uint32_t)(x)) - #define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x) -#endif - -#endif diff --git a/malloc.h b/malloc.h deleted file mode 100644 index b720a12..0000000 --- a/malloc.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef foosydneymallochfoo -#define foocanberramallochfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include -#include - -#define ca_malloc malloc -#define ca_free free -#define ca_malloc0(size) calloc(1, (size)) -#define ca_strdup strdup -#define ca_strndup strndup - -void* ca_memdup(const void* p, size_t size); - -#define ca_new(t, n) ((t*) ca_malloc(sizeof(t)*(n))) -#define ca_new0(t, n) ((t*) ca_malloc0(sizeof(t)*(n))) -#define ca_newdup(t, p, n) ((t*) ca_memdup(p, sizeof(t)*(n))) - -#endif diff --git a/mutex-posix.c b/mutex-posix.c deleted file mode 100644 index f8e0f54..0000000 --- a/mutex-posix.c +++ /dev/null @@ -1,80 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include - -#include "mutex.h" -#include "malloc.h" - -struct ca_mutex { - pthread_mutex_t mutex; -}; - -ca_mutex* ca_mutex_new(void) { - ca_mutex *m; - - if (!(m = ca_new(ca_mutex, 1))) - return NULL; - - if (pthread_mutex_init(&m->mutex, NULL) < 0) { - ca_free(m); - return NULL; - } - - return m; -} - -void ca_mutex_free(ca_mutex *m) { - ca_assert(m); - - ca_assert_se(pthread_mutex_destroy(&m->mutex) == 0); - ca_free(m); -} - -void ca_mutex_lock(ca_mutex *m) { - ca_assert(m); - - ca_assert_se(pthread_mutex_lock(&m->mutex) == 0); -} - -ca_bool_t ca_mutex_try_lock(ca_mutex *m) { - int r; - ca_assert(m); - - if ((r = pthread_mutex_trylock(&m->mutex)) != 0) { - ca_assert(r == EBUSY); - return FALSE; - } - - return TRUE; -} - -void ca_mutex_unlock(ca_mutex *m) { - ca_assert(m); - - ca_assert_se(pthread_mutex_unlock(&m->mutex) == 0); -} diff --git a/mutex.h b/mutex.h deleted file mode 100644 index 31b75b3..0000000 --- a/mutex.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef foocamutexhfoo -#define foocamutexhfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "macro.h" - -typedef struct ca_mutex ca_mutex; - -ca_mutex* ca_mutex_new(void); -void ca_mutex_free(ca_mutex *m); - -void ca_mutex_lock(ca_mutex *m); -ca_bool_t ca_mutex_try_lock(ca_mutex *m); -void ca_mutex_unlock(ca_mutex *m); - -#endif diff --git a/proplist.c b/proplist.c deleted file mode 100644 index 6c10595..0000000 --- a/proplist.c +++ /dev/null @@ -1,317 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include - -#include "canberra.h" -#include "proplist.h" -#include "macro.h" -#include "malloc.h" - -#ifdef HAVE_CONFIG_H -#include -#endif - -static unsigned calc_hash(const char *c) { - unsigned hash = 0; - - for (; *c; c++) - hash = 31 * hash + *c; - - return hash; -} - -int ca_proplist_create(ca_proplist **_p) { - ca_proplist *p; - ca_return_val_if_fail(_p, CA_ERROR_INVALID); - - if (!(p = ca_new0(ca_proplist, 1))) - return CA_ERROR_OOM; - - if (!(p->mutex = ca_mutex_new())) { - ca_free(p); - return CA_ERROR_OOM; - } - - *_p = p; - - return CA_SUCCESS; -} - -static int _unset(ca_proplist *p, const char *key) { - ca_prop *prop, *nprop; - unsigned i; - - ca_return_val_if_fail(p, CA_ERROR_INVALID); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - - i = calc_hash(key) % N_HASHTABLE; - - nprop = NULL; - for (prop = p->prop_hashtable[i]; prop; nprop = prop, prop = prop->next_in_slot) - if (strcmp(prop->key, key) == 0) - break; - - if (prop) { - if (nprop) - nprop->next_in_slot = prop->next_in_slot; - else - p->prop_hashtable[i] = prop->next_in_slot; - - if (prop->prev_item) - prop->prev_item->next_item = prop->next_item; - else - p->first_item = prop->next_item; - - if (prop->next_item) - prop->next_item->prev_item = prop->prev_item; - - ca_free(prop); - } - - return CA_SUCCESS; -} - -int ca_proplist_sets(ca_proplist *p, const char *key, const char *value) { - ca_return_val_if_fail(p, CA_ERROR_INVALID); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - ca_return_val_if_fail(!value, CA_ERROR_INVALID); - - return ca_proplist_set(p, key, value, sizeof(value)+1); -} - -int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) { - int ret; - char *k; - ca_prop *prop; - int size = 100; - unsigned h; - - ca_return_val_if_fail(p, CA_ERROR_INVALID); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - ca_return_val_if_fail(format, CA_ERROR_INVALID); - - if (!(k = ca_strdup(key))) - return CA_ERROR_OOM; - - for (;;) { - va_list ap; - int r; - - if (!(prop = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + size))) { - ca_free(k); - return CA_ERROR_OOM; - } - - - va_start(ap, format); - r = vsnprintf(CA_PROP_DATA(prop), size, format, ap); - va_end(ap); - - ((char*) CA_PROP_DATA(prop))[size-1] = 0; - - if (r > -1 && r < size) { - prop->nbytes = r+1; - break; - } - - if (r > -1) /* glibc 2.1 */ - size = r+1; - else /* glibc 2.0 */ - size *= 2; - - ca_free(prop); - } - - prop->key = k; - - ca_mutex_lock(p->mutex); - - if ((ret = _unset(p, key)) < 0) { - ca_free(prop); - ca_free(k); - goto finish; - } - - h = calc_hash(key) % N_HASHTABLE; - - prop->next_in_slot = p->prop_hashtable[h]; - p->prop_hashtable[h] = prop; - - prop->prev_item = NULL; - prop->next_item = p->first_item; - p->first_item = prop; - -finish: - - ca_mutex_unlock(p->mutex); - - return ret; -} - -int ca_proplist_set(ca_proplist *p, const char *key, const void *data, size_t nbytes) { - int ret; - char *k; - ca_prop *prop; - unsigned h; - - ca_return_val_if_fail(p, CA_ERROR_INVALID); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - ca_return_val_if_fail(!nbytes || data, CA_ERROR_INVALID); - - if (!(k = ca_strdup(key))) - return CA_ERROR_OOM; - - if (!(prop = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + nbytes))) { - ca_free(k); - return CA_ERROR_OOM; - } - - prop->key = k; - prop->nbytes = nbytes; - memcpy(CA_PROP_DATA(prop), data, nbytes); - - ca_mutex_lock(p->mutex); - - if ((ret = _unset(p, key)) < 0) { - ca_free(prop); - ca_free(k); - goto finish; - } - - h = calc_hash(key) % N_HASHTABLE; - - prop->next_in_slot = p->prop_hashtable[h]; - p->prop_hashtable[h] = prop; - - prop->prev_item = NULL; - prop->next_item = p->first_item; - p->first_item = prop; - -finish: - - ca_mutex_unlock(p->mutex); - - return ret; -} - -/* Not exported, not self-locking */ -ca_prop* ca_proplist_get_unlocked(ca_proplist *p, const char *key) { - ca_prop *prop; - unsigned i; - - ca_return_val_if_fail(p, NULL); - ca_return_val_if_fail(key, NULL); - - i = calc_hash(key) % N_HASHTABLE; - - for (prop = p->prop_hashtable[i]; prop; prop = prop->next_in_slot) - if (strcmp(prop->key, key) == 0) - return prop; - - return NULL; -} - -/* Not exported, not self-locking */ -const char* ca_proplist_gets_unlocked(ca_proplist *p, const char *key) { - ca_prop *prop; - - ca_return_val_if_fail(p, NULL); - ca_return_val_if_fail(key, NULL); - - if (!(prop = ca_proplist_get_unlocked(p, key))) - return NULL; - - if (!memchr(CA_PROP_DATA(prop), 0, prop->nbytes)) - return NULL; - - return CA_PROP_DATA(prop); -} - -int ca_proplist_destroy(ca_proplist *p) { - ca_prop *prop, *nprop; - - ca_return_val_if_fail(p, CA_ERROR_INVALID); - - for (prop = p->first_item; prop; prop = nprop) { - nprop = prop->next_item; - ca_free(prop); - } - - ca_mutex_free(p->mutex); - - ca_free(p); - - return CA_SUCCESS; -} - -static int merge_into(ca_proplist *a, ca_proplist *b) { - int ret = CA_SUCCESS; - ca_prop *prop; - - ca_return_val_if_fail(a, CA_ERROR_INVALID); - ca_return_val_if_fail(b, CA_ERROR_INVALID); - - ca_mutex_lock(b->mutex); - - for (prop = b->first_item; prop; prop = prop->next_item) - if ((ret = ca_proplist_set(a, prop->key, CA_PROP_DATA(prop), prop->nbytes)) < 0) - break; - - ca_mutex_unlock(b->mutex); - - return ret; -} - -int ca_proplist_merge(ca_proplist **_a, ca_proplist *b, ca_proplist *c) { - ca_proplist *a; - int ret; - - ca_return_val_if_fail(_a, CA_ERROR_INVALID); - ca_return_val_if_fail(b, CA_ERROR_INVALID); - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - if ((ret = ca_proplist_create(&a)) < 0) - return ret; - - if ((ret = merge_into(a, b)) < 0 || - (ret = merge_into(a, c)) < 0) { - ca_proplist_destroy(a); - return ret; - } - - *_a = a; - return CA_SUCCESS; -} - -ca_bool_t ca_proplist_contains(ca_proplist *p, const char *key) { - ca_bool_t b; - - ca_return_val_if_fail(p, CA_ERROR_INVALID); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - - ca_mutex_lock(p->mutex); - b = !!ca_proplist_get_unlocked(p, key); - ca_mutex_unlock(p->mutex); - - return b; -} diff --git a/proplist.h b/proplist.h deleted file mode 100644 index 4cb511e..0000000 --- a/proplist.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef foocaproplisthfoo -#define foocaproplisthfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "canberra.h" -#include "mutex.h" - -#define N_HASHTABLE 31 - -typedef struct ca_prop { - char *key; - size_t nbytes; - struct ca_prop *next_in_slot, *next_item, *prev_item; -} ca_prop; - -#define CA_PROP_DATA(p) ((void*) ((char*) (p) + CA_ALIGN(sizeof(ca_prop)))) - -struct ca_proplist { - ca_mutex *mutex; - - ca_prop *prop_hashtable[N_HASHTABLE]; - ca_prop *first_item; -}; - -int ca_proplist_merge(ca_proplist **_a, ca_proplist *b, ca_proplist *c); -ca_bool_t ca_proplist_contains(ca_proplist *p, const char *key); - -/* Both of the following two functions are not locked! Need manual locking! */ -ca_prop* ca_context_get_unlocked(ca_proplist *c, const char *key); -const char* ca_context_gets_unlocked(ca_proplist *c, const char *key); - -#endif diff --git a/pulse.c b/pulse.c deleted file mode 100644 index c69668a..0000000 --- a/pulse.c +++ /dev/null @@ -1,903 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -/* The locking order needs to be strictly followed! First take the - * mainloop mutex, only then take outstanding_mutex if you need both! - * Not the other way round, beacause we might then enter a - * deadlock! */ - -#include -#include -#include - -#include "canberra.h" -#include "common." -#include "driver.h" - -enum outstanding_type { - OUTSTANDING_SAMPLE, - OUTSTANDING_STREAM, - OUTSTANDING_UPLOAD -}; - -struct outstanding { - PA_LLIST_FIELDS(struct outstanding); - enum outstanding_type type; - ca_context *context; - uint32_t id; - uint32_t sink_input; - ca_stream *stream; - ca_finish_callback_t callback; - void *userdata; - ca_sound_file *file; - int error; - ca_bool_t clean_up; -}; - -struct private { - pa_threaded_mainloop *mainloop; - pa_context *context; - ca_theme_data *theme; - ca_bool_t subscribed; - - ca_mutex *outstanding_mutex; - PA_LLIST_HEAD(struct outstanding, outstanding); -}; - -#define PRIVATE(c) ((struct private *) ((c)->private) - -static void outstanding_free(struct outstanding *o) { - ca_assert(o); - - if (o->file) - ca_sound_file_free(o->file); - - if (o->stream) { - pa_stream_disconnect(o->stream); - pa_stream_unref(o->stream); - } - - ca_free(o); -} - -static int convert_proplist(pa_proplist **_l, pa_proplist *c) { - pa_proplist *l; - ca_prop *i; - - ca_return_val_if_fail(_l, CA_ERROR_INVALID); - ca_return_val_if_fail(c, CA_ERROR_INVALID); - - if (!(l = pa_proplist_new())) - return CA_ERROR_OOM; - - ca_mutex_lock(c->mutex); - - for (i = c->first_item; i; i = i->next_item) - if (pa_proplist_put(l, i->key, CA_PROP_DATA(i), i->nbytes) < 0) { - ca_mutex_unlock(c->mutex); - pa_proplist_free(l); - return PA_ERROR_INVALID; - } - - ca_mutex_unlock(c->mutex); - - *_l = l; - - return PA_SUCCESS; -} - -static pa_proplist *strip_canberra_data(pa_proplist *l) { - const char *key; - void *state = NULL; - ca_assert(l); - - while ((key = pa_proplist_iterate(l, &state))) - if (strncmp(key, "canberra.", 12) == 0) - pa_proplist_remove(l, key); - - return l; -} - -static int translate_error(int error) { - static const int table[PA_ERR_MAX] = { - [PA_OK] = CA_SUCCESS, - [PA_ERR_ACCESS] = CA_ERROR_ACCESS, - [PA_ERR_COMMAND] = CA_ERROR_IO, - [PA_ERR_INVALID] = CA_ERROR_INVALID, - [PA_ERR_EXIST] = CA_ERROR_IO, - [PA_ERR_NOENTITY] = CA_ERROR_NOTFOUND, - [PA_ERR_CONNECTIONREFUSED] = CA_ERROR_NOTAVAILABLE, - [PA_ERR_PROTOCOL] = CA_ERROR_IO, - [PA_ERR_TIMEOUT] = CA_ERROR_IO, - [PA_ERR_AUTHKEY] = CA_ERROR_ACCESS, - [PA_ERR_INTERNAL] = CA_ERROR_IO, - [PA_ERR_CONNECTIONTERMINATED] = CA_ERROR_IO, - [PA_ERR_KILLED] = CA_ERROR_DESTROYED, - [PA_ERR_INVALIDSERVER] = CA_ERROR_INVALID, - [PA_ERR_MODINITFAILED] = CA_ERROR_NODRIVER, - [PA_ERR_BADSTATE] = CA_ERROR_STATE, - [PA_ERR_NODATA] = CA_ERROR_IO, - [PA_ERR_VERSION] = CA_ERROR_NOTSUPPORTED, - [PA_ERR_TOOLARGE] = CA_ERROR_TOOBIG - }; - - ca_assert(error >= 0); - - if (error >= PA_ERR_MAX) - return CA_ERROR_IO; - - return table[error]; -} - -static void context_state_cb(pa_context *pc, void *userdata) { - ca_context *c = userdata; - pa_context_state_t state; - - ca_assert(pc); - ca_assert(c); - - state = pa_context_get_state(pc); - - if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) { - struct outstanding *out; - int ret; - - ret = translate_error(pa_context_errno(pc)); - - ca_mutex_lock(c->outstanding_mutex); - - while ((out = c->outstanding)) { - - PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); - ca_mutex_unlock(c->outstanding_mutex); - - if (out->callback) - out->callback(c, out->id, ret, out->userdata); - outstanding_free(c->outstanding); - - ca_mutex_lock(c->outstanding_mutex); - } - - ca_mutex_unlock(c->outstanding_mutex); - } - - pa_threaded_mainloop_signal(c->mainloop, FALSE); -} - -static void context_subscribe_cb(pa_context *pc, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { - struct outstanding *out, *n; - PA_LLIST_HEAD(struct outstanding, l); - ca_context *c = userdata; - - ca_assert(pc); - ca_assert(c); - - if (t != PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE) - return; - - PA_LLIST_HEAD_INIT(struct outstanding, l); - - ca_mutex_lock(c->outstanding_mutex); - - for (out = c->outstanding; out; out = n) { - n = out->next; - - if (out->type != OUTSTANDING_SAMPLE || out->sink_input != idx) - continue; - - PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); - PA_LLIST_PREPEND(struct outstanding, l, out); - } - - ca_mutex_unlock(c->outstanding_mutex); - - while (l) { - out = l; - - PA_LLIST_REMOVE(struct outstanding, l, out); - - if (out->callback) - out->callback(c, out->id, CA_SUCCESS, out->userdata); - - outstanding_free(out); - } -} - -int driver_open(ca_context *c) { - pa_proplist *l; - struct private *p; - - ca_return_val_if_fail(c, PA_ERROR_INVALID); - ca_return_val_if_fail(!c->driver || streq(c->driver, "pulse"), PA_ERROR_NO_DRIVER); - ca_return_val_if_fail(!PRIVATE(c), PA_ERROR_STATE); - - if (!(p = PRIVATE(c) = ca_new0(struct private, 1))) - return PA_ERROR_OOM; - - if (!(p->mainloop = pa_threaded_mainloop_new())) { - driver_destroy(c); - return PA_ERROR_OOM; - } - - if ((ret = convert_proplist(&l, c->proplist))) { - driver_destroy(c); - return ret; - } - - if (!(p->context = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(p->mainloop), l))) { - pa_proplist_free(l); - driver_destroy(c); - return PA_ERROR_OOM; - } - - pa_proplist_free(l); - - pa_context_set_state_callback(p->context, context_state_cb, c); - pa_context_set_subscribe_callback(p->context, context_subscribe_cb, c); - - if (pa_context_connect(p->context, NULL, 0, NULL) < 0) { - int ret = translate_error(pa_context_errno(p->context)); - driver_destroy(c); - return ret; - } - - pa_threaded_mainloop_lock(p->mainloop); - - if (pa_threaded_mainloop_start(p->mainloop) < 0) { - pa_threaded_mainloop_unlock(p->mainloop); - driver_destroy(c); - return PA_ERROR_INTERNAL; - } - - pa_threaded_mainloop_wait(p->mainloop); - - if (pa_context_get_state(p->context) != PA_CONTEXT_READY) { - int ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - driver_destroy(c); - return ret; - } - - pa_threaded_mainloop_unlock(p->mainloop); - - return CA_SUCCESS; -} - -int driver_destroy(ca_context *c) { - ca_return_val_if_fail(c, PA_ERROR_INVALID); - ca_return_val_if_fail(c->private, PA_ERROR_STATE); - - p = PRIVATE(c); - - if (p->mainloop) - pa_threaded_mainloop_stop(p->mainloop); - - if (p->context) { - pa_context_disconnect(p->context); - pa_context_unref(p->context); - } - - if (p->mainloop) - pa_threaded_mainloop_free(p->mainloop); - - if (p->theme) - ca_theme_data_free(p->theme); - - while (p->outstanding) { - struct outstanding *out = p->outstanding; - PA_LLIST_REMOVE(struct outstanding, p->outstanding, out); - - if (out->callback) - out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata); - - outstanding_free(out); - } - - ca_free(p); -} - -int driver_change_device(ca_context *c, char *device) { - ca_return_val_if_fail(c, PA_ERROR_INVALID); - - /* We're happy with any device change. We might however add code - * here eventually to move all currently played back event sounds - * to the new device. */ - - return CA_SUCCESS; -} - -int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) { - struct private *p; - pa_operation *o; - pa_proplist *l; - int ret = CA_SUCCESS; - - ca_return_val_if_fail(c, PA_ERROR_INVALID); - ca_return_val_if_fail(changed, PA_ERROR_INVALID); - ca_return_val_if_fail(merged, PA_ERROR_INVALID); - ca_return_val_if_fail(c->private, PA_ERROR_STATE); - - p = PRIVATE(c); - - ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); - ca_return_val_if_fail(p->context, PA_ERROR_STATE); - - if ((ret = convert_proplist(&l, changed))) - return ret; - - strip_canberra_data(l); - - pa_threaded_mainloop_lock(p->mainloop); - - /* We start these asynchronously and don't care about the return - * value */ - - if (!(o = pa_context_proplist_update(p->context, PA_UPDATE_REPLACE, l, NULL, NULL))) - ret = translate_error(pa_context_errno(p->context)); - else - pa_operation_unref(o); - - pa_threaded_mainloop_unlock(p->mainloop); - - pa_proplist_free(l); - - return ret; -} - -static int subscribe(ca_context *c) { - struct private *p; - pa_operation *o; - - ca_return_val_if_fail(c, PA_ERROR_INVALID); - ca_return_val_if_fail(c->private, PA_ERROR_STATE); - p = PRIVATE(c); - - ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); - ca_return_val_if_fail(p->context, PA_ERROR_STATE); - ca_return_val_if_fail(!p->subscribed, PA_SUCCESS); - - pa_threaded_mainloop_lock(p->mainloop); - - /* We start these asynchronously and don't care about the return - * value */ - - if (!(o = pa_context_subscribe(p->context, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL))) - ret = translate_error(pa_context_errno(p->context)); - else - pa_operation_unref(o); - - pa_threaded_mainloop_unlock(p->mainloop); - - p->subscribed = TRUE; - - return ret; -} - -static void play_sample_cb(pa_context *c, uint32_t idx, void *userdata) { - struct outstanding *out = userdata; - - ca_assert(c); - ca_assert(out); - - if (idx != PA_INVALID_INDEX) { - out->error = CA_SUCCESS; - out->sink_input = idx; - } else - out->error = translate_error(pa_context_errno(c)); -} - -static void stream_state_cb(pa_stream *s, void *userdata) { - struct private *p; - struct outstanding *out = userdata; - - ca_assert(s); - ca_assert(out); - - p = PRIVATE(out->context); - - if (out->clean_up) { - pa_stream_state_t state; - - state = pa_stream_get_state(s); - - if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED) { - int err; - - ca_mutex_lock(p->context->outstanding_mutex); - PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); - ca_mutex_unlock(p->context->outstanding_mutex); - - err = state == PA_STREAM_FAILED ? translate_error(pa_context_errno(pa_stream_get_context(s))) - - if (out->callback) - out->callback(c, out->id, ret, out->userdata); - - outstanding_free(c->outstanding); - } - } - - pa_threaded_mainloop_signal(c->mainloop, TRUE); -} - -static void stream_drain_cb(pa_stream *s, int success, void *userdata) { - struct private *p; - struct outstanding *out = userdata; - - ca_assert(s); - ca_assert(out); - - p = PRIVATE(out->context); - - ca_assert(out->type = OUTSTANDING_STREAM); - ca_assert(out->clean_up); - - ca_mutex_lock(p->context->outstanding_mutex); - PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); - ca_mutex_unlock(p->context->outstanding_mutex); - - if (out->callback) { - int err; - - err = success ? CA_SUCCESS : translate_error(pa_context_errno(p->context)); - out->callback(out->context, out->id, err, out->userdata); - } - - outstanding_free(out); -} - -static void stream_write_cb(pa_stream *s, size_t bytes, void *userdata) { - struct outstanding *out = userdata; - struct private *p; - void *data; - int ret; - - ca_assert(s); - ca_assert(bytes > 0); - ca_assert(out); - - p = PRIVATE(out->context); - - if (!(data = ca_malloc(bytes))) { - ret = CA_ERROR_OOM - goto finish; - } - - if ((ret = ca_sound_file_read_arbitrary(out->file, data, &bytes)) < 0) - goto finish; - - if (bytes > 0) { - - if ((ret = pa_stream_writesp, data, bytes)) < 0) { - ret = translate_error(ret); - goto finish; - } - - } else { - /* We reached EOF */ - - if (out->type == OUTSTANDING_UPLOAD) { - - if (pa_stream_finish_upload(s) < 0) { - ret = translate_error(pa_context_errno(p->context)); - goto finish; - } - - /* Let's just signal driver_cache() which has been waiting for us */ - pa_threaded_mainloop_signal(c->mainloop, TRUE); - - } else { - ca_assert(out->type = OUTSTANDING_STREAM); - - if (!(o = pa_stream_drain(p->context, stream_drain_cb, out))) { - ret = translate_error(pa_context_errno(p->context)); - goto fail; - } - - pa_operation_unref(o); - } - } - - ca_free(data); - - return; - -finish: - - ca_free(data); - - if (out->clean_up) { - ca_mutex_lock(p->context->outstanding_mutex); - PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); - ca_mutex_unlock(p->context->outstanding_mutex); - - if (out->callback) - out->callback(out->context, out->id, ret, out->userdata); - - outstanding_free(out); - } else { - pa_stream_disconnect(p); - pa_threaded_mainloop_signal(c->mainloop, TRUE); - out->error = ret; - } -} - -static const pa_sample_format_t sample_type_table[] = { - [CA_SAMPLE_S16NE] = PA_SAMPLE_S16NE, - [CA_SAMPLE_S16RE] = PA_SAMPLE_S16RE, - [CA_SAMPLE_U8] = PA_SAMPLE_U8 -}; - -int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) { - struct private *p; - pa_proplist *l = NULL; - const char *n, *vol, *ct; - char *name = NULL; - int err = 0; - pa_volume_t v = PA_VOLUME_NORM; - pa_sample_spec ss; - ca_cache_control_t cache_control = CA_CACHE_CONTROL_NEVER; - struct outstanding *out = NULL; - int try = 3; - - ca_return_val_if_fail(c, PA_ERROR_INVALID); - ca_return_val_if_fail(proplist, PA_ERROR_INVALID); - ca_return_val_if_fail(!userdata || cb, PA_ERROR_INVALID); - ca_return_val_if_fail(c->private, PA_ERROR_STATE); - - p = PRIVATE(c); - - ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); - ca_return_val_if_fail(p->context, PA_ERROR_STATE); - - if (!(out = pa_xnew0(struct outstanding, 1))) { - ret = PA_ERROR_OOM; - goto finish; - } - - out->type = OUTSTANDING_SAMPLE; - out->context = c; - out->sink_input = PA_INVALID_INDEX; - out->id = id; - out->callback = cb; - out->userdata = userdata; - - if ((ret = convert_proplist(&l, proplist))) - goto finish; - - if (!(n = pa_proplist_gets(l, CA_PROP_EVENT_ID))) { - ret = PA_ERROR_INVALID; - goto finish; - } - - if (!(name = ca_strdup(n))) { - ret = PA_ERROR_OOM; - goto finish; - } - - if ((vol = pa_proplist_gets(l, CA_PROP_CANBERRA_VOLUME))) { - char *e = NULL; - double dvol; - - errno = 0; - dvol = strtod(vol, &e); - if (errno != 0 || !e || *e) { - ret = PA_ERROR_INVALID; - goto finish; - } - - v = pa_sw_volume_from_dB(dvol); - } - - if ((ct = pa_proplist_gets(l, CA_PROP_CANBERRA_CACHE_CONTROL))) - if ((ret = ca_parse_cache_control(&cache_control, ct)) < 0) { - ret = PA_ERROR_INVALID; - goto finish; - } - - strip_canberra_data(l); - - if (cb) - if ((ret = subscribe(c)) < 0) - goto finish; - - for (;;) { - pa_threaded_mainloop_lock(p->mainloop); - - /* Let's try to play the sample */ - if (!(o = pa_context_play_sample_with_proplist(p->context, name, NULL, v, l, id, play_sample_cb, out))) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto finish; - } - - while (pa_operation_get_state(o) != OPERATION_DONE) - pa_threaded_mainloop_wait(m); - - pa_operation_unref(o); - - pa_threaded_mainloop_unlock(p->mainloop); - - /* Did we manage to play the sample or did some other error occur? */ - if (out->error != CA_ERROR_NOT_FOUND) - goto finish; - - /* Hmm, we need to play it directly */ - if (cache_control == CA_CACHE_CONTROL_NEVER) - break; - - /* Don't loop forever */ - if (--try <= 0) - break; - - /* Let's upload the sample and retry playing */ - if ((ret = driver_cache(c, proplist)) < 0) - goto fail; - } - - out->type = OUTSTANDING_STREAM; - - /* Let's stream the sample directly */ - if ((ret = ca_lookup_sound(&out->file, &p->theme, proplist)) < 0) - goto fail; - - ss.channels = sample_type_table[ca_sound_file_get_sample_type(f)]; - ss.channels = ca_sound_file_get_nchannels(f); - ss.rate = ca_sound_file_get_rate(f); - - pa_threaded_mainloop_lock(p->mainloop); - - if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l))) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - pa_stream_set_userdata(p->stream, out); - pa_stream_set_state_callback(p->stream, stream_state_cb, out); - pa_stream_set_write_callback(p->stream, stream_request_cb, out); - - if (pa_stream_connect_playback(s, NULL, NULL, 0, NULL, NULL) < 0) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - for (;;) { - pa_stream_state state = pa_stream_get_state(s); - - /* Stream sucessfully created */ - if (state == PA_STREAM_READY) - break; - - /* Check for failure */ - if (state == PA_STREAM_FAILED) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - if (state == PA_STREAM_TERMINATED) { - ret = out->error; - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - pa_threaded_mainloop_wait(p->mainloop); - } - - if ((out->sink_input = pa_stream_get_index(s)) == PA_INVALID_INDEX) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - pa_threaded_mainloop_unlock(p->mainloop); - - ret = CA_SUCCESS; - -finish: - - /* We keep the outstanding struct around if we need clean up later to */ - if (ret == CA_SUCCESS && (out->type == OUTSTANDING_STREAM || cb)) { - out->clean_up = TRUE; - - pa_mutex_lock(p->outstanding_mutex); - PA_LLIST_PREPEND(struct outstanding, p->outstanding, out); - pa_mutex_unlock(p->outstanding_mutex); - } else - outstanding_free(out); - - if (l) - pa_proplist_free(l); - - ca_free(name); - - return ret; -} - -int driver_cancel(ca_context *c, uint32_t id) { - struct private *p; - pa_operation *o; - int ret = CA_SUCCESS; - struct outstanding *out, *n; - - ca_return_val_if_fail(c, PA_ERROR_INVALID); - ca_return_val_if_fail(c->private, PA_ERROR_STATE); - - p = PRIVATE(c); - - ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); - ca_return_val_if_fail(p->context, PA_ERROR_STATE); - - pa_threaded_mainloop_lock(p->mainloop); - - ca_mutex_lock(p->outstanding_mutex); - - /* We start these asynchronously and don't care about the return - * value */ - - for (out = p->outstanding; out; out = n) { - int ret2; - n = out->next; - - if (out->type == OUTSTANDING_UPLOAD || - out->id != id || - out->sink_input == PA_INVALID_INDEX) - continue; - - if (!(o = pa_context_kill_sink_input(p->context, out->sink_input, NULL, NULL))) - ret2 = translate_error(pa_context_errno(p->context)); - else - pa_operation_unref(o); - - /* We make sure here to kill all streams identified by the id - * here. However, we will return only the first error we - * encounter */ - - if (ret2 && ret == CA_SUCCESS) - ret = ret2; - - if (out->callback) - out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata); - - CA_LLIST_REMOVE(struct outstanding, p->outstanding, out); - outstanding_free(out); - } - - ca_mutex_unlock(p->outstanding_mutex); - - pa_threaded_mainloop_unlock(p->mainloop); - - return ret; -} - -int driver_cache(ca_context *c, ca_proplist *proplist) { - struct private *p; - pa_proplist *l = NULL; - const char *n, *ct; - char *name = NULL; - pa_sample_spec ss; - ca_cache_control_t cache_control = CA_CACHE_CONTROL_NEVER; - struct outstanding out; - - ca_return_val_if_fail(c, PA_ERROR_INVALID); - ca_return_val_if_fail(proplist, PA_ERROR_INVALID); - ca_return_val_if_fail(c->private, PA_ERROR_STATE); - - p = PRIVATE(c); - - ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); - ca_return_val_if_fail(p->context, PA_ERROR_STATE); - - if (!(out = ca_new0(struct outstanding, 1))) { - ret = CA_ERROR_OOM; - goto finish; - } - - out->type = OUTSTANDING_UPLOAD; - out->context = c; - out->sink_input = PA_INVALID_INDEX; - - if ((ret = convert_proplist(&l, proplist))) - goto finish; - - if (!(n = pa_proplist_gets(l, CA_PROP_EVENT_ID))) { - ret = PA_ERROR_INVALID; - goto finish; - } - - if (!(name = ca_strdup(n))) { - ret = PA_ERROR_OOM; - goto finish; - } - - if ((ct = pa_proplist_gets(l, CA_PROP_CANBERRA_CACHE_CONTROL))) - if ((ret = ca_parse_cache_control(&cache_control, ct)) < 0) { - ret = PA_ERROR_INVALID; - goto finish; - } - - if (ct == CA_CACHE_CONTROL_NEVER) { - ret = PA_ERROR_INVALID; - goto finish; - } - - strip_canberra_data(l); - - /* Let's stream the sample directly */ - if ((ret = ca_lookup_sound(&out->file, &p->theme, proplist)) < 0) - goto fail; - - ss.channels = sample_type_table[ca_sound_file_get_sample_type(out->file)]; - ss.channels = ca_sound_file_get_nchannels(out->file); - ss.rate = ca_sound_file_get_rate(out->file); - - pa_threaded_mainloop_lock(p->mainloop); - - if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l))) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - pa_stream_set_userdata(out->stream, out); - pa_stream_set_state_callback(out->stream, stream_state_cb, out); - pa_stream_set_write_callback(out->stream, stream_request_cb, out); - - if (pa_stream_connect_upload(s, ca_sound_file_get_size(out->file)) < 0) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - for (;;) { - pa_stream_state state = pa_stream_get_state(s); - - /* Stream sucessfully created and uploaded */ - if (state == PA_STREAM_TERMINATED) - break; - - /* Check for failure */ - if (state == PA_STREAM_FAILED) { - ret = translate_error(pa_context_errno(p->context)); - pa_threaded_mainloop_unlock(p->mainloop); - goto fail; - } - - pa_threaded_mainloop_wait(p->mainloop); - } - - pa_threaded_mainloop_unlock(p->mainloop); - - ret = CA_SUCCESS; - -finish: - - outstanding_free(out); - - if (l) - pa_proplist_free(l); - - ca_free(name); - - return ret; -} diff --git a/read-sound-file.c b/read-sound-file.c deleted file mode 100644 index 8670445..0000000 --- a/read-sound-file.c +++ /dev/null @@ -1,175 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "read-sound-file.h" - -struct ca_sound_file { - ca_wav *wav; - ca_vorbis *vorbis; - char *filename; - - unsigned nchannels; - unsigned rate; - ca_sample_type_t type; -}; - -int ca_sound_file_open(ca_sound_file *_f, const char *fn) { - FILE *file; - ca_sound_file *f; - int ret; - - ca_return_val_if_fail(_f, PA_ERROR_INVALID); - ca_return_val_if_fail(fn, PA_ERROR_INVALID); - - if (!(f = ca_new0(ca_sound_file, 1))) - return CA_ERROR_OOM; - - if (!(f->filename = ca_strdup(fn))) { - ret = CA_ERROR_OOM; - goto fail; - } - - if (!(file = fopen(fn, "r"))) { - ret = errno == ENOENT ? CA_ERROR_NOTFOUND : CA_ERROR_SYSTEM; - goto fail; - } - - if ((ret = ca_wav_open(&f->wav, file)) == CA_SUCCESS) { - f->nchannels = ca_wav_get_nchannels(f->wav); - f->rate = ca_wav_get_rate(f->wav); - f->type = ca_wav_get_sample_type(f->wav); - *f = f; - return CA_SUCCESS; - } - - if (ret == CA_ERROR_CORRUPT) { - - if (fseek(file, 0, SEEK_SET) < 0) { - ret = CA_ERROR_SYSTEM; - goto fail; - } - - if ((ret = ca_vorbis_open(&f->vorbis, file)) == CA_SUCCESS) { - f->nchannels = ca_vorbis_get_nchannels(f->vorbis); - f->rate = ca_vorbis_get_rate(f->vorbis); - f->type = CA_SAMPLE_S16NE; - *f = f; - return CA_SUCCESS; - } - } - -fail: - - ca_free(f->filename); - ca_free(f); - - return ret; -} - -void ca_sound_file_close(ca_sound_file *f) { - ca_assert(f); - - if (f->wav) - ca_wav_free(f->wav); - if (f->vorbis) - ca_vorbis_free(f->vorbis); - ca_free(f); -} - -unsigned ca_sound_file_get_nchannels(ca_sound_file *f) { - ca_assert(f); - return f->nchannels; -} - -unsigned ca_sound_file_get_rate(ca_sound_file *f) { - ca_assert(f); - return f->nchannels; -} - -ca_sample_type_t ca_sound_file_get_sample_type(ca_sound_file *f) { - ca_assert(f); - return f->type; -} - -int ca_sound_file_read_int16(ca_sound_file *f, int16_t *d, unsigned *n) { - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(d, CA_ERROR_INVALID); - ca_return_val_if_fail(n, CA_ERROR_INVALID); - ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); - ca_return_val_if_fail(f->wav || f->vorbis, CA_ERROR_STATE); - ca_return_val_if_fail(f->type == CA_SAMPLE_S16NE || f->type == CA_SAMPLE_S16RE, CA_ERROR_STATE); - - if (f->wav) - return ca_wav_read_s16le(f->wav, d, n); - else - return ca_vorbis_read_s16ne(f->wav, d, n); -} - -int ca_sound_file_read_uint8(ca_sound_file *fn, uint8_t *d, unsigned *n) { - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(d, CA_ERROR_INVALID); - ca_return_val_if_fail(n, CA_ERROR_INVALID); - ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); - ca_return_val_if_fail(f->wav && !f->vorbis, CA_ERROR_STATE); - ca_return_val_if_fail(f->type == CA_SAMPLE_U8, CA_ERROR_STATE); - - if (f->wav) - return ca_wav_read_u8(f->wav, d, n); -} - -int ca_sound_file_read_arbitrary(ca_sound_file *f, void *d, size_t *n) { - int ret; - - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(d, CA_ERROR_INVALID); - ca_return_val_if_fail(n, CA_ERROR_INVALID); - ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); - ca_return_val_if_fail(f->wav && !f->vorbis, CA_ERROR_STATE); - - switch (f->type) { - case CA_SAMPLE_S16NE: - case CA_SAMPLE_S16RE: { - unsigned k; - - k = *n / sizeof(int16_t); - if ((ret = ca_sound_file_read_int16(f, d, &k)) == CA_SUCCESS) - *n = k * sizeof(int16_t); - - break; - } - - case CA_SAMPLE_S16RE: { - unsigned k; - - k = *n; - if ((ret = ca_sound_file_read_uint8(f, d, &k)) == CA_SUCCESS) - *n = k; - - break; - } - - default: - ca_assert_not_reached(); - } - - return ret; -} diff --git a/read-sound-file.h b/read-sound-file.h deleted file mode 100644 index 5ab7e66..0000000 --- a/read-sound-file.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef foocareadsoundfilehfoo -#define foocareadsoundfilehfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -typedef enum ca_sample_type { - CA_SAMPLE_S16NE, - CA_SAMPLE_S16RE, - CA_SAMPLE_U8 -} ca_sample_type_t; - -typedef struct ca_sound_file wa_sound_file; - -int ca_sound_file_open(ca_sound_file *f, const char *fn); -void ca_sound_file_close(ca_sound_file *f); - -unsigned ca_sound_file_get_nchannels(ca_sound_file *f); -unsigned ca_sound_file_get_rate(ca_sound_file *f); -ca_sample_type_t ca_sound_file_get_sample_type(ca_sound_file *f); - -size_t ca_sound_file_get_size(ca_sound_file *f); - -int ca_sound_file_read_int16(ca_sound_file *f, int16_t *d, unsigned *n); -int ca_sound_file_read_uint8(ca_sound_file *f, uint8_t *d, unsigned *n); - -int ca_sound_file_read_arbitrary(ca_sound_file *f, void *d, size_t *n); - -#endif diff --git a/read-vorbis.c b/read-vorbis.c deleted file mode 100644 index 717f851..0000000 --- a/read-vorbis.c +++ /dev/null @@ -1,148 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "read-vorbis.h" - -#define FILE_SIZE_MAX (64U*1024U*1024U) - -struct ca_vorbis { - OggVorbis_File ovf; -}; - -static int convert_error(int or) { - switch (or) { - case OV_NOSEEK: - case OV_BADPACKET: - case OV_BADLINK: - case OV_FAULT: - case OV_EREAD: - case OV_HOLE: - return CA_ERROR_IO; - - case OV_EIMPL: - case OV_EVERSION: - case OV_ENOTAUDIO - return CA_ERROR_NOT_SUPPORTED; - - case OV_ENOTVORBIS: - case OV_EBADHEADER: - case OV_EOF: - return CA_ERROR_CORRUPT; - - case OV_EINVAL: - return CA_ERROR_INVALID; - - default: - return CA_ERROR_INTERNAL; - } -} - -int ca_vorbis_open(ca_vorbis **_v, FILE *f) { - int ret, or; - ca_vorbis *v; - int64_t n; - - ca_return_val_if_fail(_v, CA_ERROR_INVALID); - ca_return_val_if_fail(f, CA_ERROR_INVALID); - - if (!(v = ca_new(ca_vorbis, 1))) - return CA_ERROR_OOM; - - if ((or = ov_open(f, &v->ovf, NULL, 0)) < 0) { - ret = convert_error(or); - goto fail; - } - - if ((n = ov_pcm_total(&ovf, -1)) < 0) { - ret = convert_error(or); - ov_clear(&v->ovf); - goto fail; - } - - if (n * sizeof(int16_t) > FILE_SIZE_MAX) { - ret = CA_ERROR_TOOBIG; - ov_clear(&v->ovf); - goto fail; - } - - *_v = v; - - return CA_SUCCESS; - -fail: - - ca_free(v); - return ret; -} - -void ca_vorbis_close(ca_vorbis *v) { - ca_assert(v); - - ov_clear(&v->ovf); - ca_free(v); -} - -unsigned ca_vorbis_get_nchannels(ca_vorbis *v) { - vorbis_info *vi; - ca_assert(v); - - ca_assert_se(vi = ov_info(&vf, -1)); - - return vi->channels; -} - -unsigned ca_vorbis_get_rate(ca_vorbis *v) { - vorbis_info *vi; - ca_assert(v); - - ca_assert_se(vi = ov_info(&vf, -1)); - - return (unsigned) vi->rate; -} - -int ca_vorbis_read_int16ne(ca_vorbis *v, int16_t *d, unsigned *n){ - long r; - int section; - - ca_return_val_if_fail(w, CA_ERROR_INVALID); - ca_return_val_if_fail(d, CA_ERROR_INVALID); - ca_return_val_if_fail(n, CA_ERROR_INVALID); - ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); - - r = ov_read(&v->ovf, d, *n * sizeof(float), -#ifdef WORDS_BIGENDIAN - 1, -#else - 0, -#endif - 2, 1, §ion); - - if (r < 0) - return convert_error(or); - - /* We only read the first section */ - if (section != 0) - return 0; - - *n = (unsigned) r; - return CA_SUCCESS; -} diff --git a/read-vorbis.h b/read-vorbis.h deleted file mode 100644 index 5521c09..0000000 --- a/read-vorbis.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef foocareadvorbishfoo -#define foocareadvorbishfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include - -typedef struct ca_vorbis ca_vorbis; - -int ca_vorbis_open(ca_vorbis *v, FILE *f); -void ca_vorbis_close(ca_vorbis *v); - -unsigned ca_vorbis_get_nchannels(ca_vorbis *v); -unsigned ca_vorbis_get_rate(ca_vorbis *v); - -int ca_vorbis_read_s16ne(ca_vorbis *v, int16_t *d, unsigned *n); - -#endif diff --git a/read-wav.c b/read-wav.c deleted file mode 100644 index 1f3d634..0000000 --- a/read-wav.c +++ /dev/null @@ -1,247 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "read-wav.h" - -#define FILE_SIZE_MAX (64U*1024U*1024U) - -struct ca_wav { - uint32_t data_size; - FILE *file; - - unsigned nchannels; - unsigned rate; - unsigned depth; -}; - -static int skip_to_chunk(ca_wav *v, uint32_t id, uint32_t *size) { - - ca_return_val_if_fail(v, CA_ERROR_INVALID); - ca_return_val_if_fail(size, CA_ERROR_INVALID); - - for (;;) { - uint32_t chunk[2]; - size_t s; - - if (fread(chunk, sizeof(uint32), CA_ELEMENTSOF(chunk), w->file) != CA_ELEMENTSOF(chunk)) - goto fail_io; - - s = PA_UINT32_FROM_LE(chunk[1]); - - if (s <= 0 || s >= FILE_SIZE_MAX) - return CA_ERROR_TOOBIG; - - if (PA_UINT32_FROM_LE(chunk[0]) == id) { - *size = s; - break; - } - - if (fseek(w->file, s, SEEK_CUR) < 0) - return CA_ERROR_SYSTEM; - } - - return CA_SUCCESS; - -fail_io: - - if (feof(f)) - return CA_ERROR_CORRUPT; - else if (ferror(f)) - return CA_ERROR_SYSTEM; - - ca_assert_not_reached(); -} - -int ca_wav_open(ca_wav **_w, FILE *f) { - uint32_t header[3], fmt_chunk[4]; - int ret; - ca_wav *w; - uint32_t file_size, fmt_size, data_size; - - ca_return_val_if_fail(_w, CA_ERROR_INVALID); - ca_return_val_if_fail(f, CA_ERROR_INVALID); - - if (!(w = ca_new(ca_wav, 1))) - return CA_ERROR_OOM; - - v->file = f; - - if (fread(header, sizeof(uint32), CA_ELEMENTSOF(header), f) != CA_ELEMENTSOF(header)) - goto fail_io; - - if (PA_UINT32_FROM_LE(header[0]) != 0x46464952U || - PA_UINT32_FROM_LE(header[2]) != 0x45564157U) { - ret = CA_ERROR_CORRUPT; - goto fail; - } - - file_size = PA_UINT32_FROM_LE(header[1]); - - if (file_size <= 0 || file_size >= FILE_SIZE_MAX) { - ret = CA_ERROR_TOOBIG; - goto fail; - } - - /* Skip to the fmt chunk */ - if ((ret = skip_to_chunk(w, 0x20746d66U, &fmt_size)) < 0) - goto fail; - - if (fmt_size != 16) { - ret = CA_ERROR_NOT_SUPPORTED; - goto fail; - } - - if (fread(fmt_chunk, sizeof(uint32), CA_ELEMENTSOF(fmt_chunk), f) != CA_ELEMENTSOF(fmt_chunk)) - goto fail_io; - - if (PA_UINT32_FROM_LE(fmt_chunk[0]) & 0xFFFF != 1) { - ret = CA_ERROR_NOT_SUPPORTED; - goto fail; - } - - w->nchannels = PA_UINT32_FROM_LE(fmt_chunk[0]) >> 16; - w->rate = PA_UINT32_FROM_LE(fmt_chunk[1]); - w->depth = PA_UINT32_FROM_LE(fmt_chunk[3]) >> 16; - - if (w->nchannels <= 0 || w->nrate <= 0) { - ret = CA_ERROR_CORRUPT; - goto fail; - } - - if (w->depth != 16 && w->depth != 8) { - ret = CA_ERROR_NOT_SUPPORTED; - goto fail; - } - - /* Skip to the data chunk */ - if ((ret = skip_to_chunk(w, 0x61746164U, &w->data_size)) < 0) - goto fail; - - if ((w->data_size % (w->depth/8)) != 0) { - ret = CA_ERROR_CORRUPT; - goto fail; - } - - *_w = w; - - return PA_SUCCESS; - -fail_io: - - if (feof(f)) - ret = CA_ERROR_CORRUPT; - else if (ferror(f)) - ret = CA_ERROR_SYSTEM; - else - ca_assert_not_reached(); - -fail: - - ca_free(w); - - return ret; -} - -void ca_wav_close(ca_wav *w) { - ca_assert(w); - - fclose(w->file); - ca_free(w); -} - -unsigned ca_wav_get_nchannels(ca_wav *w) { - ca_assert(w); - - return w->nchannels; -} - -unsigned ca_wav_get_rate(ca_wav *w) { - ca_assert(w); - - return w->rate; -} - -ca_sample_type_t ca_wav_get_sample_type(ca_wav *f) { - ca_assert(w); - - return w->depth == 16 ? -#ifdef WORDS_BIGENDIAN - CA_SAMPLE_S16RE -#else - CA_SAMPLE_S16NE -#endif - : CA_SAMPLE_U8; -} - -int ca_wav_read_s16le(ca_wav *w, int16_t *d, unsigned *n) { - unsigned remaining; - - ca_return_val_if_fail(w, CA_ERROR_INVALID); - ca_return_val_if_fail(w->depth == 16, CA_ERROR_INVALID); - ca_return_val_if_fail(d, CA_ERROR_INVALID); - ca_return_val_if_fail(n, CA_ERROR_INVALID); - ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); - - remaining = w->data_size / sizeof(int16_t); - - if (*n > remaining) - *n = remaining; - - if (*n > 0) { - *n = fread(d, sizeof(int16_t), *n, w->file); - - if (*n <= 0 && ferror(w->file)) - return CA_ERROR_SYSTEM; - - ca_assert(w->data_size >= *n * sizeof(int16_t)); - w->data_size -= *n * sizeof(int16_t); - } - - return CA_SUCCESS; -} - -int ca_wav_read_u(ca_wav *w, uint8_t *d, unsigned *n) { - unsigned remaining; - - ca_return_val_if_fail(w, CA_ERROR_INVALID); - ca_return_val_if_fail(w->depth == 8, CA_ERROR_INVALID); - ca_return_val_if_fail(d, CA_ERROR_INVALID); - ca_return_val_if_fail(n, CA_ERROR_INVALID); - ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); - - remaining = w->data_size / sizeof(uint8_t); - - if (*n > remaining) - *n = remaining; - - if (*n > 0) { - *n = fread(d, sizeof(uint8_t), *n, w->file); - - if (*n <= 0 && ferror(w->file)) - return CA_ERROR_SYSTEM; - - ca_assert(w->data_size >= *n * sizeof(int16_t)); - w->data_size -= *n * sizeof(uint8_t); - } - - return CA_SUCCESS; -} diff --git a/read-wav.h b/read-wav.h deleted file mode 100644 index 8344cc9..0000000 --- a/read-wav.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef foocareadwavhfoo -#define foocareadwavhfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include - -#include "read-sound-file.h" - -typedef struct ca_wav ca_wav; - -int ca_wav_open(ca_wav **v, FILE *f); -void ca_wav_close(ca_wav *f); - -unsigned ca_wav_get_nchannels(ca_wav *f); -unsigned ca_wav_get_rate(ca_wav *f); -ca_sample_type ca_wav_get_sample_type(ca_wav *f); - -int ca_wav_read_u8(ca_wav *f, uint8_t *d, unsigned *n); -int ca_wav_read_s16le(ca_wav *f, int16_t *d, unsigned *n); - -#endif diff --git a/sound-theme-spec.c b/sound-theme-spec.c deleted file mode 100644 index fd0f95b..0000000 --- a/sound-theme-spec.c +++ /dev/null @@ -1,632 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "sound-theme-spec.h" -#include "llist.h" - -#define DEFAULT_THEME "freedesktop" -#define FALLBACK_THEME "freedesktop" -#define DEFAULT_OUTPUT_PROFILE "stereo" -#define N_THEME_DIR_MAX 8 - -typedef struct ca_data_dir ca_data_dir; - -struct ca_data_dir { - CA_LLIST_FIELDS(ca_data_dir); - - char *name; - char *output_profile; -}; - -struct ca_theme_data { - char *name; - - CA_LLIST_HEAD(ca_data_dir, data_dirs); - ca_theme_dir *last_dir; - - unsigned n_theme_dir; - ca_bool_t loaded_fallback_theme; -}; - -static int get_data_home(char **e) { - const char *env, *subdir; - char *r; - ca_return_val_if_fail(e, CA_ERROR_INVALID); - - if ((env = getenv("XDG_DATA_HOME")) && *env == '/') - subdir = ""; - else if ((env = getenv("HOME")) && *env == '/') - subdir = "/.local/share"; - else { - *e = NULL; - return CA_SUCCESS; - } - - if (!(r = ca_new(char, strlen(env) + strlen(subdir) + 1))) - return CA_ERROR_OOM; - - sprintf(r, "%s%s", env, subdir); - *e = r; - - return CA_SUCCESS; -} - -static ca_bool_t data_dir_matches(ca_data_dir *d, const char*output_profile) { - ca_assert(d); - ca_assert(profile); - - /* We might want to add more elaborate matching here eventually */ - - return streq(d->profile, output_profile); -} - -static ca_data_dir* find_data_dir(ca_theme_data *t, const char *name) { - ca_data_dir *d; - - ca_assert(t); - ca_assert(name); - - for (d = t->data_dirs; d; d = d->next) - if (streq(d->name, name)) - return d; - - return NULL; -} - -static int add_data_dir(ca_theme_data *t, const char *name) { - ca_data_dir *d; - - ca_return_val_if_fail(t, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - - if (find_data_dir(t, name)) - return; - - if (!(d = ca_new0(ca_data_dir, 1))) - return CA_ERROR_OOM; - - if (!(d->name = ca_strdup(name))) { - ca_free(d); - return CA_ERROR_OOM; - } - - CA_LLIST_INSERT_AFTER(ca_data_dir, t->data_dirs, t->last_dir, d); - t->last_dir = d; - - return CA_SUCCESS; -} - -static int load_theme_path(ca_theme_data *t, const char *prefix, const char *name) { - char *fn, *inherits = NULL; - FILE *f; - ca_bool_t in_sound_theme_section = FALSE; - ca_data_dir *current_data_dir = NULL; - - ca_return_val_if_fail(t, CA_ERROR_INVALID); - ca_return_val_if_fail(prefix, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - - if (!(fn = ca_new(char, strlen(prefix) + sizeof("/sounds/")-1 + strlen(name) + sizeof("index.theme")))) - return CA_ERROR_OOM; - - sprintf(fn, "%s/sounds/%s/index.theme", prefix, name); - f = fopen(fn, "r"); - ca_free(fn); - - if (!f) { - if (errno == ENOENT) - return CA_ERROR_NOTFOUND; - - return CA_ERROR_SYSTEM; - } - - for (;;) { - char ln[1024]; - - if (!(fgets(ln, f))) { - - if (feof(f)) - break; - - ca_assert(ferror(f)); - ret = CA_ERROR_SYSTEM; - goto fail; - } - - ln[strcspn(ln, "\n\r#")] = 0; - - if (!ln[0]) - continue; - - if (streq(ln, "[Sound Theme]")) { - in_sound_theme_section = TRUE; - current_data_dir = NULL; - continue; - } - - if (ln[0] == '[' && ln[strlen(ln-1)] == ']') { - char *d; - - if (!(d = ca_strndup(ln+1, strlen(ln)-2))) { - ret = CA_ERROR_OOM; - goto fail; - } - - current_data_dir = find_data_dir(e, d); - ca_free(d); - - in_sound_theme_section = FALSE; - continue; - } - - ca_assert(!in_sound_theme_section || !current_data_dir); - ca_assert(!current_data_dir || !in_sound_theme_section); - - if (in_sound_theme_section) { - - if (!strncmp(ln, "Inherits", 8)) { - - if (inherits) { - ret = CA_ERROR_CORRUPT; - goto fail; - } - - if (!(inherits = ca_strdup(ln + 8))) { - ret = CA_ERROR_OOM; - goto fail; - } - - continue; - } - - if (!strncmp(ln, "Directories", 11)) { - char *d; - - d = ln+11; - for (;;) { - size_t k = strcspn(d, ", "); - - if (k > 0) { - char *p; - - if (!(p = ca_strndup(d, k))) { - ret = CA_ERROR_OOM; - goto fail; - } - - ret = add_data_dir(t, p); - ca_free(p); - - if (ret != CA_SUCCESS) - goto fail; - } - - if (d[k] == 0) - break; - - = k+1; - } - - continue; - } - } - - if (current_data_dir) { - - if (!strncmp(ln, "OutputProfile", 13)) { - - if (current_data_dir->output_profile && !streq(current_data_dir->output_profile, ln+13)) { - ret = CA_ERROR_CORRUPT; - goto fail; - } - - if (!(current_data_dir->output_profile = ca_strdup(ln+13))) { - ret = CA_ERROR_OOM; - goto fail; - } - - continue; - } - } - } - - t->n_theme_dir ++; - - if (inherits) { - i = inherits; - for (;;) { - size_t k = strcspn(i, ", "); - - if (k > 0) { - char *p; - - if (!(p = ca_strndup(i, k))) { - ret = CA_ERROR_OOM; - goto fail; - } - - ret = load_theme_dir(t, p); - ca_free(p); - - if (ret != CA_SUCCESS) - goto fail; - } - - if (i[k] == 0) - break; - - i = k+1; - } - } - - ret = CA_SUCCESS; - -fail: - - ca_free(inherits); - ca_free(directories); - fclose(f); - - return ret; -} - -static int load_theme_dir(ca_theme_data *t, const char *name) { - ca_return_val_if_fail(t, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - ca_return_val_if_fail(t->n_theme_dir < N_THEME_DIR_MAX, CA_ERROR_CORRUPT); - - if ((ret = get_data_home(&e)) < 0) - return ret; - - if (streq(name, FALLBACK_THEME)) - t->loaded_fallback_theme = TRUE; - - if (e) { - ret = load_theme_path(t, e, name); - ca_free(e); - - if (ret != CA_ERROR_NOTFOUND) - return ret; - } - - if (!(e = getenv("XDG_DATA_DIRS")) || *e = 0) - e = "/usr/local/share:/usr/share"; - - for (;;) { - size_t k; - - k = strcspn(e, ":"); - - if (e[0] == '/' && k > 0) { - char *p; - - if (!(p = ca_strndup(e, k))) - return CA_ERROR_OOM; - - ret = load_theme_path(t, p, name); - ca_free(p); - - if (ret != CA_ERROR_NOTFOUND) - return ret; - } - - if (e[k] == 0) - break; - - e += k+1; - } - - return CA_ERROR_NOTFOUND; -} - -static int load_theme_data(ca_theme_data **_t, const char *name) { - ca_theme_data *t; - int ret; - - ca_return_val_if_fail(_t, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - - if (*_t) - if (streq((*_t)->name, name)) - return CA_SUCCESS; - - if (!(t = pa_xnew0(ca_theme_data, 1))) - return CA_ERROR_OOM; - - if (!(t->name = ca_strdup(name))) { - ret = CA_ERROR_OOM; - goto fail; - } - - if ((ret = load_theme_dir(t, name)) < 0) - goto fail; - - if (!t->loaded_fallback_theme) - if ((ret = load_theme_dir(t, FALLBACK_THEME)) < 0) - goto fail; - - if (*_t) - ca_theme_data_free(*_t); - - *_t = t; - - return CA_SUCCESS; - -fail: - - if (t) - ca_theme_data_free(t); - - return ret; -} - -static int find_sound_for_suffix(ca_sound_file **f, ca_theme_data *t, const char *name, const char *path, const char *suffix, const char *locale, const char *subdir) { - const char *fn; - - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - ca_return_val_if_fail(path, CA_ERROR_INVALID); - ca_return_val_if_fail(path[0] == '/', CA_ERROR_INVALID); - ca_return_val_if_fail(suffix, CA_ERROR_INVALID); - - if (!(fn = ca_sprintf_malloc("%s/%s%s%s%s%s%s", - path, - t ? "/" : "", - t ? t->name : "", - subdir ? "/" : "" - subdir ? subdir : "", - locale ? "/" : "", - locale ? locale : "", - name, suffix))) - return CA_ERROR_OOM; - - ret = ca_sound_file_open(f, fn); - ca_free(fn); - - return ret; -} - -static int find_sound_in_path(ca_sound_file **f, ca_theme_data *t, const char *name, const char *path, const char *locale, const char *subdir) { - int ret; - - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - ca_return_val_if_fail(path, CA_ERROR_INVALID); - ca_return_val_if_fail(path[0] == '/', CA_ERROR_INVALID); - - if (!(p = ca_new(char, strlen(path) + sizeof("/sounds")))) - return CA_ERROR_OOM; - - sprintf(p, "%s/sounds", path); - - if ((ret = find_sound_for_suffix(f, t, name, p, ".ogg", locale, subdir)) == CA_ERROR_NOTFOUND) - ret = find_sound_for_suffix(f, t, name, p, ".wav", locale, subdir); - - ca_free(p); - - return ret; -} - -static int find_sound_in_subdir(ca_sound_file **f, ca_theme_data *t, const char *name, const char *locale, const char *subdir) { - int ret; - char *e = NULL; - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - - if ((ret = get_data_home(&e)) < 0) - return ret; - - if (e) { - ret = find_sound_in_path(f, t, name, e, locale, subdir); - ca_free(e); - - if (ret != CA_ERROR_NOTFOUND) - return ret; - } - - if (!(e = getenv("XDG_DATA_DIRS")) || *e = 0) - e = "/usr/local/share:/usr/share"; - - for (;;) { - size_t k; - - k = strcspn(e, ":"); - - if (e[0] == '/' && k > 0) { - char *p; - - if (!(p = ca_strndup(e, k))) - return CA_ERROR_OOM; - - ret = find_sound_in_path(f, t, name, p, locale, subdir); - ca_free(p); - - if (ret != CA_ERROR_NOTFOUND) - return ret; - } - - if (e[k] == 0) - break; - - e += k+1; - } - - return CA_ERROR_NOTFOUND; -} - -static int find_sound_for_profile(ca_sound_file **f, ca_theme_data *t, const char *name, const char *locale, const char *profile) { - ca_data_dir *d; - - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - - if (!t) - return find_sound_in_subdir(f, NULL, name, locale, NULL); - - for (d = t->data_dirs; d; d = d->next) - if (data_dir_matches(d, profile)) { - int ret; - - if ((ret = find_sound_in_subdir(f, t, name, locale, d->name)) != CA_ERROR_NOTFOUND) - return ret; - } - - return CA_ERROR_NOTFOUND; -} - -static int find_sound_in_locale(ca_sound_file **f, ca_theme_data *t, const char *name, const char *locale, const char *profile) { - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - ca_return_val_if_fail(profile, CA_ERROR_INVALID); - - /* First, try the profile def itself */ - if ((ret = find_sound_for_profile(f, t, name, locale, profile)) != CA_ERROR_NOTFOUND) - return ret; - - /* Then, fall back to stereo */ - if (!streq(profile, DEFAULT_OUTPUT_PROFILE)) - if ((ret = find_sound_for_profile(f, t, name, locale, DEFAULT_PROFILE)) != CA_ERROR_NOTFOUND) - return ret; - - /* And fall back to no profile */ - return find_sound_for_profile(f, t, name, locale, NULL); -} - -static int find_sound_for_locale(ca_sound_file **f, ca_theme_data *theme, const char *name, const char *locale, const char *profile) { - const char *e; - - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - ca_return_val_if_fail(locale, CA_ERROR_INVALID); - ca_return_val_if_fail(profile, CA_ERROR_INVALID); - - /* First, try the locale def itself */ - if ((ret = find_sound_in_locale(f, theme, name, locale, profile)) != CA_ERROR_NOTFOUND) - return ret; - - /* Then, try to truncate at the @ */ - if ((e = strchr(locale, '@'))) { - char *t; - - if (!(t = ca_strndup(t, e - locale))) - return CA_ERROR_OOM; - - ret = find_sound_in_locale(f, theme, name, t, profile); - ca_free(t); - - if (ret != CA_ERROR_NOTFOUND) - return ret; - } - - /* Followed by truncating at the _ */ - if ((e = strchr(locale, '_'))) { - char *t; - - if (!(t = ca_strndup(t, e - locale))) - return CA_ERROR_OOM; - - ret = find_sound_in_locale(f, theme, name, t, profile); - ca_free(t); - - if (ret != CA_ERROR_NOTFOUND) - return ret; - } - - /* Then, try "C" as fallback locale */ - if (strcmp(locale, "C")) - if ((ret = find_sound_in_locale(f, theme, name, "C", profile)) != CA_ERROR_NOTFOUND) - return ret; - - /* Try without locale */ - return find_sound_in_locale(f, theme, name, NULL, profile); -} - -static int find_sound_for_theme(ca_sound_file **f, ca_theme_data **t, const char *theme, const char *name, const char *locale, const char *profile) { - int ret; - - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(t, CA_ERROR_INVALID); - ca_return_val_if_fail(theme, CA_ERROR_INVALID); - ca_return_val_if_fail(name, CA_ERROR_INVALID); - ca_return_val_if_fail(locale, CA_ERROR_INVALID); - ca_return_val_if_fail(profile, CA_ERROR_INVALID); - - /* First, try in the theme itself */ - if ((ret = load_theme_data(t, theme)) == CA_SUCCESS) - if ((ret = find_sound_in_theme(f, t, name, locale, profile)) != CA_ERROR_NOTFOUND) - return ret; - - /* Then, fall back to "unthemed" files */ - return find_sound_in_theme(f, NULL, name, locale, profile); -} - -int ca_lookup_sound(ca_sound_file **f, ca_theme_data **t, ca_proplist *p) { - int ret; - const char *name, *fname; - - ca_return_val_if_fail(f, CA_ERROR_INVALID); - ca_return_val_if_fail(t, CA_ERROR_INVALID); - ca_return_val_if_fail(p, CA_ERROR_INVALID); - - ca_mutex_lock(p->mutex); - - if ((name = ca_proplist_gets(p, CA_PROP_EVENT_ID))) { - const char *theme, *locale, *profile; - - if (!(theme = ca_proplist_gets(p, CA_PROP_CANBERRA_XDG_THEME_NAME))) - theme = DEFAULT_THEME; - - if (!(locale = ca_proplist_gets(p, CA_PROP_APPLICATION_LANGUAGE))) - if (!(locale = setlocale(LC_MESSAGES, NULL))) - locale = "C"; - - if (!(profile = ca_proplist_gets(p, CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE))) - profile = DEFAULT_OUTPUT_PROFILE; - - ret = find_sound_for_theme(f, t, theme, name, locale, profile); - - } else if ((fname = ca_proplist_gets(p, CA_PROP_MEDIA_FILENAME))) - ret = ca_sound_file_open(f, fname); - else - ret = CA_ERROR_INVALID; - - ca_mutex_unlock(p->mutex); - - return ret; -} - -void ca_theme_data_free(ca_theme_data *t) { - ca_assert(t); - - while (t->data_dirs) { - ca_data_dir *d = t->data_dirs; - - CA_LLIST_REMOVE(ca_data_dir, t->data_dirs, d); - - ca_free(d->name); - ca_free(d->output_profile); - ca_free(d); - } - - ca_free(t->name); - ca_free(t); -} diff --git a/sound-theme-spec.h b/sound-theme-spec.h deleted file mode 100644 index 527ad11..0000000 --- a/sound-theme-spec.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef foocasoundthemespechfoo -#define foocasoundthemespechfoo - -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include "read-sound-file.h" -#include "proplist.h" - -typedef struct ca_theme_data ca_theme_data; - -int ca_lookup_sound(ca_sound_file **f, ca_theme_data **t, ca_proplist *p); -void ca_theme_data_free(ca_theme_data *t); - -#endif diff --git a/src/canberra.c b/src/canberra.c new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/canberra.c @@ -0,0 +1 @@ + diff --git a/src/canberra.h b/src/canberra.h new file mode 100644 index 0000000..2d48cd8 --- /dev/null +++ b/src/canberra.h @@ -0,0 +1,210 @@ +#ifndef foocanberrahfoo +#define foocanberrahfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include +#include +#include + +/* + + Requirements & General observations: + + - Property set extensible. To be kept in sync with PulseAudio and libsydney. + - Property keys need to be valid UTF-8, text values, too. + - Will warn if application.name or application.id not set. + - Will fail if event.id not set + - Fully thread safe, not async-signal safe + - Error codes are returned immediately, as negative integers + - If the control.cache property is set it will control whether the + specific sample will be cached in the server: + + * permanent: install the sample permanently in the server (for usage in gnome-session) + * volatile: install the sample temporarily in the server (will be expelled from cache on cache pressure or after timeout) + * never: never cache the sample in the server, always stream + + control.cache will default to "volatile" for ca_context_cache() and "never" for ca_context_play(). + control.cache is only a hint, the server may ignore this value + - application.process.* will be filled in automatically but may be overwritten by the client. + They thus should not be used for authentication purposes. + - The property list attached to the client object in the sound + server will be those specified via ca_context_prop_xx(). + - The property list attached to cached samples in the sound server + will be those specified via ca_context_prop_xx() at sample upload time, + combined with those specified directly at the _cache() function call + (the latter potentially overwriting the former). + - The property list attached to sample streams in the sound server + will be those attached to the cached sample (only if the event + sound is cached, of course) combined (i.e. potentially + overwritten by) those set via ca_context_prop_xx() at play time, + combined (i.e. potentially overwritten by) those specified + directly at the _play() function call. + - It is recommended to set application.* once before calling + _open(), and media.* event.* at both cache and play time. + +*/ + +#ifdef __GNUC__ +#define CA_GCC_PRINTF_ATTR(a,b) __attribute__ ((format (printf, a, b))) +#else +/** If we're in GNU C, use some magic for detecting invalid format strings */ +#define CA_GCC_PRINTF_ATTR(a,b) +#endif + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define CA_GCC_SENTINEL __attribute__ ((sentinel)) +#else +/** Macro for usage of GCC's sentinel compilation warnings */ +#define CA_GCC_SENTINEL +#endif + +/** Properties for the media that is being played, about the event + * that caused the media to play, the window on which behalf this + * media is being played, the application that this window belongs to + * and finally properties for libcanberra specific usage. */ +#define CA_PROP_MEDIA_NAME "media.name" +#define CA_PROP_MEDIA_TITLE "media.title" +#define CA_PROP_MEDIA_ARTIST "media.artist" +#define CA_PROP_MEDIA_LANGUAGE "media.language" +#define CA_PROP_MEDIA_FILENAME "media.filename" +#define CA_PROP_MEDIA_ICON "media.icon" +#define CA_PROP_MEDIA_ICON_NAME "media.icon_name" +#define CA_PROP_MEDIA_ROLE "media.role" +#define CA_PROP_EVENT_ID "event.id" +#define CA_PROP_EVENT_MOUSE_X "event.mouse.x" +#define CA_PROP_EVENT_MOUSE_Y "event.mouse.y" +#define CA_PROP_EVENT_MOUSE_BUTTON "event.mouse.button" +#define CA_PROP_WINDOW_NAME "window.name" +#define CA_PROP_WINDOW_ID "window.id" +#define CA_PROP_WINDOW_ICON "window.icon" +#define CA_PROP_WINDOW_ICON_NAME "window.icon_name" +#define CA_PROP_WINDOW_X11_DISPLAY "window.x11.display" +#define CA_PROP_WINDOW_X11_XID "window.x11.xid" +#define CA_PROP_APPLICATION_NAME "application.name" +#define CA_PROP_APPLICATION_ID "application.id" +#define CA_PROP_APPLICATION_VERSION "application.version" +#define CA_PROP_APPLICATION_ICON "application.icon" +#define CA_PROP_APPLICATION_ICON_NAME "application.icon_name" +#define CA_PROP_APPLICATION_LANGUAGE "application.language" +#define CA_PROP_APPLICATION_PROCESS_ID "application.process.id" +#define CA_PROP_APPLICATION_PROCESS_BINARY "application.process.binary" +#define CA_PROP_APPLICATION_PROCESS_USER "application.process.user" +#define CA_PROP_APPLICATION_PROCESS_HOST "application.process.host" +#define CA_PROP_CANBERRA_CACHE_CONTROL "canberra.cache-control" /* permanent, volatile, never */ +#define CA_PROP_CANBERRA_VOLUME "canberra.volume" /* decibel */ +#define CA_PROP_CANBERRA_XDG_THEME_NAME "canberra.xdg-theme.name" /* XDG theme name */ +#define CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE "canberra.xdg-theme.output-profile" /* XDG theme profile */ + +/* Context object */ +typedef struct ca_context ca_context; + +/** Playback completion event callback. This callback will be called + * from a background thread. */ +typedef void ca_finish_callback_t(ca_context *c, uint32_t id, int error_code, void *userdata); + +/** Error codes */ +enum { + CA_SUCCESS = 0, + CA_ERROR_NOTSUPPORTED = -1, + CA_ERROR_INVALID = -2, + CA_ERROR_STATE = -3, + CA_ERROR_OOM = -4, + CA_ERROR_NODRIVER = -5, + CA_ERROR_SYSTEM = -6, + CA_ERROR_CORRUPT = -7, + CA_ERROR_TOOBIG = -8, + CA_ERROR_NOTFOUND = -9, + CA_ERROR_DESTROYED = -10, + CA_ERROR_CANCELED = -11, + CA_ERROR_NOTAVAILABLE = -12, + CA_ERROR_ACCESS = -13, + CA_ERROR_IO = -14, + _CA_ERROR_MAX = -15 +}; + +typedef struct ca_proplist ca_proplist; + +int ca_proplist_create(ca_proplist **c); +int ca_proplist_destroy(ca_proplist *c); +int ca_proplist_sets(ca_proplist *p, const char *key, const char *value); +int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) CA_GCC_PRINTF_ATTR(3,4); +int ca_proplist_set(ca_proplist *p, const char *key, const void *data, size_t nbytes); + +/** Create an (unconnected) context object */ +int ca_context_create(ca_context **c); + +int ca_context_set_driver(ca_context **c, const char *driver); + +int ca_context_change_device(ca_context **c, const char *device); + +/** Connect the context. This call is implicitly called if necessary. It + * is recommended to initialize the application.* properties before + * issuing this call */ +int ca_context_open(ca_context *c); + +/** Destroy a (connected or unconnected) cntext object. */ +int ca_context_destroy(ca_context *c); + +/** Write one or more string properties to the context + * object. Requires final NULL sentinel. Properties set like this will + * be attached to both the client object of the sound server and to + * all event sounds played or cached. */ +int ca_context_change_props(ca_context *c, ...) CA_GCC_SENTINEL; + +/** Write an arbitrary data property to the context object. */ +int ca_context_change_props_full(ca_context *c, ca_proplist *p); + +/** Play one event sound. id can be any numeric value which later can + * be used to cancel an event sound that is currently being + * played. You may use the same id twice or more times if you want to + * cancel multiple event sounds with a single ca_context_cancel() call + * at once. It is recommended to pass 0 for the id if the event sound + * shall never be canceled. If the requested sound is not cached in + * the server yet this call might result in the sample being uploaded + * temporarily or permanently. This function will only start playback + * in the background. It will not wait until playback completed. */ +int ca_context_play(ca_context *c, uint32_t id, ...) CA_GCC_SENTINEL; + +/** Play one event sound, and call the specified callback function + when completed. The callback will be called from a background + thread. Other arguments identical to ca_context_play(). */ +int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata); + +/** Upload the specified sample into the server and attach the + * specified properties to it. This function will only return after + * the sample upload was finished. */ +int ca_context_cache(ca_context *c, ...) CA_GCC_SENTINEL; + +/** Upload the specified sample into the server and attach the + * specified properties to it */ +int ca_context_cache_full(ca_context *c, ca_proplist *p); + +/** Cancel one or more event sounds that have been started via + * ca_context_play(). */ +int ca_context_cancel(ca_context *c, uint32_t id); + +/** Return a human readable error string */ +const char *ca_strerror(int code); + +#endif diff --git a/src/common.c b/src/common.c new file mode 100644 index 0000000..bf11450 --- /dev/null +++ b/src/common.c @@ -0,0 +1,380 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include + +#include "canberra.h" +#include "common.h" +#include "malloc.h" +#include "driver.h" +#include "proplist.h" + +int ca_context_create(ca_context **_c) { + ca_context *c; + int ret; + + ca_return_val_if_fail(_c, CA_ERROR_INVALID); + + if (!(c = ca_new0(ca_context, 1))) + return CA_ERROR_OOM; + + if (!(c->mutex = ca_mutex_new())) { + ca_free(c); + return CA_ERROR_OOM; + } + + if ((ret = ca_proplist_create(&c->props)) < 0) { + ca_mutex_free(c->mutex); + ca_free(c); + return ret; + } + + *_c = c; + return CA_SUCCESS; +} + +int ca_context_destroy(ca_context *c) { + int ret = CA_SUCCESS; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + /* There's no locking necessary here, because the application is + * broken anyway if it destructs this object in one thread and + * still is calling a method of it in another. */ + + if (c->opened) + ret = driver_destroy(c); + + if (c->props) + ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS); + + ca_mutex_free(c->mutex); + ca_free(c->driver); + ca_free(c->device); + ca_free(c); + + return ret; +} + +int ca_context_set_driver(ca_context *c, char *driver) { + char *n; + int ret; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_mutex_lock(c->mutex); + ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex); + + if (!(n = ca_strdup(driver))) { + ret = CA_ERROR_OOM; + goto fail; + } + + ca_free(c->driver); + c->driver = n; + + ret = CA_SUCCESS; + +fail: + ca_mutex_unlock(c->mutex); + + return ret; +} + +int ca_context_change_device(ca_context *c, char *device) { + char *n; + int ret; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_mutex_lock(c->mutex); + + if (!(n = ca_strdup(device))) { + ret = CA_ERROR_OOM; + goto fail; + } + + ret = c->opened ? driver_change_device(c, n) : CA_SUCCESS; + + if (ret == CA_SUCCESS) { + ca_free(c->device); + c->device = n; + } else + ca_free(n); + +fail: + ca_mutex_unlock(c->mutex); + + return ret; +} + +static int context_open_unlocked(ca_context *c) { + int ret; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + if (c->opened) + return CA_SUCCESS; + + if ((ret = driver_open(c)) == CA_SUCCESS) + c->opened = TRUE; + + return ret; +} + +int ca_context_open(ca_context *c) { + int ret; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_mutex_lock(c->mutex); + ca_return_val_if_fail_unlock(!c->opened, CA_ERROR_STATE, c->mutex); + + ret = context_open_unlocked(c); + + ca_mutex_unlock(c->mutex); + + return ret; +} + +static int ca_proplist_from_ap(ca_proplist **_p, va_list ap) { + int ret; + ca_proplist *p; + + ca_assert(_p); + + if ((ret = ca_proplist_create(&p)) < 0) + return ret; + + for (;;) { + const char *key, *value; + int ret; + + if (!(key = va_arg(ap, const char*))) + break; + + if (!(value = va_arg(ap, const char*))) { + ret = CA_ERROR_INVALID; + goto fail; + } + + if ((ret = ca_proplist_sets(p, key, value)) < 0) + goto fail; + } + + *_p = p; + + return CA_SUCCESS; + +fail: + ca_assert_se(ca_proplist_destroy(p) == CA_SUCCESS); + + return ret; +} + +int ca_context_change_props(ca_context *c, ...) { + va_list ap; + int ret; + ca_proplist *p = NULL; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + va_start(ap, c); + ret = ca_proplist_from_ap(&p, ap); + va_end(ap); + + if (ret < 0) + return ret; + + ret = ca_context_change_props_full(c, p); + + ca_assert_se(ca_proplist_destroy(p) == 0); + + return ret; +} + +int ca_context_change_props_full(ca_context *c, ca_proplist *p) { + int ret; + ca_proplist *merged; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(p, CA_ERROR_INVALID); + + ca_mutex_lock(c->mutex); + + if ((ret = ca_proplist_merge(&merged, c->props, p)) < 0) + goto finish; + + ret = c->opened ? driver_change_props(c, p, merged) : CA_SUCCESS; + + if (ret == CA_SUCCESS) { + ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS); + c->props = merged; + } else + ca_assert_se(ca_proplist_destroy(merged) == CA_SUCCESS); + +finish: + + ca_mutex_unlock(c->mutex); + + return ret; +} + +int ca_context_play(ca_context *c, uint32_t id, ...) { + int ret; + va_list ap; + ca_proplist *p = NULL; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + va_start(ap, id); + ret = ca_proplist_from_ap(&p, ap); + va_end(ap); + + if (ret < 0) + return ret; + + ret = ca_context_play_full(c, id, p, NULL, NULL); + + ca_assert_se(ca_proplist_destroy(p) == 0); + + return ret; +} + +int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata) { + int ret; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(p, CA_ERROR_INVALID); + ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID); + + ca_mutex_lock(c->mutex); + + ca_return_val_if_fail_unlock(ca_proplist_contains(p, CA_PROP_EVENT_ID) || + ca_proplist_contains(c->props, CA_PROP_EVENT_ID), CA_ERROR_INVALID, c->mutex); + + if ((ret = context_open_unlocked(c)) < 0) + goto finish; + + ca_assert(c->opened); + + ret = driver_play(c, id, p, cb, userdata); + +finish: + + ca_mutex_unlock(c->mutex); + + return ret; +} + +int ca_context_cancel(ca_context *c, uint32_t id) { + int ret; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_mutex_lock(c->mutex); + ca_return_val_if_fail_unlock(c->opened, CA_ERROR_STATE, c->mutex); + + ret = driver_cancel(c, id); + + ca_mutex_unlock(c->mutex); + + return ret; +} + +int ca_context_cache(ca_context *c, ...) { + int ret; + va_list ap; + ca_proplist *p = NULL; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + va_start(ap, c); + ret = ca_proplist_from_ap(&p, ap); + va_end(ap); + + if (ret < 0) + return ret; + + ret = ca_context_cache_full(c, p); + + ca_assert_se(ca_proplist_destroy(p) == 0); + + return ret; +} + +int ca_context_cache_full(ca_context *c, ca_proplist *p) { + int ret; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(p, CA_ERROR_INVALID); + + ca_mutex_lock(c->mutex); + + ca_return_val_if_fail_unlock(ca_proplist_contains(p, CA_PROP_EVENT_ID) || + ca_proplist_contains(c->props, CA_PROP_EVENT_ID), CA_ERROR_INVALID, c->mutex); + + if ((ret = context_open_unlocked(c)) < 0) + goto finish; + + ca_assert(c->opened); + + ret = driver_cache(c, p); + +finish: + + ca_mutex_unlock(c->mutex); + + return ret; +} + +/** Return a human readable error */ +const char *ca_strerror(int code) { + + const char * const error_table[-_CA_ERROR_MAX] = { + [-CA_SUCCESS] = "Success", + [-CA_ERROR_NOT_SUPPORTED] = "Operation not supported", + [-CA_ERROR_INVALID] = "Invalid argument", + [-CA_ERROR_STATE] = "Invalid state", + [-CA_ERROR_OOM] = "Out of memory", + [-CA_ERROR_NO_DRIVER] = "No such driver", + [-CA_ERROR_SYSTEM] = "System error" + }; + + ca_return_val_if_fail(code <= 0, NULL); + ca_return_val_if_fail(code > _CA_ERROR_MAX, NULL); + + return error_table[-code]; +} + +/* Not exported */ +int ca_parse_cache_control(ca_cache_control_t *control, const char *c) { + ca_return_val_if_fail(control, CA_ERROR_INVALID); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + if (streq(control, "never")) + *control = CA_CACHE_CONTROL_NEVER; + else if (streq(control, "permanent")) + *control = CA_CACHE_CONTROL_PERMANENT; + else if (streq(control, "volatile")) + *control = CA_CACHE_CONTROL_VOLATILE; + else + return CA_ERROR_INVALID; + + return CA_SUCCESS; +} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..13139b3 --- /dev/null +++ b/src/common.h @@ -0,0 +1,50 @@ +#ifndef foocacommonh +#define foocacommonh + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "canberra.h" +#include "macro.h" +#include "mutex.h" + +struct ca_context { + ca_bool_t opened; + ca_mutex *mutex; + + ca_proplist *props; + + char *driver; + char *device; + + void *private; +}; + +typedef enum ca_cache_control { + CA_CACHE_CONTROL_NEVER, + CA_CACHE_CONTROL_PERMANENT, + CA_CACHE_CONTROL_VOLATILE +} ca_cache_control_t; + +int ca_parse_cache_control(ca_cache_control_t *control, const char *c); + +#endif diff --git a/src/driver.h b/src/driver.h new file mode 100644 index 0000000..732a5d4 --- /dev/null +++ b/src/driver.h @@ -0,0 +1,38 @@ +#ifndef foocanberradriverhfoo +#define foocanberradriverhfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "canberra.h" + +int driver_open(ca_context *c); +int driver_destroy(ca_context *c); + +int driver_change_device(ca_context *c, char *device); +int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged); + +int driver_play(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata); +int driver_cancel(ca_context *c, uint32_t id); +int driver_cache(ca_context *c, ca_proplist *p); + +#endif diff --git a/src/llist.h b/src/llist.h new file mode 100644 index 0000000..e9122ff --- /dev/null +++ b/src/llist.h @@ -0,0 +1,108 @@ +#ifndef foocallistfoo +#define foocallistfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "macro.h" + +/* Some macros for maintaining doubly linked lists */ + +/* The head of the linked list. Use this in the structure that shall + * contain the head of the linked list */ +#define CA_LLIST_HEAD(t,name) \ + t *name + +/* The pointers in the linked list's items. Use this in the item structure */ +#define CA_LLIST_FIELDS(t) \ + t *next, *prev + +/* Initialize the list's head */ +#define CA_LLIST_HEAD_INIT(t,item) \ + do { \ + (item) = (t*) NULL; } \ + while(0) + +/* Initialize a list item */ +#define CA_LLIST_INIT(t,item) \ + do { \ + t *_item = (item); \ + ca_assert(_item); \ + _item->prev = _item->next = NULL; \ + } while(0) + +/* Prepend an item to the list */ +#define CA_LLIST_PREPEND(t,head,item) \ + do { \ + t **_head = &(head), *_item = (item); \ + ca_assert(_item); \ + if ((_item->next = *_head)) \ + _item->next->prev = _item; \ + _item->prev = NULL; \ + *_head = _item; \ + } while (0) + +/* Remove an item from the list */ +#define CA_LLIST_REMOVE(t,head,item) \ + do { \ + t **_head = &(head), *_item = (item); \ + ca_assert(_item); \ + if (_item->next) \ + _item->next->prev = _item->prev; \ + if (_item->prev) \ + _item->prev->next = _item->next; \ + else { \ + ca_assert(*_head == _item); \ + *_head = _item->next; \ + } \ + _item->next = _item->prev = NULL; \ + } while(0) + +/* Find the head of the list */ +#define CA_LLIST_FIND_HEAD(t,item,head) \ + do { \ + t **_head = (head), *_item = (item); \ + *_head = _item; \ + ca_assert(_head); \ + while ((*_head)->prev) \ + *_head = (*_head)->prev; \ + } while (0) + +/* Insert an item after another one (a = where, b = what) */ +#define CA_LLIST_INSERT_AFTER(t,head,a,b) \ + do { \ + t **_head = &(head), *_a = (a), *_b = (b); \ + ca_assert(_b); \ + if (!_a) { \ + if ((_b->next = *_head)) \ + _b->next->prev = _b; \ + _b->prev = NULL; \ + *_head = _b; \ + } else { \ + if ((_b->next = _a->next)) \ + _b->next->prev = _b; \ + _b->prev = _a; \ + _a->next = _b; \ + } \ + } while (0) + +#endif diff --git a/src/macro.h b/src/macro.h new file mode 100644 index 0000000..2f37bc1 --- /dev/null +++ b/src/macro.h @@ -0,0 +1,239 @@ +#ifndef foocanberramacrohfoo +#define foocanberramacrohfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include +#include + +#ifndef PACKAGE +#error "Please include config.h before including this file!" +#endif + +#ifdef __GNUC__ +#define CA_LIKELY(x) (__builtin_expect(!!(x),1)) +#define CA_UNLIKELY(x) (__builtin_expect((x),0)) +#else +#define CA_LIKELY(x) (x) +#define CA_UNLIKELY(x) (x) +#endif + +#ifdef __GNUC__ +#define CA_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#else +#define CA_PRETTY_FUNCTION "" +#endif + +#define ca_return_if_fail(expr) \ + do { \ + if (CA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ + return; \ + } \ + } while(FALSE) + +#define ca_return_val_if_fail(expr, val) \ + do { \ + if (CA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ + return (val); \ + } \ + } while(FALSE) + +#define ca_return_null_if_fail(expr) ca_return_val_if_fail(expr, NULL) + +#define ca_return_if_fail_unlock(expr, mutex) \ + do { \ + if (CA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ + ca_mutex_unlock(mutex); \ + return; \ + } \ + } while(FALSE) + +#define ca_return_val_if_fail_unlock(expr, val, mutex) \ + do { \ + if (CA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s().", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ + ca_mutex_unlock(mutex); \ + return (val); \ + } \ + } while(FALSE) + +#define ca_return_null_if_fail_unlock(expr, mutex) ca_return_val_if_fail_unlock(expr, NULL, mutex) + +/* An assert which guarantees side effects of x, i.e. is never + * optimized away */ +#define ca_assert_se(expr) \ + do { \ + if (CA_UNLIKELY(!(expr))) { \ + fprintf(stderr, "Assertion '%s' failed at %s:%u, function %s(). Aborting.", #expr , __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ + abort(); \ + } \ + } while (FALSE) + +/* An assert that may be optimized away by defining NDEBUG */ +#ifdef NDEBUG +#define ca_assert(expr) do {} while (FALSE) +#else +#define ca_assert(expr) ca_assert_se(expr) +#endif + +#define ca_assert_not_reached() \ + do { \ + fprintf(stderr, "Code should not be reached at %s:%u, function %s(). Aborting.", __FILE__, __LINE__, CA_PRETTY_FUNCTION); \ + abort(); \ + } while (FALSE) + +#define CA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) + +#ifdef __GNUC__ +#define CA_MAX(a,b) \ + __extension__ ({ typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) +#else +#define CA_MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef __GNUC__ +#define CA_MIN(a,b) \ + __extension__ ({ typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#else +#define CA_MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifdef __GNUC__ +#define CA_CLAMP(x, low, high) \ + __extension__ ({ typeof(x) _x = (x); \ + typeof(low) _low = (low); \ + typeof(high) _high = (high); \ + ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ + }) +#else +#define CA_CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#endif + +#ifndef FALSE +#define FALSE (0) +#endif + +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +#define CA_PTR_TO_UINT(p) ((unsigned int) (unsigned long) (p)) +#define CA_UINT_TO_PTR(u) ((void*) (unsigned long) (u)) + +#define CA_PTR_TO_UINT32(p) ((uint32_t) CA_PTR_TO_UINT(p)) +#define CA_UINT32_TO_PTR(u) CA_UINT_TO_PTR((uint32_t) u) + +#define CA_PTR_TO_INT(p) ((int) CA_PTR_TO_UINT(p)) +#define CA_INT_TO_PTR(u) CA_UINT_TO_PTR((int) u) + +#define CA_PTR_TO_INT32(p) ((int32_t) CA_PTR_TO_UINT(p)) +#define CA_INT32_TO_PTR(u) CA_UINT_TO_PTR((int32_t) u) + + + +static inline size_t ca_align(size_t l) { + return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*)); +} + +#define CA_ALIGN(x) (ca_align(x)) + +typedef void (*ca_free_cb_t)(void *); + +typedef int ca_bool_t; + +#ifdef HAVE_BYTESWAP_H +#include +#endif + +#ifdef HAVE_BYTESWAP_H +#define PA_INT16_SWAP(x) ((int16_t) bswap_16((uint16_t) x)) +#define PA_UINT16_SWAP(x) ((uint16_t) bswap_16((uint16_t) x)) +#define PA_INT32_SWAP(x) ((int32_t) bswap_32((uint32_t) x)) +#define PA_UINT32_SWAP(x) ((uint32_t) bswap_32((uint32_t) x)) +#else +#define PA_INT16_SWAP(x) ( (int16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define PA_UINT16_SWAP(x) ( (uint16_t) ( ((uint16_t) x >> 8) | ((uint16_t) x << 8) ) ) +#define PA_INT32_SWAP(x) ( (int32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) +#define PA_UINT32_SWAP(x) ( (uint32_t) ( ((uint32_t) x >> 24) | ((uint32_t) x << 24) | (((uint32_t) x & 0xFF00) << 8) | ((((uint32_t) x) >> 8) & 0xFF00) ) ) +#endif + +#ifdef WORDS_BIGENDIAN + #define PA_INT16_FROM_LE(x) PA_INT16_SWAP(x) + #define PA_INT16_FROM_BE(x) ((int16_t)(x)) + + #define PA_INT16_TO_LE(x) PA_INT16_SWAP(x) + #define PA_INT16_TO_BE(x) ((int16_t)(x)) + + #define PA_UINT16_FROM_LE(x) PA_UINT16_SWAP(x) + #define PA_UINT16_FROM_BE(x) ((uint16_t)(x)) + + #define PA_UINT16_TO_LE(x) PA_UINT16_SWAP(x) + #define PA_UINT16_TO_BE(x) ((uint16_t)(x)) + + #define PA_INT32_FROM_LE(x) PA_INT32_SWAP(x) + #define PA_INT32_FROM_BE(x) ((int32_t)(x)) + + #define PA_INT32_TO_LE(x) PA_INT32_SWAP(x) + #define PA_INT32_TO_BE(x) ((int32_t)(x)) + + #define PA_UINT32_FROM_LE(x) PA_UINT32_SWAP(x) + #define PA_UINT32_FROM_BE(x) ((uint32_t)(x)) + + #define PA_UINT32_TO_LE(x) PA_UINT32_SWAP(x) + #define PA_UINT32_TO_BE(x) ((uint32_t)(x)) +#else + #define PA_INT16_FROM_LE(x) ((int16_t)(x)) + #define PA_INT16_FROM_BE(x) PA_INT16_SWAP(x) + + #define PA_INT16_TO_LE(x) ((int16_t)(x)) + #define PA_INT16_TO_BE(x) PA_INT16_SWAP(x) + + #define PA_UINT16_FROM_LE(x) ((uint16_t)(x)) + #define PA_UINT16_FROM_BE(x) PA_UINT16_SWAP(x) + + #define PA_UINT16_TO_LE(x) ((uint16_t)(x)) + #define PA_UINT16_TO_BE(x) PA_UINT16_SWAP(x) + + #define PA_INT32_FROM_LE(x) ((int32_t)(x)) + #define PA_INT32_FROM_BE(x) PA_INT32_SWAP(x) + + #define PA_INT32_TO_LE(x) ((int32_t)(x)) + #define PA_INT32_TO_BE(x) PA_INT32_SWAP(x) + + #define PA_UINT32_FROM_LE(x) ((uint32_t)(x)) + #define PA_UINT32_FROM_BE(x) PA_UINT32_SWAP(x) + + #define PA_UINT32_TO_LE(x) ((uint32_t)(x)) + #define PA_UINT32_TO_BE(x) PA_UINT32_SWAP(x) +#endif + +#endif diff --git a/src/malloc.h b/src/malloc.h new file mode 100644 index 0000000..b720a12 --- /dev/null +++ b/src/malloc.h @@ -0,0 +1,41 @@ +#ifndef foosydneymallochfoo +#define foocanberramallochfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include +#include + +#define ca_malloc malloc +#define ca_free free +#define ca_malloc0(size) calloc(1, (size)) +#define ca_strdup strdup +#define ca_strndup strndup + +void* ca_memdup(const void* p, size_t size); + +#define ca_new(t, n) ((t*) ca_malloc(sizeof(t)*(n))) +#define ca_new0(t, n) ((t*) ca_malloc0(sizeof(t)*(n))) +#define ca_newdup(t, p, n) ((t*) ca_memdup(p, sizeof(t)*(n))) + +#endif diff --git a/src/mutex-posix.c b/src/mutex-posix.c new file mode 100644 index 0000000..f8e0f54 --- /dev/null +++ b/src/mutex-posix.c @@ -0,0 +1,80 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "mutex.h" +#include "malloc.h" + +struct ca_mutex { + pthread_mutex_t mutex; +}; + +ca_mutex* ca_mutex_new(void) { + ca_mutex *m; + + if (!(m = ca_new(ca_mutex, 1))) + return NULL; + + if (pthread_mutex_init(&m->mutex, NULL) < 0) { + ca_free(m); + return NULL; + } + + return m; +} + +void ca_mutex_free(ca_mutex *m) { + ca_assert(m); + + ca_assert_se(pthread_mutex_destroy(&m->mutex) == 0); + ca_free(m); +} + +void ca_mutex_lock(ca_mutex *m) { + ca_assert(m); + + ca_assert_se(pthread_mutex_lock(&m->mutex) == 0); +} + +ca_bool_t ca_mutex_try_lock(ca_mutex *m) { + int r; + ca_assert(m); + + if ((r = pthread_mutex_trylock(&m->mutex)) != 0) { + ca_assert(r == EBUSY); + return FALSE; + } + + return TRUE; +} + +void ca_mutex_unlock(ca_mutex *m) { + ca_assert(m); + + ca_assert_se(pthread_mutex_unlock(&m->mutex) == 0); +} diff --git a/src/mutex.h b/src/mutex.h new file mode 100644 index 0000000..31b75b3 --- /dev/null +++ b/src/mutex.h @@ -0,0 +1,37 @@ +#ifndef foocamutexhfoo +#define foocamutexhfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "macro.h" + +typedef struct ca_mutex ca_mutex; + +ca_mutex* ca_mutex_new(void); +void ca_mutex_free(ca_mutex *m); + +void ca_mutex_lock(ca_mutex *m); +ca_bool_t ca_mutex_try_lock(ca_mutex *m); +void ca_mutex_unlock(ca_mutex *m); + +#endif diff --git a/src/proplist.c b/src/proplist.c new file mode 100644 index 0000000..6c10595 --- /dev/null +++ b/src/proplist.c @@ -0,0 +1,317 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include + +#include "canberra.h" +#include "proplist.h" +#include "macro.h" +#include "malloc.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + +static unsigned calc_hash(const char *c) { + unsigned hash = 0; + + for (; *c; c++) + hash = 31 * hash + *c; + + return hash; +} + +int ca_proplist_create(ca_proplist **_p) { + ca_proplist *p; + ca_return_val_if_fail(_p, CA_ERROR_INVALID); + + if (!(p = ca_new0(ca_proplist, 1))) + return CA_ERROR_OOM; + + if (!(p->mutex = ca_mutex_new())) { + ca_free(p); + return CA_ERROR_OOM; + } + + *_p = p; + + return CA_SUCCESS; +} + +static int _unset(ca_proplist *p, const char *key) { + ca_prop *prop, *nprop; + unsigned i; + + ca_return_val_if_fail(p, CA_ERROR_INVALID); + ca_return_val_if_fail(key, CA_ERROR_INVALID); + + i = calc_hash(key) % N_HASHTABLE; + + nprop = NULL; + for (prop = p->prop_hashtable[i]; prop; nprop = prop, prop = prop->next_in_slot) + if (strcmp(prop->key, key) == 0) + break; + + if (prop) { + if (nprop) + nprop->next_in_slot = prop->next_in_slot; + else + p->prop_hashtable[i] = prop->next_in_slot; + + if (prop->prev_item) + prop->prev_item->next_item = prop->next_item; + else + p->first_item = prop->next_item; + + if (prop->next_item) + prop->next_item->prev_item = prop->prev_item; + + ca_free(prop); + } + + return CA_SUCCESS; +} + +int ca_proplist_sets(ca_proplist *p, const char *key, const char *value) { + ca_return_val_if_fail(p, CA_ERROR_INVALID); + ca_return_val_if_fail(key, CA_ERROR_INVALID); + ca_return_val_if_fail(!value, CA_ERROR_INVALID); + + return ca_proplist_set(p, key, value, sizeof(value)+1); +} + +int ca_proplist_setf(ca_proplist *p, const char *key, const char *format, ...) { + int ret; + char *k; + ca_prop *prop; + int size = 100; + unsigned h; + + ca_return_val_if_fail(p, CA_ERROR_INVALID); + ca_return_val_if_fail(key, CA_ERROR_INVALID); + ca_return_val_if_fail(format, CA_ERROR_INVALID); + + if (!(k = ca_strdup(key))) + return CA_ERROR_OOM; + + for (;;) { + va_list ap; + int r; + + if (!(prop = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + size))) { + ca_free(k); + return CA_ERROR_OOM; + } + + + va_start(ap, format); + r = vsnprintf(CA_PROP_DATA(prop), size, format, ap); + va_end(ap); + + ((char*) CA_PROP_DATA(prop))[size-1] = 0; + + if (r > -1 && r < size) { + prop->nbytes = r+1; + break; + } + + if (r > -1) /* glibc 2.1 */ + size = r+1; + else /* glibc 2.0 */ + size *= 2; + + ca_free(prop); + } + + prop->key = k; + + ca_mutex_lock(p->mutex); + + if ((ret = _unset(p, key)) < 0) { + ca_free(prop); + ca_free(k); + goto finish; + } + + h = calc_hash(key) % N_HASHTABLE; + + prop->next_in_slot = p->prop_hashtable[h]; + p->prop_hashtable[h] = prop; + + prop->prev_item = NULL; + prop->next_item = p->first_item; + p->first_item = prop; + +finish: + + ca_mutex_unlock(p->mutex); + + return ret; +} + +int ca_proplist_set(ca_proplist *p, const char *key, const void *data, size_t nbytes) { + int ret; + char *k; + ca_prop *prop; + unsigned h; + + ca_return_val_if_fail(p, CA_ERROR_INVALID); + ca_return_val_if_fail(key, CA_ERROR_INVALID); + ca_return_val_if_fail(!nbytes || data, CA_ERROR_INVALID); + + if (!(k = ca_strdup(key))) + return CA_ERROR_OOM; + + if (!(prop = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + nbytes))) { + ca_free(k); + return CA_ERROR_OOM; + } + + prop->key = k; + prop->nbytes = nbytes; + memcpy(CA_PROP_DATA(prop), data, nbytes); + + ca_mutex_lock(p->mutex); + + if ((ret = _unset(p, key)) < 0) { + ca_free(prop); + ca_free(k); + goto finish; + } + + h = calc_hash(key) % N_HASHTABLE; + + prop->next_in_slot = p->prop_hashtable[h]; + p->prop_hashtable[h] = prop; + + prop->prev_item = NULL; + prop->next_item = p->first_item; + p->first_item = prop; + +finish: + + ca_mutex_unlock(p->mutex); + + return ret; +} + +/* Not exported, not self-locking */ +ca_prop* ca_proplist_get_unlocked(ca_proplist *p, const char *key) { + ca_prop *prop; + unsigned i; + + ca_return_val_if_fail(p, NULL); + ca_return_val_if_fail(key, NULL); + + i = calc_hash(key) % N_HASHTABLE; + + for (prop = p->prop_hashtable[i]; prop; prop = prop->next_in_slot) + if (strcmp(prop->key, key) == 0) + return prop; + + return NULL; +} + +/* Not exported, not self-locking */ +const char* ca_proplist_gets_unlocked(ca_proplist *p, const char *key) { + ca_prop *prop; + + ca_return_val_if_fail(p, NULL); + ca_return_val_if_fail(key, NULL); + + if (!(prop = ca_proplist_get_unlocked(p, key))) + return NULL; + + if (!memchr(CA_PROP_DATA(prop), 0, prop->nbytes)) + return NULL; + + return CA_PROP_DATA(prop); +} + +int ca_proplist_destroy(ca_proplist *p) { + ca_prop *prop, *nprop; + + ca_return_val_if_fail(p, CA_ERROR_INVALID); + + for (prop = p->first_item; prop; prop = nprop) { + nprop = prop->next_item; + ca_free(prop); + } + + ca_mutex_free(p->mutex); + + ca_free(p); + + return CA_SUCCESS; +} + +static int merge_into(ca_proplist *a, ca_proplist *b) { + int ret = CA_SUCCESS; + ca_prop *prop; + + ca_return_val_if_fail(a, CA_ERROR_INVALID); + ca_return_val_if_fail(b, CA_ERROR_INVALID); + + ca_mutex_lock(b->mutex); + + for (prop = b->first_item; prop; prop = prop->next_item) + if ((ret = ca_proplist_set(a, prop->key, CA_PROP_DATA(prop), prop->nbytes)) < 0) + break; + + ca_mutex_unlock(b->mutex); + + return ret; +} + +int ca_proplist_merge(ca_proplist **_a, ca_proplist *b, ca_proplist *c) { + ca_proplist *a; + int ret; + + ca_return_val_if_fail(_a, CA_ERROR_INVALID); + ca_return_val_if_fail(b, CA_ERROR_INVALID); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + if ((ret = ca_proplist_create(&a)) < 0) + return ret; + + if ((ret = merge_into(a, b)) < 0 || + (ret = merge_into(a, c)) < 0) { + ca_proplist_destroy(a); + return ret; + } + + *_a = a; + return CA_SUCCESS; +} + +ca_bool_t ca_proplist_contains(ca_proplist *p, const char *key) { + ca_bool_t b; + + ca_return_val_if_fail(p, CA_ERROR_INVALID); + ca_return_val_if_fail(key, CA_ERROR_INVALID); + + ca_mutex_lock(p->mutex); + b = !!ca_proplist_get_unlocked(p, key); + ca_mutex_unlock(p->mutex); + + return b; +} diff --git a/src/proplist.h b/src/proplist.h new file mode 100644 index 0000000..4cb511e --- /dev/null +++ b/src/proplist.h @@ -0,0 +1,53 @@ +#ifndef foocaproplisthfoo +#define foocaproplisthfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "canberra.h" +#include "mutex.h" + +#define N_HASHTABLE 31 + +typedef struct ca_prop { + char *key; + size_t nbytes; + struct ca_prop *next_in_slot, *next_item, *prev_item; +} ca_prop; + +#define CA_PROP_DATA(p) ((void*) ((char*) (p) + CA_ALIGN(sizeof(ca_prop)))) + +struct ca_proplist { + ca_mutex *mutex; + + ca_prop *prop_hashtable[N_HASHTABLE]; + ca_prop *first_item; +}; + +int ca_proplist_merge(ca_proplist **_a, ca_proplist *b, ca_proplist *c); +ca_bool_t ca_proplist_contains(ca_proplist *p, const char *key); + +/* Both of the following two functions are not locked! Need manual locking! */ +ca_prop* ca_context_get_unlocked(ca_proplist *c, const char *key); +const char* ca_context_gets_unlocked(ca_proplist *c, const char *key); + +#endif diff --git a/src/pulse.c b/src/pulse.c new file mode 100644 index 0000000..c69668a --- /dev/null +++ b/src/pulse.c @@ -0,0 +1,903 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +/* The locking order needs to be strictly followed! First take the + * mainloop mutex, only then take outstanding_mutex if you need both! + * Not the other way round, beacause we might then enter a + * deadlock! */ + +#include +#include +#include + +#include "canberra.h" +#include "common." +#include "driver.h" + +enum outstanding_type { + OUTSTANDING_SAMPLE, + OUTSTANDING_STREAM, + OUTSTANDING_UPLOAD +}; + +struct outstanding { + PA_LLIST_FIELDS(struct outstanding); + enum outstanding_type type; + ca_context *context; + uint32_t id; + uint32_t sink_input; + ca_stream *stream; + ca_finish_callback_t callback; + void *userdata; + ca_sound_file *file; + int error; + ca_bool_t clean_up; +}; + +struct private { + pa_threaded_mainloop *mainloop; + pa_context *context; + ca_theme_data *theme; + ca_bool_t subscribed; + + ca_mutex *outstanding_mutex; + PA_LLIST_HEAD(struct outstanding, outstanding); +}; + +#define PRIVATE(c) ((struct private *) ((c)->private) + +static void outstanding_free(struct outstanding *o) { + ca_assert(o); + + if (o->file) + ca_sound_file_free(o->file); + + if (o->stream) { + pa_stream_disconnect(o->stream); + pa_stream_unref(o->stream); + } + + ca_free(o); +} + +static int convert_proplist(pa_proplist **_l, pa_proplist *c) { + pa_proplist *l; + ca_prop *i; + + ca_return_val_if_fail(_l, CA_ERROR_INVALID); + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + if (!(l = pa_proplist_new())) + return CA_ERROR_OOM; + + ca_mutex_lock(c->mutex); + + for (i = c->first_item; i; i = i->next_item) + if (pa_proplist_put(l, i->key, CA_PROP_DATA(i), i->nbytes) < 0) { + ca_mutex_unlock(c->mutex); + pa_proplist_free(l); + return PA_ERROR_INVALID; + } + + ca_mutex_unlock(c->mutex); + + *_l = l; + + return PA_SUCCESS; +} + +static pa_proplist *strip_canberra_data(pa_proplist *l) { + const char *key; + void *state = NULL; + ca_assert(l); + + while ((key = pa_proplist_iterate(l, &state))) + if (strncmp(key, "canberra.", 12) == 0) + pa_proplist_remove(l, key); + + return l; +} + +static int translate_error(int error) { + static const int table[PA_ERR_MAX] = { + [PA_OK] = CA_SUCCESS, + [PA_ERR_ACCESS] = CA_ERROR_ACCESS, + [PA_ERR_COMMAND] = CA_ERROR_IO, + [PA_ERR_INVALID] = CA_ERROR_INVALID, + [PA_ERR_EXIST] = CA_ERROR_IO, + [PA_ERR_NOENTITY] = CA_ERROR_NOTFOUND, + [PA_ERR_CONNECTIONREFUSED] = CA_ERROR_NOTAVAILABLE, + [PA_ERR_PROTOCOL] = CA_ERROR_IO, + [PA_ERR_TIMEOUT] = CA_ERROR_IO, + [PA_ERR_AUTHKEY] = CA_ERROR_ACCESS, + [PA_ERR_INTERNAL] = CA_ERROR_IO, + [PA_ERR_CONNECTIONTERMINATED] = CA_ERROR_IO, + [PA_ERR_KILLED] = CA_ERROR_DESTROYED, + [PA_ERR_INVALIDSERVER] = CA_ERROR_INVALID, + [PA_ERR_MODINITFAILED] = CA_ERROR_NODRIVER, + [PA_ERR_BADSTATE] = CA_ERROR_STATE, + [PA_ERR_NODATA] = CA_ERROR_IO, + [PA_ERR_VERSION] = CA_ERROR_NOTSUPPORTED, + [PA_ERR_TOOLARGE] = CA_ERROR_TOOBIG + }; + + ca_assert(error >= 0); + + if (error >= PA_ERR_MAX) + return CA_ERROR_IO; + + return table[error]; +} + +static void context_state_cb(pa_context *pc, void *userdata) { + ca_context *c = userdata; + pa_context_state_t state; + + ca_assert(pc); + ca_assert(c); + + state = pa_context_get_state(pc); + + if (state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED) { + struct outstanding *out; + int ret; + + ret = translate_error(pa_context_errno(pc)); + + ca_mutex_lock(c->outstanding_mutex); + + while ((out = c->outstanding)) { + + PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); + ca_mutex_unlock(c->outstanding_mutex); + + if (out->callback) + out->callback(c, out->id, ret, out->userdata); + outstanding_free(c->outstanding); + + ca_mutex_lock(c->outstanding_mutex); + } + + ca_mutex_unlock(c->outstanding_mutex); + } + + pa_threaded_mainloop_signal(c->mainloop, FALSE); +} + +static void context_subscribe_cb(pa_context *pc, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { + struct outstanding *out, *n; + PA_LLIST_HEAD(struct outstanding, l); + ca_context *c = userdata; + + ca_assert(pc); + ca_assert(c); + + if (t != PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_REMOVE) + return; + + PA_LLIST_HEAD_INIT(struct outstanding, l); + + ca_mutex_lock(c->outstanding_mutex); + + for (out = c->outstanding; out; out = n) { + n = out->next; + + if (out->type != OUTSTANDING_SAMPLE || out->sink_input != idx) + continue; + + PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); + PA_LLIST_PREPEND(struct outstanding, l, out); + } + + ca_mutex_unlock(c->outstanding_mutex); + + while (l) { + out = l; + + PA_LLIST_REMOVE(struct outstanding, l, out); + + if (out->callback) + out->callback(c, out->id, CA_SUCCESS, out->userdata); + + outstanding_free(out); + } +} + +int driver_open(ca_context *c) { + pa_proplist *l; + struct private *p; + + ca_return_val_if_fail(c, PA_ERROR_INVALID); + ca_return_val_if_fail(!c->driver || streq(c->driver, "pulse"), PA_ERROR_NO_DRIVER); + ca_return_val_if_fail(!PRIVATE(c), PA_ERROR_STATE); + + if (!(p = PRIVATE(c) = ca_new0(struct private, 1))) + return PA_ERROR_OOM; + + if (!(p->mainloop = pa_threaded_mainloop_new())) { + driver_destroy(c); + return PA_ERROR_OOM; + } + + if ((ret = convert_proplist(&l, c->proplist))) { + driver_destroy(c); + return ret; + } + + if (!(p->context = pa_context_new_with_proplist(pa_threaded_mainloop_get_api(p->mainloop), l))) { + pa_proplist_free(l); + driver_destroy(c); + return PA_ERROR_OOM; + } + + pa_proplist_free(l); + + pa_context_set_state_callback(p->context, context_state_cb, c); + pa_context_set_subscribe_callback(p->context, context_subscribe_cb, c); + + if (pa_context_connect(p->context, NULL, 0, NULL) < 0) { + int ret = translate_error(pa_context_errno(p->context)); + driver_destroy(c); + return ret; + } + + pa_threaded_mainloop_lock(p->mainloop); + + if (pa_threaded_mainloop_start(p->mainloop) < 0) { + pa_threaded_mainloop_unlock(p->mainloop); + driver_destroy(c); + return PA_ERROR_INTERNAL; + } + + pa_threaded_mainloop_wait(p->mainloop); + + if (pa_context_get_state(p->context) != PA_CONTEXT_READY) { + int ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + driver_destroy(c); + return ret; + } + + pa_threaded_mainloop_unlock(p->mainloop); + + return CA_SUCCESS; +} + +int driver_destroy(ca_context *c) { + ca_return_val_if_fail(c, PA_ERROR_INVALID); + ca_return_val_if_fail(c->private, PA_ERROR_STATE); + + p = PRIVATE(c); + + if (p->mainloop) + pa_threaded_mainloop_stop(p->mainloop); + + if (p->context) { + pa_context_disconnect(p->context); + pa_context_unref(p->context); + } + + if (p->mainloop) + pa_threaded_mainloop_free(p->mainloop); + + if (p->theme) + ca_theme_data_free(p->theme); + + while (p->outstanding) { + struct outstanding *out = p->outstanding; + PA_LLIST_REMOVE(struct outstanding, p->outstanding, out); + + if (out->callback) + out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata); + + outstanding_free(out); + } + + ca_free(p); +} + +int driver_change_device(ca_context *c, char *device) { + ca_return_val_if_fail(c, PA_ERROR_INVALID); + + /* We're happy with any device change. We might however add code + * here eventually to move all currently played back event sounds + * to the new device. */ + + return CA_SUCCESS; +} + +int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) { + struct private *p; + pa_operation *o; + pa_proplist *l; + int ret = CA_SUCCESS; + + ca_return_val_if_fail(c, PA_ERROR_INVALID); + ca_return_val_if_fail(changed, PA_ERROR_INVALID); + ca_return_val_if_fail(merged, PA_ERROR_INVALID); + ca_return_val_if_fail(c->private, PA_ERROR_STATE); + + p = PRIVATE(c); + + ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); + ca_return_val_if_fail(p->context, PA_ERROR_STATE); + + if ((ret = convert_proplist(&l, changed))) + return ret; + + strip_canberra_data(l); + + pa_threaded_mainloop_lock(p->mainloop); + + /* We start these asynchronously and don't care about the return + * value */ + + if (!(o = pa_context_proplist_update(p->context, PA_UPDATE_REPLACE, l, NULL, NULL))) + ret = translate_error(pa_context_errno(p->context)); + else + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(p->mainloop); + + pa_proplist_free(l); + + return ret; +} + +static int subscribe(ca_context *c) { + struct private *p; + pa_operation *o; + + ca_return_val_if_fail(c, PA_ERROR_INVALID); + ca_return_val_if_fail(c->private, PA_ERROR_STATE); + p = PRIVATE(c); + + ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); + ca_return_val_if_fail(p->context, PA_ERROR_STATE); + ca_return_val_if_fail(!p->subscribed, PA_SUCCESS); + + pa_threaded_mainloop_lock(p->mainloop); + + /* We start these asynchronously and don't care about the return + * value */ + + if (!(o = pa_context_subscribe(p->context, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL))) + ret = translate_error(pa_context_errno(p->context)); + else + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(p->mainloop); + + p->subscribed = TRUE; + + return ret; +} + +static void play_sample_cb(pa_context *c, uint32_t idx, void *userdata) { + struct outstanding *out = userdata; + + ca_assert(c); + ca_assert(out); + + if (idx != PA_INVALID_INDEX) { + out->error = CA_SUCCESS; + out->sink_input = idx; + } else + out->error = translate_error(pa_context_errno(c)); +} + +static void stream_state_cb(pa_stream *s, void *userdata) { + struct private *p; + struct outstanding *out = userdata; + + ca_assert(s); + ca_assert(out); + + p = PRIVATE(out->context); + + if (out->clean_up) { + pa_stream_state_t state; + + state = pa_stream_get_state(s); + + if (state == PA_STREAM_FAILED || state == PA_STREAM_TERMINATED) { + int err; + + ca_mutex_lock(p->context->outstanding_mutex); + PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); + ca_mutex_unlock(p->context->outstanding_mutex); + + err = state == PA_STREAM_FAILED ? translate_error(pa_context_errno(pa_stream_get_context(s))) + + if (out->callback) + out->callback(c, out->id, ret, out->userdata); + + outstanding_free(c->outstanding); + } + } + + pa_threaded_mainloop_signal(c->mainloop, TRUE); +} + +static void stream_drain_cb(pa_stream *s, int success, void *userdata) { + struct private *p; + struct outstanding *out = userdata; + + ca_assert(s); + ca_assert(out); + + p = PRIVATE(out->context); + + ca_assert(out->type = OUTSTANDING_STREAM); + ca_assert(out->clean_up); + + ca_mutex_lock(p->context->outstanding_mutex); + PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); + ca_mutex_unlock(p->context->outstanding_mutex); + + if (out->callback) { + int err; + + err = success ? CA_SUCCESS : translate_error(pa_context_errno(p->context)); + out->callback(out->context, out->id, err, out->userdata); + } + + outstanding_free(out); +} + +static void stream_write_cb(pa_stream *s, size_t bytes, void *userdata) { + struct outstanding *out = userdata; + struct private *p; + void *data; + int ret; + + ca_assert(s); + ca_assert(bytes > 0); + ca_assert(out); + + p = PRIVATE(out->context); + + if (!(data = ca_malloc(bytes))) { + ret = CA_ERROR_OOM + goto finish; + } + + if ((ret = ca_sound_file_read_arbitrary(out->file, data, &bytes)) < 0) + goto finish; + + if (bytes > 0) { + + if ((ret = pa_stream_writesp, data, bytes)) < 0) { + ret = translate_error(ret); + goto finish; + } + + } else { + /* We reached EOF */ + + if (out->type == OUTSTANDING_UPLOAD) { + + if (pa_stream_finish_upload(s) < 0) { + ret = translate_error(pa_context_errno(p->context)); + goto finish; + } + + /* Let's just signal driver_cache() which has been waiting for us */ + pa_threaded_mainloop_signal(c->mainloop, TRUE); + + } else { + ca_assert(out->type = OUTSTANDING_STREAM); + + if (!(o = pa_stream_drain(p->context, stream_drain_cb, out))) { + ret = translate_error(pa_context_errno(p->context)); + goto fail; + } + + pa_operation_unref(o); + } + } + + ca_free(data); + + return; + +finish: + + ca_free(data); + + if (out->clean_up) { + ca_mutex_lock(p->context->outstanding_mutex); + PA_LLIST_REMOVE(struct outstanding, c->outstanding, out); + ca_mutex_unlock(p->context->outstanding_mutex); + + if (out->callback) + out->callback(out->context, out->id, ret, out->userdata); + + outstanding_free(out); + } else { + pa_stream_disconnect(p); + pa_threaded_mainloop_signal(c->mainloop, TRUE); + out->error = ret; + } +} + +static const pa_sample_format_t sample_type_table[] = { + [CA_SAMPLE_S16NE] = PA_SAMPLE_S16NE, + [CA_SAMPLE_S16RE] = PA_SAMPLE_S16RE, + [CA_SAMPLE_U8] = PA_SAMPLE_U8 +}; + +int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) { + struct private *p; + pa_proplist *l = NULL; + const char *n, *vol, *ct; + char *name = NULL; + int err = 0; + pa_volume_t v = PA_VOLUME_NORM; + pa_sample_spec ss; + ca_cache_control_t cache_control = CA_CACHE_CONTROL_NEVER; + struct outstanding *out = NULL; + int try = 3; + + ca_return_val_if_fail(c, PA_ERROR_INVALID); + ca_return_val_if_fail(proplist, PA_ERROR_INVALID); + ca_return_val_if_fail(!userdata || cb, PA_ERROR_INVALID); + ca_return_val_if_fail(c->private, PA_ERROR_STATE); + + p = PRIVATE(c); + + ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); + ca_return_val_if_fail(p->context, PA_ERROR_STATE); + + if (!(out = pa_xnew0(struct outstanding, 1))) { + ret = PA_ERROR_OOM; + goto finish; + } + + out->type = OUTSTANDING_SAMPLE; + out->context = c; + out->sink_input = PA_INVALID_INDEX; + out->id = id; + out->callback = cb; + out->userdata = userdata; + + if ((ret = convert_proplist(&l, proplist))) + goto finish; + + if (!(n = pa_proplist_gets(l, CA_PROP_EVENT_ID))) { + ret = PA_ERROR_INVALID; + goto finish; + } + + if (!(name = ca_strdup(n))) { + ret = PA_ERROR_OOM; + goto finish; + } + + if ((vol = pa_proplist_gets(l, CA_PROP_CANBERRA_VOLUME))) { + char *e = NULL; + double dvol; + + errno = 0; + dvol = strtod(vol, &e); + if (errno != 0 || !e || *e) { + ret = PA_ERROR_INVALID; + goto finish; + } + + v = pa_sw_volume_from_dB(dvol); + } + + if ((ct = pa_proplist_gets(l, CA_PROP_CANBERRA_CACHE_CONTROL))) + if ((ret = ca_parse_cache_control(&cache_control, ct)) < 0) { + ret = PA_ERROR_INVALID; + goto finish; + } + + strip_canberra_data(l); + + if (cb) + if ((ret = subscribe(c)) < 0) + goto finish; + + for (;;) { + pa_threaded_mainloop_lock(p->mainloop); + + /* Let's try to play the sample */ + if (!(o = pa_context_play_sample_with_proplist(p->context, name, NULL, v, l, id, play_sample_cb, out))) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto finish; + } + + while (pa_operation_get_state(o) != OPERATION_DONE) + pa_threaded_mainloop_wait(m); + + pa_operation_unref(o); + + pa_threaded_mainloop_unlock(p->mainloop); + + /* Did we manage to play the sample or did some other error occur? */ + if (out->error != CA_ERROR_NOT_FOUND) + goto finish; + + /* Hmm, we need to play it directly */ + if (cache_control == CA_CACHE_CONTROL_NEVER) + break; + + /* Don't loop forever */ + if (--try <= 0) + break; + + /* Let's upload the sample and retry playing */ + if ((ret = driver_cache(c, proplist)) < 0) + goto fail; + } + + out->type = OUTSTANDING_STREAM; + + /* Let's stream the sample directly */ + if ((ret = ca_lookup_sound(&out->file, &p->theme, proplist)) < 0) + goto fail; + + ss.channels = sample_type_table[ca_sound_file_get_sample_type(f)]; + ss.channels = ca_sound_file_get_nchannels(f); + ss.rate = ca_sound_file_get_rate(f); + + pa_threaded_mainloop_lock(p->mainloop); + + if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l))) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + pa_stream_set_userdata(p->stream, out); + pa_stream_set_state_callback(p->stream, stream_state_cb, out); + pa_stream_set_write_callback(p->stream, stream_request_cb, out); + + if (pa_stream_connect_playback(s, NULL, NULL, 0, NULL, NULL) < 0) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + for (;;) { + pa_stream_state state = pa_stream_get_state(s); + + /* Stream sucessfully created */ + if (state == PA_STREAM_READY) + break; + + /* Check for failure */ + if (state == PA_STREAM_FAILED) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + if (state == PA_STREAM_TERMINATED) { + ret = out->error; + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + pa_threaded_mainloop_wait(p->mainloop); + } + + if ((out->sink_input = pa_stream_get_index(s)) == PA_INVALID_INDEX) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + pa_threaded_mainloop_unlock(p->mainloop); + + ret = CA_SUCCESS; + +finish: + + /* We keep the outstanding struct around if we need clean up later to */ + if (ret == CA_SUCCESS && (out->type == OUTSTANDING_STREAM || cb)) { + out->clean_up = TRUE; + + pa_mutex_lock(p->outstanding_mutex); + PA_LLIST_PREPEND(struct outstanding, p->outstanding, out); + pa_mutex_unlock(p->outstanding_mutex); + } else + outstanding_free(out); + + if (l) + pa_proplist_free(l); + + ca_free(name); + + return ret; +} + +int driver_cancel(ca_context *c, uint32_t id) { + struct private *p; + pa_operation *o; + int ret = CA_SUCCESS; + struct outstanding *out, *n; + + ca_return_val_if_fail(c, PA_ERROR_INVALID); + ca_return_val_if_fail(c->private, PA_ERROR_STATE); + + p = PRIVATE(c); + + ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); + ca_return_val_if_fail(p->context, PA_ERROR_STATE); + + pa_threaded_mainloop_lock(p->mainloop); + + ca_mutex_lock(p->outstanding_mutex); + + /* We start these asynchronously and don't care about the return + * value */ + + for (out = p->outstanding; out; out = n) { + int ret2; + n = out->next; + + if (out->type == OUTSTANDING_UPLOAD || + out->id != id || + out->sink_input == PA_INVALID_INDEX) + continue; + + if (!(o = pa_context_kill_sink_input(p->context, out->sink_input, NULL, NULL))) + ret2 = translate_error(pa_context_errno(p->context)); + else + pa_operation_unref(o); + + /* We make sure here to kill all streams identified by the id + * here. However, we will return only the first error we + * encounter */ + + if (ret2 && ret == CA_SUCCESS) + ret = ret2; + + if (out->callback) + out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata); + + CA_LLIST_REMOVE(struct outstanding, p->outstanding, out); + outstanding_free(out); + } + + ca_mutex_unlock(p->outstanding_mutex); + + pa_threaded_mainloop_unlock(p->mainloop); + + return ret; +} + +int driver_cache(ca_context *c, ca_proplist *proplist) { + struct private *p; + pa_proplist *l = NULL; + const char *n, *ct; + char *name = NULL; + pa_sample_spec ss; + ca_cache_control_t cache_control = CA_CACHE_CONTROL_NEVER; + struct outstanding out; + + ca_return_val_if_fail(c, PA_ERROR_INVALID); + ca_return_val_if_fail(proplist, PA_ERROR_INVALID); + ca_return_val_if_fail(c->private, PA_ERROR_STATE); + + p = PRIVATE(c); + + ca_return_val_if_fail(p->mainloop, PA_ERROR_STATE); + ca_return_val_if_fail(p->context, PA_ERROR_STATE); + + if (!(out = ca_new0(struct outstanding, 1))) { + ret = CA_ERROR_OOM; + goto finish; + } + + out->type = OUTSTANDING_UPLOAD; + out->context = c; + out->sink_input = PA_INVALID_INDEX; + + if ((ret = convert_proplist(&l, proplist))) + goto finish; + + if (!(n = pa_proplist_gets(l, CA_PROP_EVENT_ID))) { + ret = PA_ERROR_INVALID; + goto finish; + } + + if (!(name = ca_strdup(n))) { + ret = PA_ERROR_OOM; + goto finish; + } + + if ((ct = pa_proplist_gets(l, CA_PROP_CANBERRA_CACHE_CONTROL))) + if ((ret = ca_parse_cache_control(&cache_control, ct)) < 0) { + ret = PA_ERROR_INVALID; + goto finish; + } + + if (ct == CA_CACHE_CONTROL_NEVER) { + ret = PA_ERROR_INVALID; + goto finish; + } + + strip_canberra_data(l); + + /* Let's stream the sample directly */ + if ((ret = ca_lookup_sound(&out->file, &p->theme, proplist)) < 0) + goto fail; + + ss.channels = sample_type_table[ca_sound_file_get_sample_type(out->file)]; + ss.channels = ca_sound_file_get_nchannels(out->file); + ss.rate = ca_sound_file_get_rate(out->file); + + pa_threaded_mainloop_lock(p->mainloop); + + if (!(out->stream = pa_stream_new_with_proplist(p->context, name, &ss, NULL, l))) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + pa_stream_set_userdata(out->stream, out); + pa_stream_set_state_callback(out->stream, stream_state_cb, out); + pa_stream_set_write_callback(out->stream, stream_request_cb, out); + + if (pa_stream_connect_upload(s, ca_sound_file_get_size(out->file)) < 0) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + for (;;) { + pa_stream_state state = pa_stream_get_state(s); + + /* Stream sucessfully created and uploaded */ + if (state == PA_STREAM_TERMINATED) + break; + + /* Check for failure */ + if (state == PA_STREAM_FAILED) { + ret = translate_error(pa_context_errno(p->context)); + pa_threaded_mainloop_unlock(p->mainloop); + goto fail; + } + + pa_threaded_mainloop_wait(p->mainloop); + } + + pa_threaded_mainloop_unlock(p->mainloop); + + ret = CA_SUCCESS; + +finish: + + outstanding_free(out); + + if (l) + pa_proplist_free(l); + + ca_free(name); + + return ret; +} diff --git a/src/read-sound-file.c b/src/read-sound-file.c new file mode 100644 index 0000000..8670445 --- /dev/null +++ b/src/read-sound-file.c @@ -0,0 +1,175 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "read-sound-file.h" + +struct ca_sound_file { + ca_wav *wav; + ca_vorbis *vorbis; + char *filename; + + unsigned nchannels; + unsigned rate; + ca_sample_type_t type; +}; + +int ca_sound_file_open(ca_sound_file *_f, const char *fn) { + FILE *file; + ca_sound_file *f; + int ret; + + ca_return_val_if_fail(_f, PA_ERROR_INVALID); + ca_return_val_if_fail(fn, PA_ERROR_INVALID); + + if (!(f = ca_new0(ca_sound_file, 1))) + return CA_ERROR_OOM; + + if (!(f->filename = ca_strdup(fn))) { + ret = CA_ERROR_OOM; + goto fail; + } + + if (!(file = fopen(fn, "r"))) { + ret = errno == ENOENT ? CA_ERROR_NOTFOUND : CA_ERROR_SYSTEM; + goto fail; + } + + if ((ret = ca_wav_open(&f->wav, file)) == CA_SUCCESS) { + f->nchannels = ca_wav_get_nchannels(f->wav); + f->rate = ca_wav_get_rate(f->wav); + f->type = ca_wav_get_sample_type(f->wav); + *f = f; + return CA_SUCCESS; + } + + if (ret == CA_ERROR_CORRUPT) { + + if (fseek(file, 0, SEEK_SET) < 0) { + ret = CA_ERROR_SYSTEM; + goto fail; + } + + if ((ret = ca_vorbis_open(&f->vorbis, file)) == CA_SUCCESS) { + f->nchannels = ca_vorbis_get_nchannels(f->vorbis); + f->rate = ca_vorbis_get_rate(f->vorbis); + f->type = CA_SAMPLE_S16NE; + *f = f; + return CA_SUCCESS; + } + } + +fail: + + ca_free(f->filename); + ca_free(f); + + return ret; +} + +void ca_sound_file_close(ca_sound_file *f) { + ca_assert(f); + + if (f->wav) + ca_wav_free(f->wav); + if (f->vorbis) + ca_vorbis_free(f->vorbis); + ca_free(f); +} + +unsigned ca_sound_file_get_nchannels(ca_sound_file *f) { + ca_assert(f); + return f->nchannels; +} + +unsigned ca_sound_file_get_rate(ca_sound_file *f) { + ca_assert(f); + return f->nchannels; +} + +ca_sample_type_t ca_sound_file_get_sample_type(ca_sound_file *f) { + ca_assert(f); + return f->type; +} + +int ca_sound_file_read_int16(ca_sound_file *f, int16_t *d, unsigned *n) { + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + ca_return_val_if_fail(f->wav || f->vorbis, CA_ERROR_STATE); + ca_return_val_if_fail(f->type == CA_SAMPLE_S16NE || f->type == CA_SAMPLE_S16RE, CA_ERROR_STATE); + + if (f->wav) + return ca_wav_read_s16le(f->wav, d, n); + else + return ca_vorbis_read_s16ne(f->wav, d, n); +} + +int ca_sound_file_read_uint8(ca_sound_file *fn, uint8_t *d, unsigned *n) { + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + ca_return_val_if_fail(f->wav && !f->vorbis, CA_ERROR_STATE); + ca_return_val_if_fail(f->type == CA_SAMPLE_U8, CA_ERROR_STATE); + + if (f->wav) + return ca_wav_read_u8(f->wav, d, n); +} + +int ca_sound_file_read_arbitrary(ca_sound_file *f, void *d, size_t *n) { + int ret; + + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + ca_return_val_if_fail(f->wav && !f->vorbis, CA_ERROR_STATE); + + switch (f->type) { + case CA_SAMPLE_S16NE: + case CA_SAMPLE_S16RE: { + unsigned k; + + k = *n / sizeof(int16_t); + if ((ret = ca_sound_file_read_int16(f, d, &k)) == CA_SUCCESS) + *n = k * sizeof(int16_t); + + break; + } + + case CA_SAMPLE_S16RE: { + unsigned k; + + k = *n; + if ((ret = ca_sound_file_read_uint8(f, d, &k)) == CA_SUCCESS) + *n = k; + + break; + } + + default: + ca_assert_not_reached(); + } + + return ret; +} diff --git a/src/read-sound-file.h b/src/read-sound-file.h new file mode 100644 index 0000000..5ab7e66 --- /dev/null +++ b/src/read-sound-file.h @@ -0,0 +1,48 @@ +#ifndef foocareadsoundfilehfoo +#define foocareadsoundfilehfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +typedef enum ca_sample_type { + CA_SAMPLE_S16NE, + CA_SAMPLE_S16RE, + CA_SAMPLE_U8 +} ca_sample_type_t; + +typedef struct ca_sound_file wa_sound_file; + +int ca_sound_file_open(ca_sound_file *f, const char *fn); +void ca_sound_file_close(ca_sound_file *f); + +unsigned ca_sound_file_get_nchannels(ca_sound_file *f); +unsigned ca_sound_file_get_rate(ca_sound_file *f); +ca_sample_type_t ca_sound_file_get_sample_type(ca_sound_file *f); + +size_t ca_sound_file_get_size(ca_sound_file *f); + +int ca_sound_file_read_int16(ca_sound_file *f, int16_t *d, unsigned *n); +int ca_sound_file_read_uint8(ca_sound_file *f, uint8_t *d, unsigned *n); + +int ca_sound_file_read_arbitrary(ca_sound_file *f, void *d, size_t *n); + +#endif diff --git a/src/read-vorbis.c b/src/read-vorbis.c new file mode 100644 index 0000000..717f851 --- /dev/null +++ b/src/read-vorbis.c @@ -0,0 +1,148 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "read-vorbis.h" + +#define FILE_SIZE_MAX (64U*1024U*1024U) + +struct ca_vorbis { + OggVorbis_File ovf; +}; + +static int convert_error(int or) { + switch (or) { + case OV_NOSEEK: + case OV_BADPACKET: + case OV_BADLINK: + case OV_FAULT: + case OV_EREAD: + case OV_HOLE: + return CA_ERROR_IO; + + case OV_EIMPL: + case OV_EVERSION: + case OV_ENOTAUDIO + return CA_ERROR_NOT_SUPPORTED; + + case OV_ENOTVORBIS: + case OV_EBADHEADER: + case OV_EOF: + return CA_ERROR_CORRUPT; + + case OV_EINVAL: + return CA_ERROR_INVALID; + + default: + return CA_ERROR_INTERNAL; + } +} + +int ca_vorbis_open(ca_vorbis **_v, FILE *f) { + int ret, or; + ca_vorbis *v; + int64_t n; + + ca_return_val_if_fail(_v, CA_ERROR_INVALID); + ca_return_val_if_fail(f, CA_ERROR_INVALID); + + if (!(v = ca_new(ca_vorbis, 1))) + return CA_ERROR_OOM; + + if ((or = ov_open(f, &v->ovf, NULL, 0)) < 0) { + ret = convert_error(or); + goto fail; + } + + if ((n = ov_pcm_total(&ovf, -1)) < 0) { + ret = convert_error(or); + ov_clear(&v->ovf); + goto fail; + } + + if (n * sizeof(int16_t) > FILE_SIZE_MAX) { + ret = CA_ERROR_TOOBIG; + ov_clear(&v->ovf); + goto fail; + } + + *_v = v; + + return CA_SUCCESS; + +fail: + + ca_free(v); + return ret; +} + +void ca_vorbis_close(ca_vorbis *v) { + ca_assert(v); + + ov_clear(&v->ovf); + ca_free(v); +} + +unsigned ca_vorbis_get_nchannels(ca_vorbis *v) { + vorbis_info *vi; + ca_assert(v); + + ca_assert_se(vi = ov_info(&vf, -1)); + + return vi->channels; +} + +unsigned ca_vorbis_get_rate(ca_vorbis *v) { + vorbis_info *vi; + ca_assert(v); + + ca_assert_se(vi = ov_info(&vf, -1)); + + return (unsigned) vi->rate; +} + +int ca_vorbis_read_int16ne(ca_vorbis *v, int16_t *d, unsigned *n){ + long r; + int section; + + ca_return_val_if_fail(w, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + + r = ov_read(&v->ovf, d, *n * sizeof(float), +#ifdef WORDS_BIGENDIAN + 1, +#else + 0, +#endif + 2, 1, §ion); + + if (r < 0) + return convert_error(or); + + /* We only read the first section */ + if (section != 0) + return 0; + + *n = (unsigned) r; + return CA_SUCCESS; +} diff --git a/src/read-vorbis.h b/src/read-vorbis.h new file mode 100644 index 0000000..5521c09 --- /dev/null +++ b/src/read-vorbis.h @@ -0,0 +1,38 @@ +#ifndef foocareadvorbishfoo +#define foocareadvorbishfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include + +typedef struct ca_vorbis ca_vorbis; + +int ca_vorbis_open(ca_vorbis *v, FILE *f); +void ca_vorbis_close(ca_vorbis *v); + +unsigned ca_vorbis_get_nchannels(ca_vorbis *v); +unsigned ca_vorbis_get_rate(ca_vorbis *v); + +int ca_vorbis_read_s16ne(ca_vorbis *v, int16_t *d, unsigned *n); + +#endif diff --git a/src/read-wav.c b/src/read-wav.c new file mode 100644 index 0000000..1f3d634 --- /dev/null +++ b/src/read-wav.c @@ -0,0 +1,247 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "read-wav.h" + +#define FILE_SIZE_MAX (64U*1024U*1024U) + +struct ca_wav { + uint32_t data_size; + FILE *file; + + unsigned nchannels; + unsigned rate; + unsigned depth; +}; + +static int skip_to_chunk(ca_wav *v, uint32_t id, uint32_t *size) { + + ca_return_val_if_fail(v, CA_ERROR_INVALID); + ca_return_val_if_fail(size, CA_ERROR_INVALID); + + for (;;) { + uint32_t chunk[2]; + size_t s; + + if (fread(chunk, sizeof(uint32), CA_ELEMENTSOF(chunk), w->file) != CA_ELEMENTSOF(chunk)) + goto fail_io; + + s = PA_UINT32_FROM_LE(chunk[1]); + + if (s <= 0 || s >= FILE_SIZE_MAX) + return CA_ERROR_TOOBIG; + + if (PA_UINT32_FROM_LE(chunk[0]) == id) { + *size = s; + break; + } + + if (fseek(w->file, s, SEEK_CUR) < 0) + return CA_ERROR_SYSTEM; + } + + return CA_SUCCESS; + +fail_io: + + if (feof(f)) + return CA_ERROR_CORRUPT; + else if (ferror(f)) + return CA_ERROR_SYSTEM; + + ca_assert_not_reached(); +} + +int ca_wav_open(ca_wav **_w, FILE *f) { + uint32_t header[3], fmt_chunk[4]; + int ret; + ca_wav *w; + uint32_t file_size, fmt_size, data_size; + + ca_return_val_if_fail(_w, CA_ERROR_INVALID); + ca_return_val_if_fail(f, CA_ERROR_INVALID); + + if (!(w = ca_new(ca_wav, 1))) + return CA_ERROR_OOM; + + v->file = f; + + if (fread(header, sizeof(uint32), CA_ELEMENTSOF(header), f) != CA_ELEMENTSOF(header)) + goto fail_io; + + if (PA_UINT32_FROM_LE(header[0]) != 0x46464952U || + PA_UINT32_FROM_LE(header[2]) != 0x45564157U) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + file_size = PA_UINT32_FROM_LE(header[1]); + + if (file_size <= 0 || file_size >= FILE_SIZE_MAX) { + ret = CA_ERROR_TOOBIG; + goto fail; + } + + /* Skip to the fmt chunk */ + if ((ret = skip_to_chunk(w, 0x20746d66U, &fmt_size)) < 0) + goto fail; + + if (fmt_size != 16) { + ret = CA_ERROR_NOT_SUPPORTED; + goto fail; + } + + if (fread(fmt_chunk, sizeof(uint32), CA_ELEMENTSOF(fmt_chunk), f) != CA_ELEMENTSOF(fmt_chunk)) + goto fail_io; + + if (PA_UINT32_FROM_LE(fmt_chunk[0]) & 0xFFFF != 1) { + ret = CA_ERROR_NOT_SUPPORTED; + goto fail; + } + + w->nchannels = PA_UINT32_FROM_LE(fmt_chunk[0]) >> 16; + w->rate = PA_UINT32_FROM_LE(fmt_chunk[1]); + w->depth = PA_UINT32_FROM_LE(fmt_chunk[3]) >> 16; + + if (w->nchannels <= 0 || w->nrate <= 0) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + if (w->depth != 16 && w->depth != 8) { + ret = CA_ERROR_NOT_SUPPORTED; + goto fail; + } + + /* Skip to the data chunk */ + if ((ret = skip_to_chunk(w, 0x61746164U, &w->data_size)) < 0) + goto fail; + + if ((w->data_size % (w->depth/8)) != 0) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + *_w = w; + + return PA_SUCCESS; + +fail_io: + + if (feof(f)) + ret = CA_ERROR_CORRUPT; + else if (ferror(f)) + ret = CA_ERROR_SYSTEM; + else + ca_assert_not_reached(); + +fail: + + ca_free(w); + + return ret; +} + +void ca_wav_close(ca_wav *w) { + ca_assert(w); + + fclose(w->file); + ca_free(w); +} + +unsigned ca_wav_get_nchannels(ca_wav *w) { + ca_assert(w); + + return w->nchannels; +} + +unsigned ca_wav_get_rate(ca_wav *w) { + ca_assert(w); + + return w->rate; +} + +ca_sample_type_t ca_wav_get_sample_type(ca_wav *f) { + ca_assert(w); + + return w->depth == 16 ? +#ifdef WORDS_BIGENDIAN + CA_SAMPLE_S16RE +#else + CA_SAMPLE_S16NE +#endif + : CA_SAMPLE_U8; +} + +int ca_wav_read_s16le(ca_wav *w, int16_t *d, unsigned *n) { + unsigned remaining; + + ca_return_val_if_fail(w, CA_ERROR_INVALID); + ca_return_val_if_fail(w->depth == 16, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + + remaining = w->data_size / sizeof(int16_t); + + if (*n > remaining) + *n = remaining; + + if (*n > 0) { + *n = fread(d, sizeof(int16_t), *n, w->file); + + if (*n <= 0 && ferror(w->file)) + return CA_ERROR_SYSTEM; + + ca_assert(w->data_size >= *n * sizeof(int16_t)); + w->data_size -= *n * sizeof(int16_t); + } + + return CA_SUCCESS; +} + +int ca_wav_read_u(ca_wav *w, uint8_t *d, unsigned *n) { + unsigned remaining; + + ca_return_val_if_fail(w, CA_ERROR_INVALID); + ca_return_val_if_fail(w->depth == 8, CA_ERROR_INVALID); + ca_return_val_if_fail(d, CA_ERROR_INVALID); + ca_return_val_if_fail(n, CA_ERROR_INVALID); + ca_return_val_if_fail(*n > 0, CA_ERROR_INVALID); + + remaining = w->data_size / sizeof(uint8_t); + + if (*n > remaining) + *n = remaining; + + if (*n > 0) { + *n = fread(d, sizeof(uint8_t), *n, w->file); + + if (*n <= 0 && ferror(w->file)) + return CA_ERROR_SYSTEM; + + ca_assert(w->data_size >= *n * sizeof(int16_t)); + w->data_size -= *n * sizeof(uint8_t); + } + + return CA_SUCCESS; +} diff --git a/src/read-wav.h b/src/read-wav.h new file mode 100644 index 0000000..8344cc9 --- /dev/null +++ b/src/read-wav.h @@ -0,0 +1,42 @@ +#ifndef foocareadwavhfoo +#define foocareadwavhfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include + +#include "read-sound-file.h" + +typedef struct ca_wav ca_wav; + +int ca_wav_open(ca_wav **v, FILE *f); +void ca_wav_close(ca_wav *f); + +unsigned ca_wav_get_nchannels(ca_wav *f); +unsigned ca_wav_get_rate(ca_wav *f); +ca_sample_type ca_wav_get_sample_type(ca_wav *f); + +int ca_wav_read_u8(ca_wav *f, uint8_t *d, unsigned *n); +int ca_wav_read_s16le(ca_wav *f, int16_t *d, unsigned *n); + +#endif diff --git a/src/sound-theme-spec.c b/src/sound-theme-spec.c new file mode 100644 index 0000000..fd0f95b --- /dev/null +++ b/src/sound-theme-spec.c @@ -0,0 +1,632 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "sound-theme-spec.h" +#include "llist.h" + +#define DEFAULT_THEME "freedesktop" +#define FALLBACK_THEME "freedesktop" +#define DEFAULT_OUTPUT_PROFILE "stereo" +#define N_THEME_DIR_MAX 8 + +typedef struct ca_data_dir ca_data_dir; + +struct ca_data_dir { + CA_LLIST_FIELDS(ca_data_dir); + + char *name; + char *output_profile; +}; + +struct ca_theme_data { + char *name; + + CA_LLIST_HEAD(ca_data_dir, data_dirs); + ca_theme_dir *last_dir; + + unsigned n_theme_dir; + ca_bool_t loaded_fallback_theme; +}; + +static int get_data_home(char **e) { + const char *env, *subdir; + char *r; + ca_return_val_if_fail(e, CA_ERROR_INVALID); + + if ((env = getenv("XDG_DATA_HOME")) && *env == '/') + subdir = ""; + else if ((env = getenv("HOME")) && *env == '/') + subdir = "/.local/share"; + else { + *e = NULL; + return CA_SUCCESS; + } + + if (!(r = ca_new(char, strlen(env) + strlen(subdir) + 1))) + return CA_ERROR_OOM; + + sprintf(r, "%s%s", env, subdir); + *e = r; + + return CA_SUCCESS; +} + +static ca_bool_t data_dir_matches(ca_data_dir *d, const char*output_profile) { + ca_assert(d); + ca_assert(profile); + + /* We might want to add more elaborate matching here eventually */ + + return streq(d->profile, output_profile); +} + +static ca_data_dir* find_data_dir(ca_theme_data *t, const char *name) { + ca_data_dir *d; + + ca_assert(t); + ca_assert(name); + + for (d = t->data_dirs; d; d = d->next) + if (streq(d->name, name)) + return d; + + return NULL; +} + +static int add_data_dir(ca_theme_data *t, const char *name) { + ca_data_dir *d; + + ca_return_val_if_fail(t, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + + if (find_data_dir(t, name)) + return; + + if (!(d = ca_new0(ca_data_dir, 1))) + return CA_ERROR_OOM; + + if (!(d->name = ca_strdup(name))) { + ca_free(d); + return CA_ERROR_OOM; + } + + CA_LLIST_INSERT_AFTER(ca_data_dir, t->data_dirs, t->last_dir, d); + t->last_dir = d; + + return CA_SUCCESS; +} + +static int load_theme_path(ca_theme_data *t, const char *prefix, const char *name) { + char *fn, *inherits = NULL; + FILE *f; + ca_bool_t in_sound_theme_section = FALSE; + ca_data_dir *current_data_dir = NULL; + + ca_return_val_if_fail(t, CA_ERROR_INVALID); + ca_return_val_if_fail(prefix, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + + if (!(fn = ca_new(char, strlen(prefix) + sizeof("/sounds/")-1 + strlen(name) + sizeof("index.theme")))) + return CA_ERROR_OOM; + + sprintf(fn, "%s/sounds/%s/index.theme", prefix, name); + f = fopen(fn, "r"); + ca_free(fn); + + if (!f) { + if (errno == ENOENT) + return CA_ERROR_NOTFOUND; + + return CA_ERROR_SYSTEM; + } + + for (;;) { + char ln[1024]; + + if (!(fgets(ln, f))) { + + if (feof(f)) + break; + + ca_assert(ferror(f)); + ret = CA_ERROR_SYSTEM; + goto fail; + } + + ln[strcspn(ln, "\n\r#")] = 0; + + if (!ln[0]) + continue; + + if (streq(ln, "[Sound Theme]")) { + in_sound_theme_section = TRUE; + current_data_dir = NULL; + continue; + } + + if (ln[0] == '[' && ln[strlen(ln-1)] == ']') { + char *d; + + if (!(d = ca_strndup(ln+1, strlen(ln)-2))) { + ret = CA_ERROR_OOM; + goto fail; + } + + current_data_dir = find_data_dir(e, d); + ca_free(d); + + in_sound_theme_section = FALSE; + continue; + } + + ca_assert(!in_sound_theme_section || !current_data_dir); + ca_assert(!current_data_dir || !in_sound_theme_section); + + if (in_sound_theme_section) { + + if (!strncmp(ln, "Inherits", 8)) { + + if (inherits) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + if (!(inherits = ca_strdup(ln + 8))) { + ret = CA_ERROR_OOM; + goto fail; + } + + continue; + } + + if (!strncmp(ln, "Directories", 11)) { + char *d; + + d = ln+11; + for (;;) { + size_t k = strcspn(d, ", "); + + if (k > 0) { + char *p; + + if (!(p = ca_strndup(d, k))) { + ret = CA_ERROR_OOM; + goto fail; + } + + ret = add_data_dir(t, p); + ca_free(p); + + if (ret != CA_SUCCESS) + goto fail; + } + + if (d[k] == 0) + break; + + = k+1; + } + + continue; + } + } + + if (current_data_dir) { + + if (!strncmp(ln, "OutputProfile", 13)) { + + if (current_data_dir->output_profile && !streq(current_data_dir->output_profile, ln+13)) { + ret = CA_ERROR_CORRUPT; + goto fail; + } + + if (!(current_data_dir->output_profile = ca_strdup(ln+13))) { + ret = CA_ERROR_OOM; + goto fail; + } + + continue; + } + } + } + + t->n_theme_dir ++; + + if (inherits) { + i = inherits; + for (;;) { + size_t k = strcspn(i, ", "); + + if (k > 0) { + char *p; + + if (!(p = ca_strndup(i, k))) { + ret = CA_ERROR_OOM; + goto fail; + } + + ret = load_theme_dir(t, p); + ca_free(p); + + if (ret != CA_SUCCESS) + goto fail; + } + + if (i[k] == 0) + break; + + i = k+1; + } + } + + ret = CA_SUCCESS; + +fail: + + ca_free(inherits); + ca_free(directories); + fclose(f); + + return ret; +} + +static int load_theme_dir(ca_theme_data *t, const char *name) { + ca_return_val_if_fail(t, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + ca_return_val_if_fail(t->n_theme_dir < N_THEME_DIR_MAX, CA_ERROR_CORRUPT); + + if ((ret = get_data_home(&e)) < 0) + return ret; + + if (streq(name, FALLBACK_THEME)) + t->loaded_fallback_theme = TRUE; + + if (e) { + ret = load_theme_path(t, e, name); + ca_free(e); + + if (ret != CA_ERROR_NOTFOUND) + return ret; + } + + if (!(e = getenv("XDG_DATA_DIRS")) || *e = 0) + e = "/usr/local/share:/usr/share"; + + for (;;) { + size_t k; + + k = strcspn(e, ":"); + + if (e[0] == '/' && k > 0) { + char *p; + + if (!(p = ca_strndup(e, k))) + return CA_ERROR_OOM; + + ret = load_theme_path(t, p, name); + ca_free(p); + + if (ret != CA_ERROR_NOTFOUND) + return ret; + } + + if (e[k] == 0) + break; + + e += k+1; + } + + return CA_ERROR_NOTFOUND; +} + +static int load_theme_data(ca_theme_data **_t, const char *name) { + ca_theme_data *t; + int ret; + + ca_return_val_if_fail(_t, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + + if (*_t) + if (streq((*_t)->name, name)) + return CA_SUCCESS; + + if (!(t = pa_xnew0(ca_theme_data, 1))) + return CA_ERROR_OOM; + + if (!(t->name = ca_strdup(name))) { + ret = CA_ERROR_OOM; + goto fail; + } + + if ((ret = load_theme_dir(t, name)) < 0) + goto fail; + + if (!t->loaded_fallback_theme) + if ((ret = load_theme_dir(t, FALLBACK_THEME)) < 0) + goto fail; + + if (*_t) + ca_theme_data_free(*_t); + + *_t = t; + + return CA_SUCCESS; + +fail: + + if (t) + ca_theme_data_free(t); + + return ret; +} + +static int find_sound_for_suffix(ca_sound_file **f, ca_theme_data *t, const char *name, const char *path, const char *suffix, const char *locale, const char *subdir) { + const char *fn; + + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + ca_return_val_if_fail(path, CA_ERROR_INVALID); + ca_return_val_if_fail(path[0] == '/', CA_ERROR_INVALID); + ca_return_val_if_fail(suffix, CA_ERROR_INVALID); + + if (!(fn = ca_sprintf_malloc("%s/%s%s%s%s%s%s", + path, + t ? "/" : "", + t ? t->name : "", + subdir ? "/" : "" + subdir ? subdir : "", + locale ? "/" : "", + locale ? locale : "", + name, suffix))) + return CA_ERROR_OOM; + + ret = ca_sound_file_open(f, fn); + ca_free(fn); + + return ret; +} + +static int find_sound_in_path(ca_sound_file **f, ca_theme_data *t, const char *name, const char *path, const char *locale, const char *subdir) { + int ret; + + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + ca_return_val_if_fail(path, CA_ERROR_INVALID); + ca_return_val_if_fail(path[0] == '/', CA_ERROR_INVALID); + + if (!(p = ca_new(char, strlen(path) + sizeof("/sounds")))) + return CA_ERROR_OOM; + + sprintf(p, "%s/sounds", path); + + if ((ret = find_sound_for_suffix(f, t, name, p, ".ogg", locale, subdir)) == CA_ERROR_NOTFOUND) + ret = find_sound_for_suffix(f, t, name, p, ".wav", locale, subdir); + + ca_free(p); + + return ret; +} + +static int find_sound_in_subdir(ca_sound_file **f, ca_theme_data *t, const char *name, const char *locale, const char *subdir) { + int ret; + char *e = NULL; + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + + if ((ret = get_data_home(&e)) < 0) + return ret; + + if (e) { + ret = find_sound_in_path(f, t, name, e, locale, subdir); + ca_free(e); + + if (ret != CA_ERROR_NOTFOUND) + return ret; + } + + if (!(e = getenv("XDG_DATA_DIRS")) || *e = 0) + e = "/usr/local/share:/usr/share"; + + for (;;) { + size_t k; + + k = strcspn(e, ":"); + + if (e[0] == '/' && k > 0) { + char *p; + + if (!(p = ca_strndup(e, k))) + return CA_ERROR_OOM; + + ret = find_sound_in_path(f, t, name, p, locale, subdir); + ca_free(p); + + if (ret != CA_ERROR_NOTFOUND) + return ret; + } + + if (e[k] == 0) + break; + + e += k+1; + } + + return CA_ERROR_NOTFOUND; +} + +static int find_sound_for_profile(ca_sound_file **f, ca_theme_data *t, const char *name, const char *locale, const char *profile) { + ca_data_dir *d; + + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + + if (!t) + return find_sound_in_subdir(f, NULL, name, locale, NULL); + + for (d = t->data_dirs; d; d = d->next) + if (data_dir_matches(d, profile)) { + int ret; + + if ((ret = find_sound_in_subdir(f, t, name, locale, d->name)) != CA_ERROR_NOTFOUND) + return ret; + } + + return CA_ERROR_NOTFOUND; +} + +static int find_sound_in_locale(ca_sound_file **f, ca_theme_data *t, const char *name, const char *locale, const char *profile) { + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + ca_return_val_if_fail(profile, CA_ERROR_INVALID); + + /* First, try the profile def itself */ + if ((ret = find_sound_for_profile(f, t, name, locale, profile)) != CA_ERROR_NOTFOUND) + return ret; + + /* Then, fall back to stereo */ + if (!streq(profile, DEFAULT_OUTPUT_PROFILE)) + if ((ret = find_sound_for_profile(f, t, name, locale, DEFAULT_PROFILE)) != CA_ERROR_NOTFOUND) + return ret; + + /* And fall back to no profile */ + return find_sound_for_profile(f, t, name, locale, NULL); +} + +static int find_sound_for_locale(ca_sound_file **f, ca_theme_data *theme, const char *name, const char *locale, const char *profile) { + const char *e; + + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + ca_return_val_if_fail(locale, CA_ERROR_INVALID); + ca_return_val_if_fail(profile, CA_ERROR_INVALID); + + /* First, try the locale def itself */ + if ((ret = find_sound_in_locale(f, theme, name, locale, profile)) != CA_ERROR_NOTFOUND) + return ret; + + /* Then, try to truncate at the @ */ + if ((e = strchr(locale, '@'))) { + char *t; + + if (!(t = ca_strndup(t, e - locale))) + return CA_ERROR_OOM; + + ret = find_sound_in_locale(f, theme, name, t, profile); + ca_free(t); + + if (ret != CA_ERROR_NOTFOUND) + return ret; + } + + /* Followed by truncating at the _ */ + if ((e = strchr(locale, '_'))) { + char *t; + + if (!(t = ca_strndup(t, e - locale))) + return CA_ERROR_OOM; + + ret = find_sound_in_locale(f, theme, name, t, profile); + ca_free(t); + + if (ret != CA_ERROR_NOTFOUND) + return ret; + } + + /* Then, try "C" as fallback locale */ + if (strcmp(locale, "C")) + if ((ret = find_sound_in_locale(f, theme, name, "C", profile)) != CA_ERROR_NOTFOUND) + return ret; + + /* Try without locale */ + return find_sound_in_locale(f, theme, name, NULL, profile); +} + +static int find_sound_for_theme(ca_sound_file **f, ca_theme_data **t, const char *theme, const char *name, const char *locale, const char *profile) { + int ret; + + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(t, CA_ERROR_INVALID); + ca_return_val_if_fail(theme, CA_ERROR_INVALID); + ca_return_val_if_fail(name, CA_ERROR_INVALID); + ca_return_val_if_fail(locale, CA_ERROR_INVALID); + ca_return_val_if_fail(profile, CA_ERROR_INVALID); + + /* First, try in the theme itself */ + if ((ret = load_theme_data(t, theme)) == CA_SUCCESS) + if ((ret = find_sound_in_theme(f, t, name, locale, profile)) != CA_ERROR_NOTFOUND) + return ret; + + /* Then, fall back to "unthemed" files */ + return find_sound_in_theme(f, NULL, name, locale, profile); +} + +int ca_lookup_sound(ca_sound_file **f, ca_theme_data **t, ca_proplist *p) { + int ret; + const char *name, *fname; + + ca_return_val_if_fail(f, CA_ERROR_INVALID); + ca_return_val_if_fail(t, CA_ERROR_INVALID); + ca_return_val_if_fail(p, CA_ERROR_INVALID); + + ca_mutex_lock(p->mutex); + + if ((name = ca_proplist_gets(p, CA_PROP_EVENT_ID))) { + const char *theme, *locale, *profile; + + if (!(theme = ca_proplist_gets(p, CA_PROP_CANBERRA_XDG_THEME_NAME))) + theme = DEFAULT_THEME; + + if (!(locale = ca_proplist_gets(p, CA_PROP_APPLICATION_LANGUAGE))) + if (!(locale = setlocale(LC_MESSAGES, NULL))) + locale = "C"; + + if (!(profile = ca_proplist_gets(p, CA_PROP_CANBERRA_XDG_THEME_OUTPUT_PROFILE))) + profile = DEFAULT_OUTPUT_PROFILE; + + ret = find_sound_for_theme(f, t, theme, name, locale, profile); + + } else if ((fname = ca_proplist_gets(p, CA_PROP_MEDIA_FILENAME))) + ret = ca_sound_file_open(f, fname); + else + ret = CA_ERROR_INVALID; + + ca_mutex_unlock(p->mutex); + + return ret; +} + +void ca_theme_data_free(ca_theme_data *t) { + ca_assert(t); + + while (t->data_dirs) { + ca_data_dir *d = t->data_dirs; + + CA_LLIST_REMOVE(ca_data_dir, t->data_dirs, d); + + ca_free(d->name); + ca_free(d->output_profile); + ca_free(d); + } + + ca_free(t->name); + ca_free(t); +} diff --git a/src/sound-theme-spec.h b/src/sound-theme-spec.h new file mode 100644 index 0000000..527ad11 --- /dev/null +++ b/src/sound-theme-spec.h @@ -0,0 +1,34 @@ +#ifndef foocasoundthemespechfoo +#define foocasoundthemespechfoo + +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include "read-sound-file.h" +#include "proplist.h" + +typedef struct ca_theme_data ca_theme_data; + +int ca_lookup_sound(ca_sound_file **f, ca_theme_data **t, ca_proplist *p); +void ca_theme_data_free(ca_theme_data *t); + +#endif diff --git a/src/test-gtk.c b/src/test-gtk.c new file mode 100644 index 0000000..71d8aac --- /dev/null +++ b/src/test-gtk.c @@ -0,0 +1,18 @@ + + +static void trigger(GtkWidget *w) { + + ca_context_play( + gca_default_context(), + CA_PROP_EVENT_ID, "clicked", + GCA_PROPS_FOR_WIDGET(w), + GCA_PROPS_FOR_MOUSE_EVENT(e), + GCA_PROPS_FOR_APP(), + NULL); + +} + +int main(int argc, char *argv[]) { + + ca_ +} diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..0fd30e0 --- /dev/null +++ b/src/test.c @@ -0,0 +1,79 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#include + +#include "canberra.h" + +int main(int argc, char *argv[]) { + ca_context_t *c; + + int id = 4711; + + ca_context_new(&c); + + /* Initialize a few meta variables for the following play() + * calls. They stay valid until they are overwritten with + * ca_context_set() again. */ + ca_context_change_props(c, + CA_PROP_APPLICATION_NAME, "An example", + CA_PROP_APPLICATION_ID, "org.freedesktop.libcanberra.Test", + CA_PROP_MEDIA_LANGUAGE, "de_DE", + CA_PROP_EVENT_X11_DISPLAY, getenv("DISPLAY"), + NULL); + + /* .. */ + + ca_context_open(c); + + + /* Signal a sound event. The meta data passed here overwrites the + * data set in any previous ca_context_set() calls. */ + ca_context_play(c, id, + CA_PROP_EVENT_ID, "click-event", + CA_PROP_MEDIA_FILENAME, "/usr/share/sounds/foo.wav", + CA_PROP_MEDIA_NAME, "Button has been clicked", + CA_PROP_MEDIA_ICON_NAME, "clicked", + NULL); + + /* .. */ + + ca_context_play(c, id, + CA_PROP_EVENT_ID, "logout", + CA_PROP_MEDIA_FILENAME, "/usr/share/sounds/bar.wav", + CA_PROP_MEDIA_NAME, "User has logged of from session", + CA_PROP_MEDIA_LANGUAGE, "en_EN", + NULL); + + /* .. */ + + sleep(1); + + /* Stops both sounds */ + ca_context_cancel(c, id); + + /* .. */ + + ca_context_destroy(c); + + return 0; +} diff --git a/test-gtk.c b/test-gtk.c deleted file mode 100644 index 71d8aac..0000000 --- a/test-gtk.c +++ /dev/null @@ -1,18 +0,0 @@ - - -static void trigger(GtkWidget *w) { - - ca_context_play( - gca_default_context(), - CA_PROP_EVENT_ID, "clicked", - GCA_PROPS_FOR_WIDGET(w), - GCA_PROPS_FOR_MOUSE_EVENT(e), - GCA_PROPS_FOR_APP(), - NULL); - -} - -int main(int argc, char *argv[]) { - - ca_ -} diff --git a/test.c b/test.c deleted file mode 100644 index 0fd30e0..0000000 --- a/test.c +++ /dev/null @@ -1,79 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of libcanberra. - - Copyright 2008 Lennart Poettering - - libcanberra is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 2.1 of the - License, or (at your option) any later version. - - libcanberra is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with libcanberra. If not, If not, see - . -***/ - -#include - -#include "canberra.h" - -int main(int argc, char *argv[]) { - ca_context_t *c; - - int id = 4711; - - ca_context_new(&c); - - /* Initialize a few meta variables for the following play() - * calls. They stay valid until they are overwritten with - * ca_context_set() again. */ - ca_context_change_props(c, - CA_PROP_APPLICATION_NAME, "An example", - CA_PROP_APPLICATION_ID, "org.freedesktop.libcanberra.Test", - CA_PROP_MEDIA_LANGUAGE, "de_DE", - CA_PROP_EVENT_X11_DISPLAY, getenv("DISPLAY"), - NULL); - - /* .. */ - - ca_context_open(c); - - - /* Signal a sound event. The meta data passed here overwrites the - * data set in any previous ca_context_set() calls. */ - ca_context_play(c, id, - CA_PROP_EVENT_ID, "click-event", - CA_PROP_MEDIA_FILENAME, "/usr/share/sounds/foo.wav", - CA_PROP_MEDIA_NAME, "Button has been clicked", - CA_PROP_MEDIA_ICON_NAME, "clicked", - NULL); - - /* .. */ - - ca_context_play(c, id, - CA_PROP_EVENT_ID, "logout", - CA_PROP_MEDIA_FILENAME, "/usr/share/sounds/bar.wav", - CA_PROP_MEDIA_NAME, "User has logged of from session", - CA_PROP_MEDIA_LANGUAGE, "en_EN", - NULL); - - /* .. */ - - sleep(1); - - /* Stops both sounds */ - ca_context_cancel(c, id); - - /* .. */ - - ca_context_destroy(c); - - return 0; -} -- cgit