diff options
author | Lennart Poettering <lennart@poettering.net> | 2008-05-23 15:58:32 +0000 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2008-05-23 15:58:32 +0000 |
commit | 3f00b1697f7304bb565232f5378ec38a23e12649 (patch) | |
tree | 18678affac93691c733eede17b99f1bf0ce39793 | |
parent | 1559476e75534b4b4896a9fdb08f78af0ad7882e (diff) |
add wav/ogg reader, rework API
git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@6 01b60673-d06a-42c0-afdd-89cb8e0f78ac
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | canberra.h | 96 | ||||
-rw-r--r-- | common.c | 358 | ||||
-rw-r--r-- | common.h | 46 | ||||
-rw-r--r-- | driver.h | 30 | ||||
-rw-r--r-- | llist.h | 108 | ||||
-rw-r--r-- | macro.h | 212 | ||||
-rw-r--r-- | malloc.h | 23 | ||||
-rw-r--r-- | mutex-posix.c | 80 | ||||
-rw-r--r-- | mutex.h | 37 | ||||
-rw-r--r-- | proplist.c | 317 | ||||
-rw-r--r-- | proplist.h | 53 | ||||
-rw-r--r-- | pulse.c | 32 | ||||
-rw-r--r-- | read-sound-file.c | 129 | ||||
-rw-r--r-- | read-sound-file.h | 44 | ||||
-rw-r--r-- | read-vorbis.c | 148 | ||||
-rw-r--r-- | read-vorbis.h | 38 | ||||
-rw-r--r-- | read-wav.c | 247 | ||||
-rw-r--r-- | read-wav.h | 42 | ||||
-rw-r--r-- | test.c | 26 |
20 files changed, 1805 insertions, 268 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..01be0a4 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +CFLAGS=-W -Wall -pipe -O0 -g -DPACKAGE=libcanberra -pthread + +test: proplist.o mutex-posix.o common.o + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm -f *.o test @@ -1,6 +1,28 @@ #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 + <http://www.gnu.org/licenses/>. +***/ + #include <sys/types.h> #include <sys/param.h> #include <inttypes.h> @@ -43,8 +65,19 @@ */ -/* Context object */ -typedef struct ca_context ca_context_t; +#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 /** Context, event, and playback properties */ #define CA_PROP_MEDIA_NAME "media.name" @@ -56,11 +89,11 @@ typedef struct ca_context ca_context_t; #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_X11_DISPLAY "event.x11.display" -#define CA_PROP_EVENT_X11_XID "event.x11.xid" #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_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" @@ -71,8 +104,11 @@ typedef struct ca_context ca_context_t; #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_CONTROL_CACHE "control.cache" /* permanent, volatile, never */ -#define CA_PROP_CONTROL_VOLUME "control.volume" /* decibel */ +#define CA_PROP_CANBERRA_CONTROL_CACHE "canberra.control.cache" /* permanent, volatile, never */ +#define CA_PROP_CANBERRA_CONTROL_VOLUME "canberra.control.volume" /* decibel */ + +/* Context object */ +typedef struct ca_context ca_context; /** Playback completion event callback */ typedef void ca_finish_callback_t(ca_context *c, uint32_t id, void *userdata); @@ -86,53 +122,65 @@ enum { CA_ERROR_OOM = -4, CA_ERROR_NO_DRIVER = -5, CA_ERROR_SYSTEM = -6, - _CA_ERROR_MAX = -7 + CA_ERROR_CORRUPT = -7, + CA_ERROR_TOOBIG = -8, + _CA_ERROR_MAX = -9 }; +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_t **c); +int ca_context_create(ca_context **c); /** 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_t *c); +int ca_context_open(ca_context *c); /** Destroy a (connected or unconnected) cntext object. */ -int ca_context_destroy(ca_context_t *c); +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_t *c, ...) CA_GCC_SENTINEL; +int ca_context_change_props(ca_context *c, ...) CA_GCC_SENTINEL; /** Write an arbitrary data property to the context object. */ -int ca_context_change_prop(ca_context_t *c, const char *key, const void *data, size_t nbytes); - -/** Remove a property from the context object again. */ -int ca_context_remove_prop(ca_context_t *c, ...) CA_GCC_SENTINEL; +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. If the requested sound is not cached in the server yet - * this call might result in the sample being uploaded temporarily or - * permanently. */ -int ca_context_play(ca_context_t *c, uint32_t id, ...) CA_GCC_SENTINEL; + * 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. */ +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_with_callback(ca_context_t *c, uint32_t id, ca_finish_callback_t cb, void *userdata, ...) CA_GCC_SENTINEL; +int ca_context_play_full(ca_context *c, uint32_t id, ca_proplist *p, ca_finish_callback_t cb, void *userdata); -/** Cancel one or more event sounds that have been started via - * ca_context_play(). */ -int ca_context_cancel(ca_context_t *c, uint32_t id); +/** Upload the specified sample into the server and attach the + * specified properties to it */ +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(ca_context_t *c, ...) CA_GCC_SENTINEL; +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); @@ -1,302 +1,292 @@ +/* $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 + <http://www.gnu.org/licenses/>. +***/ + +#include <stdarg.h> + #include "canberra.h" #include "common.h" +#include "malloc.h" +#include "driver.h" +#include "proplist.h" -int ca_context_create(ca_context_t **c) { - ca_return_val_if_fail(c, CA_ERROR_INVALID); +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 = ca_new0(ca_context_t, 1))) + 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_t *c) { - int ret; - unsigned i; - ca_prop *p, *n; +int ca_context_destroy(ca_context *c) { + int ret = CA_SUCCESS; ca_return_val_if_fail(c, CA_ERROR_INVALID); - ret = driver_destroy(c); + /* 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. */ - for (p = c->first_item; p; p = n) { - n = p->next_item; - ca_free(p); - } + 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); return ret; } -int ca_context_open(ca_context_t *c) { +static int context_open_unlocked(ca_context *c) { int ret; ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(!c->opened, CA_ERROR_STATE); - if ((ret = driver_open(c)) == 0) + if (c->opened) + return CA_SUCCESS; + + if ((ret = driver_open(c)) == CA_SUCCESS) c->opened = TRUE; return ret; } -int ca_context_sets(ca_context_t *c, ...) { - va_list ap; - int ret = CA_SUCCESS; +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); - va_start(ap, c); + 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 = v_arg(ap, const char*))) + if (!(key = va_arg(ap, const char*))) break; - if (!(value = v_arg(ap, const char *))) { + if (!(value = va_arg(ap, const char*))) { ret = CA_ERROR_INVALID; - break; + goto fail; } - if ((ret = ca_context_set(c, key, value, strlen(value)+1)) < 0) - break; + if ((ret = ca_proplist_sets(p, key, value)) < 0) + goto fail; } - va_end(ap); + *_p = p; + + return CA_SUCCESS; + +fail: + ca_assert_se(ca_proplist_destroy(p) == CA_SUCCESS); return ret; } -static int _unset(ca_context_t *c, const char *key) { - ca_prop *p, *np; - unsigned i; +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); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - - i = calc_hash(key) % N_HASHTABLE; - np = NULL; - for (p = c->props[i]; p; np = p, p = p->next_in_slot) - if (strcmp(p->key, key) == 0) - break; + va_start(ap, c); + ret = ca_proplist_from_ap(&p, ap); + va_end(ap); - if (p) { - if (np) - np->next_in_slot = p->next_in_slot; - else - c->props[i] = p->next_in_slot; + if (ret < 0) + return ret; - if (p->prev_item) - p->prev_item->next_item = p->next_item; - else - c->first_item = p->next_item; + ret = ca_context_change_props_full(c, p); - if (p->next_item) - p->next_item->prev_item = p->prev_item; + ca_assert_se(ca_proplist_destroy(p) == 0); - ca_free(p); - } + return ret; } -int ca_context_set(ca_context_t *c, const char *key, const void *data, size_t nbytes) { +int ca_context_change_props_full(ca_context *c, ca_proplist *p) { int ret; - ca_prop *p; - char *k; + ca_proplist *merged; ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - ca_return_val_if_fail(!nbytes || data, CA_ERROR_INVALID); + ca_return_val_if_fail(p, CA_ERROR_INVALID); - if (!(k = ca_strdup(key))) - return CA_ERROR_OOM; + ca_mutex_lock(c->mutex); - if (!(p = ca_malloc(CA_ALIGN(sizeof(ca_prop)) + nbytes))) { - ca_free(k); - return CA_ERROR_OOM; - } - - if ((ret = _unset(c, key)) < 0) { - ca_free(p); - ca_free(k); - return ret; - } + if ((ret = ca_proplist_merge(&merged, c->props, p)) < 0) + goto finish; - p->key = k; - p->nbytes = nbytes; - memcpy(CA_PROP_DATA(p), data, nbytes); + ret = c->opened ? driver_change_props(c, p, merged) : CA_SUCCESS; - i = calc_hash(key) % N_HASHTABLE; + 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); - p->next_in_slot = c->props[i]; - c->props[i] = p; +finish: - p->prev_item = NULL; - p->next_item = c->first_item; - c->first_item = p; + ca_mutex_unlock(c->mutex); - if (c->opened) - if ((ret = driver_set(c, key, data, nbytes)) < 0) - return ret; - - return CA_SUCCESS; + return ret; } -int ca_context_unset(ca_context *c, ...) { - int ret = CA_SUCCESS; +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); - ca_return_val_if_fail(key, CA_ERROR_INVALID); - - va_start(ap, c); - - for (;;) { - const char *key; - if (!(key = v_arg(ap, const char*))) - break; + va_start(ap, id); + ret = ca_proplist_from_ap(&p, ap); + va_end(ap); - if (c->opened) { - if ((ret = driver_unset(c, key)) < 0) - break; - } + if (ret < 0) + return ret; - if ((ret = _unset(c, key)) < 0) - break; - } + ret = ca_context_play_full(c, id, p, NULL, NULL); - va_end(ap); + ca_assert_se(ca_proplist_destroy(p) == 0); return ret; } -/* Not exported */ -ca_prop* ca_context_get(ca_context *c, const char *key) { - ca_prop *p; - unsigned i; +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, NULL); - ca_return_val_if_fail(key, NULL); + 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); - i = calc_hash(key) % N_HASHTABLE; + ca_mutex_lock(c->mutex); - for (p = c->props[i]; p; p = p->next_in_slot) - if (strcmp(p->key, key) == 0) - return p; + 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); - return NULL; -} + if ((ret = context_open_unlocked(c)) < 0) + goto finish; -/* Not exported */ -const char* ca_context_gets(ca_context *c, const char *key) { - ca_prop *p; + ca_assert(c->opened); - ca_return_val_if_fail(c, NULL); - ca_return_val_if_fail(key, NULL); + ret = driver_play(c, id, p, cb, userdata); - if (!(p = ca_context_get(c, key))) - return NULL; +finish: - if (memchr(CA_PROP_DATA(p), 0, p->nbytes)) - return CA_PROP_DATA(p); + ca_mutex_unlock(c->mutex); - return NULL; + return ret; } -int ca_context_play(ca_context_t *c, uint32_t id, ...) { +int ca_context_cancel(ca_context *c, uint32_t id) { int ret; - va_list ap; ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(key, CA_ERROR_INVALID); + ca_mutex_lock(c->mutex); + ca_return_val_if_fail_unlock(c->opened, CA_ERROR_STATE, c->mutex); - if (!c->opened) - if ((ret = ca_context_open(c)) < 0) - return ret; - - ca_assert(c->opened); + ret = driver_cancel(c, id); - /* make sure event.id is set */ + ca_mutex_unlock(c->mutex); - va_start(ap, c); - for (;;) { - const char *key, *value; + return ret; +} - if (!(key = va_arg(ap, const char *))) - break; +int ca_context_cache(ca_context *c, ...) { + int ret; + va_list ap; + ca_proplist *p = NULL; - if (!(value = va_arg(ap, const char *))) { - va_end(ap); - return CA_ERROR_INVALID; - } + ca_return_val_if_fail(c, CA_ERROR_INVALID); - found = found || strcmp(key, CA_PROP_EVENT_ID) == 0; - } + va_start(ap, c); + ret = ca_proplist_from_ap(&p, ap); va_end(ap); - found = found || ca_context_gets(c, CA_PROP_EVENT_ID); + if (ret < 0) + return ret; - if (!found) - return CA_ERROR_INVALID; + ret = ca_context_cache_full(c, p); - va_start(ap, id); - ret = driver_play(c, id, ap); - va_end(ap); + ca_assert_se(ca_proplist_destroy(p) == 0); return ret; } -int ca_context_cancel(ca_context_t *c, uint32_ id) { - - ca_return_val_if_fail(c, CA_ERROR_INVALID); - ca_return_val_if_fail(c->opened, CA_ERROR_STATE); - - return driver_cancel(c, id); -} - -int ca_context_cache(ca_context_t *c, ...) { +int ca_context_cache_full(ca_context *c, ca_proplist *p) { int ret; - va_list ap; - ca_bool_t found = FALSE; ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(p, CA_ERROR_INVALID); - if (!c->opened) - if ((ret = ca_context_open(c)) < 0) - return ret; + ca_mutex_lock(c->mutex); - ca_assert(c->opened); + 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); - /* make sure event.id is set */ + if ((ret = context_open_unlocked(c)) < 0) + goto finish; - va_start(ap, c); - for (;;) { - const char *key, *value; - - if (!(key = va_arg(ap, const char *))) - break; - - if (!(value = va_arg(ap, const char *))) { - va_end(ap); - return CA_ERROR_INVALID; - } + ca_assert(c->opened); - found = found || strcmp(key, CA_PROP_EVENT_ID) == 0; - } - va_end(ap); + ret = driver_cache(c, p); - found = found || ca_context_gets(c, CA_PROP_EVENT_ID); +finish: - if (!found) - return CA_ERROR_INVALID; - - va_start(ap, c); - ret = driver_cache(c, ap); - va_end(ap); + ca_mutex_unlock(c->mutex); return ret; } @@ -315,9 +305,7 @@ const char *ca_strerror(int code) { }; ca_return_val_if_fail(code <= 0, NULL); - ca_return_val_if_fail(code > _SA_ERROR_MAX, NULL); + ca_return_val_if_fail(code > _CA_ERROR_MAX, NULL); return error_table[-code]; } - -#endif @@ -1,27 +1,39 @@ -#ifndef foocanberracommonh -#define foocanberracommonh +#ifndef foocacommonh +#define foocacommonh -#include "canberra.h" +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering -#define N_HASHTABLE 39 + 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. -typedef struct ca_prop { - char *key; - size_t nbytes; - struct ca_prop *next_in_slot, *next_item, *prev_item; -} ca_prop; + 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 + <http://www.gnu.org/licenses/>. +***/ + +#include "canberra.h" +#include "macro.h" +#include "mutex.h" struct ca_context { ca_bool_t opened; - ca_prop *prop_hashtable[N_HASHTABLE]; - ca_prop *first_item; - void *private; -}; - -#define CA_PROP_DATA(p) ((void*) ((char*) (p) + CA_ALIGN(sizeof(ca_prop)))) + ca_mutex *mutex; -ca_prop* ca_context_get(ca_context *c, const char *key); -const char* ca_context_gets(ca_context *c, const char *key); + ca_proplist *props; + void *private; +}; #endif @@ -1,16 +1,36 @@ #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 + <http://www.gnu.org/licenses/>. +***/ + #include "canberra.h" int driver_open(ca_context *c); int driver_destroy(ca_context *c); -int driver_set(ca_context *c, const char *key, const void* data, size_t nbytes); -int driver_unset(ca_context *c, const char *key); - -int driver_play(ca_context *c, uint32_t id, ca_notify_cb_t cb, void *userdata, va_list ap); +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, va_list ap); +int driver_cache(ca_context *c, ca_proplist *p); #endif @@ -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 + <http://www.gnu.org/licenses/>. +***/ + +#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 @@ -1,8 +1,42 @@ #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 + <http://www.gnu.org/licenses/>. +***/ + #include <stdio.h> -#include <assert.h> +#include <stdlib.h> + +#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__ @@ -10,50 +44,98 @@ #define CA_PRETTY_FUNCTION "" #endif -#define ca_return_if_fail(expr) \ - do { \ - if (!(expr)) { \ - fprintf(stderr, "%s: Assertion <%s> failed.\n", CA_PRETTY_FUNCTION, #expr ); \ - return; \ - } \ - } while(0) +#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 (!(expr)) { \ - fprintf(stderr, "%s: Assertion <%s> failed.\n", CA_PRETTY_FUNCTION, #expr ); \ - return (val); \ - } \ - } while(0) - -#define ca_assert assert - -/* An assert which guarantees side effects of x */ + 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_se(x) x +#define ca_assert(expr) do {} while (FALSE) #else -#define ca_assert_se(x) ca_assert(x) +#define ca_assert(expr) ca_assert_se(expr) #endif -#define ca_assert_not_reached() ca_assert(!"Should not be reached.") - -#define ca_assert_success(x) do { \ - int _r = (x); \ - ca_assert(_r == 0); \ - } while(0) +#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])) -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#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 -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#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 -#ifndef CLAMP -#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#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 @@ -76,6 +158,8 @@ #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*)); } @@ -86,4 +170,70 @@ typedef void (*ca_free_cb_t)(void *); typedef int ca_bool_t; +#ifdef HAVE_BYTESWAP_H +#include <byteswap.h> +#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 @@ -1,6 +1,28 @@ #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 + <http://www.gnu.org/licenses/>. +***/ + #include <stdlib.h> #include <string.h> @@ -17,4 +39,3 @@ void* ca_memdup(const void* p, size_t size); #define ca_newdup(t, p, n) ((t*) ca_memdup(p, sizeof(t)*(n))) #endif -~ diff --git a/mutex-posix.c b/mutex-posix.c new file mode 100644 index 0000000..f8e0f54 --- /dev/null +++ b/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 + <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pthread.h> +#include <errno.h> + +#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); +} @@ -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 + <http://www.gnu.org/licenses/>. +***/ + +#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 new file mode 100644 index 0000000..6c10595 --- /dev/null +++ b/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 + <http://www.gnu.org/licenses/>. +***/ + +#include <stdarg.h> + +#include "canberra.h" +#include "proplist.h" +#include "macro.h" +#include "malloc.h" + +#ifdef HAVE_CONFIG_H +#include <config.h> +#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 new file mode 100644 index 0000000..4cb511e --- /dev/null +++ b/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 + <http://www.gnu.org/licenses/>. +***/ + +#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 @@ -1,4 +1,28 @@ +/* $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 + <http://www.gnu.org/licenses/>. +***/ + #include <pulse/thread-mainloop.h> +#include <pulse/context.h> +#include <pulse/scache.h> #include "canberra.h" #include "common." @@ -11,10 +35,15 @@ struct private { #define PRIVATE(c) ((struct private *) ((c)->private) +static pa_proplist *convert_proplist(ca_proplist *c) { + ca_assert(c); +} + int driver_open(ca_context *c) { pa_proplist *l; struct private *p; ca_prop *i; + ca_return_val_if_fail(c, PA_ERROR_INVALID); if (!(p = PRIVATE(c) = ca_new0(struct private, 1))) @@ -72,8 +101,7 @@ int driver_open(ca_context *c) { int driver_destroy(ca_context *c) { ca_return_val_if_fail(c, PA_ERROR_INVALID); - - p = PRIVATE(c); + ca_assert_se(p = PRIVATE(c)); if (p->mainloop) pa_threaded_mainloop_stop(p->mainloop); diff --git a/read-sound-file.c b/read-sound-file.c new file mode 100644 index 0000000..37d0dd1 --- /dev/null +++ b/read-sound-file.c @@ -0,0 +1,129 @@ +/* $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 + <http://www.gnu.org/licenses/>. +***/ + +#include "read-sound-file.h" + +struct ca_sound_file { + ca_wav *wav; + ca_vorbis *vorbis; + + 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 (!(file = fopen(fn, "r"))) { + ret = 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); + + 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); +} diff --git a/read-sound-file.h b/read-sound-file.h new file mode 100644 index 0000000..8dcc1e0 --- /dev/null +++ b/read-sound-file.h @@ -0,0 +1,44 @@ +#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 + <http://www.gnu.org/licenses/>. +***/ + +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); + +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); + +#endif diff --git a/read-vorbis.c b/read-vorbis.c new file mode 100644 index 0000000..717f851 --- /dev/null +++ b/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 + <http://www.gnu.org/licenses/>. +***/ + +#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 new file mode 100644 index 0000000..5521c09 --- /dev/null +++ b/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 + <http://www.gnu.org/licenses/>. +***/ + +#include <stdio.h> + +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 new file mode 100644 index 0000000..1f3d634 --- /dev/null +++ b/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 + <http://www.gnu.org/licenses/>. +***/ + +#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 new file mode 100644 index 0000000..8344cc9 --- /dev/null +++ b/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 + <http://www.gnu.org/licenses/>. +***/ + +#include <stdio.h> + +#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 @@ -1,3 +1,25 @@ +/* $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 + <http://www.gnu.org/licenses/>. +***/ + #include <unistd.h> #include "canberra.h" @@ -12,7 +34,7 @@ int main(int argc, char *argv[]) { /* Initialize a few meta variables for the following play() * calls. They stay valid until they are overwritten with * ca_context_set() again. */ - ca_context_puts(c, + 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", @@ -21,8 +43,6 @@ int main(int argc, char *argv[]) { /* .. */ - ca_context_put(c, CA_PROP_MEDIA_ICON, "some png data here", 23); - ca_context_open(c); |