summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2008-05-23 15:58:32 +0000
committerLennart Poettering <lennart@poettering.net>2008-05-23 15:58:32 +0000
commit3f00b1697f7304bb565232f5378ec38a23e12649 (patch)
tree18678affac93691c733eede17b99f1bf0ce39793
parent1559476e75534b4b4896a9fdb08f78af0ad7882e (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--Makefile7
-rw-r--r--canberra.h96
-rw-r--r--common.c358
-rw-r--r--common.h46
-rw-r--r--driver.h30
-rw-r--r--llist.h108
-rw-r--r--macro.h212
-rw-r--r--malloc.h23
-rw-r--r--mutex-posix.c80
-rw-r--r--mutex.h37
-rw-r--r--proplist.c317
-rw-r--r--proplist.h53
-rw-r--r--pulse.c32
-rw-r--r--read-sound-file.c129
-rw-r--r--read-sound-file.h44
-rw-r--r--read-vorbis.c148
-rw-r--r--read-vorbis.h38
-rw-r--r--read-wav.c247
-rw-r--r--read-wav.h42
-rw-r--r--test.c26
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
diff --git a/canberra.h b/canberra.h
index 8482cf3..abcfd05 100644
--- a/canberra.h
+++ b/canberra.h
@@ -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);
diff --git a/common.c b/common.c
index 9ae9d8e..bc535d8 100644
--- a/common.c
+++ b/common.c
@@ -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
diff --git a/common.h b/common.h
index 6d4ca55..02e10c2 100644
--- a/common.h
+++ b/common.h
@@ -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
diff --git a/driver.h b/driver.h
index dc2e8c5..03d4461 100644
--- a/driver.h
+++ b/driver.h
@@ -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
diff --git a/llist.h b/llist.h
new file mode 100644
index 0000000..e9122ff
--- /dev/null
+++ b/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
+ <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
diff --git a/macro.h b/macro.h
index 26dca20..2f37bc1 100644
--- a/macro.h
+++ b/macro.h
@@ -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
diff --git a/malloc.h b/malloc.h
index f87b85b..b720a12 100644
--- a/malloc.h
+++ b/malloc.h
@@ -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);
+}
diff --git a/mutex.h b/mutex.h
new file mode 100644
index 0000000..31b75b3
--- /dev/null
+++ b/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
+ <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
diff --git a/pulse.c b/pulse.c
index 2abb4a5..f5e3996 100644
--- a/pulse.c
+++ b/pulse.c
@@ -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, &section);
+
+ 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
diff --git a/test.c b/test.c
index 8542ebd..0fd30e0 100644
--- a/test.c
+++ b/test.c
@@ -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);