From 617c912e950d582c5690e558b28fa1070f9b8263 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 6 Jun 2008 12:17:58 +0000 Subject: unify double inclusion defines src/sound-theme-spec.h src/read-wav.h src/read-vorbis.h src/proplist.h src/mutex.h src/llist.h src/common.h git-svn-id: file:///home/lennart/svn/public/libcanberra/trunk@52 01b60673-d06a-42c0-afdd-89cb8e0f78ac --- Makefile.am | 4 + configure.ac | 54 ++++- src/Makefile.am | 18 +- src/alsa.c | 493 ++++++++++++++++++++++++++++++++++++++++++++++ src/canberra-gtk-module.c | 101 +++++++--- src/common.c | 24 ++- src/common.h | 4 +- src/dso.c | 8 +- src/llist.h | 4 +- src/mutex.h | 4 +- src/null.c | 82 ++++++++ src/proplist.h | 4 +- src/pulse.c | 1 + src/read-sound-file.c | 10 + src/read-sound-file.h | 6 +- src/read-vorbis.h | 4 +- src/read-wav.h | 4 +- src/sound-theme-spec.h | 4 +- 18 files changed, 769 insertions(+), 60 deletions(-) create mode 100644 src/alsa.c create mode 100644 src/null.c diff --git a/Makefile.am b/Makefile.am index 2cb96a6..0c85fe2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,3 +26,7 @@ noinst_DATA = pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libcanberra.pc + +if HAVE_GTK +pkgconfig_DATA += libcanberra-gtk.pc +endif diff --git a/configure.ac b/configure.ac index 707179e..0e7f310 100644 --- a/configure.ac +++ b/configure.ac @@ -258,6 +258,26 @@ fi AC_SUBST(PULSE_CFLAGS) AC_SUBST(PULSE_LIBS) +### Null output (optional) #### + +AC_ARG_ENABLE([null], + AC_HELP_STRING([--disable-null], [Disable optional null output]), + [ + case "${enableval}" in + yes) null=yes ;; + no) null=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --disable-null) ;; + esac + ], + [null=yes]) + +if test "x${null}" != xno ; then + HAVE_NULL=1 + AC_DEFINE([HAVE_NULL], 1, [Have NULL output?]) +else + HAVE_NULL=0 +fi + ### GTK (optional) #### AC_ARG_ENABLE([gtk], @@ -302,7 +322,7 @@ PKG_CHECK_MODULES(VORBIS, [ vorbisfile ]) ### Chose builtin driver ### - AC_ARG_WITH([builtin], +AC_ARG_WITH([builtin], [AS_HELP_STRING([--with-builtin], [Choose builtin driver])], [], [with_builtin=dso]) @@ -311,6 +331,7 @@ HAVE_DSO=0 BUILTIN_DSO=0 BUILTIN_PULSE=0 BUILTIN_ALSA=0 +BUILTIN_NULL=0 case "x$with_builtin" in xpulse) @@ -320,6 +341,7 @@ case "x$with_builtin" in BUILTIN_PULSE=1 HAVE_ALSA=0 + HAVE_NULL=0 ;; xalsa) @@ -329,6 +351,17 @@ case "x$with_builtin" in BUILTIN_ALSA=1 HAVE_PULSE=0 + HAVE_NULL=0 + ;; + + xnull) + if test "x$HAVE_NULL" != x1 ; then + AC_MSG_ERROR([*** Null output selected for builtin driver, but not enabled. ***]) + fi + + BUILTIN_NULL=1 + HAVE_PULSE=0 + HAVE_ALSA=0 ;; xdso) @@ -341,21 +374,25 @@ case "x$with_builtin" in AC_MSG_ERROR([*** Unknown driver $with_builtin selected for builtin ***]) esac -if test "x$HAVE_PULSE" != x1 -a "x$HAVE_ALSA" != x1 ; then +if test "x$HAVE_PULSE" != x1 -a "x$HAVE_ALSA" != x1 -a "x$HAVE_NULL" != x1 ; then AC_MSG_ERROR([*** No backend enabled. ***]) fi AC_SUBST(HAVE_DSO) AC_SUBST(HAVE_PULSE) AC_SUBST(HAVE_ALSA) +AC_SUBST(HAVE_NULL) AC_SUBST(BUILTIN_DSO) -AC_SUBST(BUILTIN_ALSA) AC_SUBST(BUILTIN_PULSE) +AC_SUBST(BUILTIN_ALSA) +AC_SUBST(BUILTIN_NULL) AM_CONDITIONAL([HAVE_PULSE], [test "x$HAVE_PULSE" = x1]) AM_CONDITIONAL([HAVE_ALSA], [test "x$HAVE_ALSA" = x1]) +AM_CONDITIONAL([HAVE_NULL], [test "x$HAVE_NULL" = x1]) AM_CONDITIONAL([BUILTIN_DSO], [test "x$BUILTIN_DSO" = x1]) AM_CONDITIONAL([BUILTIN_PULSE], [test "x$BUILTIN_PULSE" = x1]) AM_CONDITIONAL([BUILTIN_ALSA], [test "x$BUILTIN_ALSA" = x1]) +AM_CONDITIONAL([BUILTIN_NULL], [test "x$BUILTIN_NULL" = x1]) ################################### # Output # @@ -393,6 +430,15 @@ if test "x$BUILTIN_ALSA" = "x1" ; then ENABLE_BUILTIN_ALSA=yes fi +ENABLE_NULL=no +if test "x$HAVE_NULL" = "x1" ; then + ENABLE_NULL=yes +fi +ENABLE_BUILTIN_NULL=no +if test "x$BUILTIN_NULL" = "x1" ; then + ENABLE_BUILTIN_NULL=yes +fi + ENABLE_GTK=no if test "x$HAVE_GTK" = "x1" ; then ENABLE_GTK=yes @@ -413,6 +459,8 @@ echo " Builtin PulseAudio: ${ENABLE_BUILTIN_PULSE} Enable ALSA: ${ENABLE_ALSA} Builtin ALSA: ${ENABLE_BUILTIN_ALSA} + Enable Null Output: ${ENABLE_NULL} + Builtin Null Output: ${ENABLE_BUILTIN_NULL} Enable GTK+: ${ENABLE_GTK} GTK Modules Directory: ${GTK_MODULES_DIR} " diff --git a/src/Makefile.am b/src/Makefile.am index 393f0b8..ea17f5d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,7 +89,7 @@ else lib_LTLIBRARIES += \ libcanberra-alsa.la -libcanberra_alsa_la_SOURCE = \ +libcanberra_alsa_la_SOURCES = \ alsa.c libcanberra_alsa_la_CFLAGS = \ $(ALSA_CFLAGS) @@ -99,6 +99,22 @@ libcanberra_alsa_la_LIBADD = \ endif endif +if HAVE_NULL +if BUILTIN_NULL + +libcanberra_la_SOURCES += \ + null.c + +else + +lib_LTLIBRARIES += \ + libcanberra-null.la + +libcanberra_null_la_SOURCES = \ + null.c +endif +endif + if BUILTIN_DSO libcanberra_la_SOURCES += \ diff --git a/src/alsa.c b/src/alsa.c new file mode 100644 index 0000000..0eb7aa3 --- /dev/null +++ b/src/alsa.c @@ -0,0 +1,493 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include + +#include "canberra.h" +#include "common.h" +#include "driver.h" +#include "llist.h" +#include "read-sound-file.h" +#include "sound-theme-spec.h" +#include "malloc.h" + +struct private; + +struct outstanding { + CA_LLIST_FIELDS(struct outstanding); + ca_bool_t dead; + uint32_t id; + ca_finish_callback_t callback; + void *userdata; + ca_sound_file *file; + snd_pcm_t *pcm; + int pipe_fd[2]; + ca_context *context; +}; + +struct private { + ca_theme_data *theme; + ca_mutex *outstanding_mutex; + ca_bool_t signal_semaphore; + sem_t semaphore; + ca_bool_t semaphore_allocated; + CA_LLIST_HEAD(struct outstanding, outstanding); +}; + +#define PRIVATE(c) ((struct private *) ((c)->private)) + +static void outstanding_free(struct outstanding *o) { + ca_assert(o); + + if (o->pipe_fd[1] >= 0) + close(o->pipe_fd[1]); + + if (o->pipe_fd[0] >= 0) + close(o->pipe_fd[0]); + + if (o->file) + ca_sound_file_close(o->file); + + if (o->pcm) + snd_pcm_close(o->pcm); + + ca_free(o); +} + +int driver_open(ca_context *c) { + struct private *p; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(!c->driver || streq(c->driver, "alsa"), CA_ERROR_NODRIVER); + ca_return_val_if_fail(!PRIVATE(c), CA_ERROR_STATE); + + if (!(c->private = p = ca_new0(struct private, 1))) + return CA_ERROR_OOM; + + if (!(p->outstanding_mutex = ca_mutex_new())) { + driver_destroy(c); + return CA_ERROR_OOM; + } + + if (sem_init(&p->semaphore, 0, 0) < 0) { + driver_destroy(c); + return CA_ERROR_OOM; + } + + p->semaphore_allocated = TRUE; + + return CA_SUCCESS; +} + +int driver_destroy(ca_context *c) { + struct private *p; + struct outstanding *out; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); + + p = PRIVATE(c); + + if (p->outstanding_mutex) { + ca_mutex_lock(p->outstanding_mutex); + + /* Tell all player threads to terminate */ + for (out = p->outstanding; out; out = out->next) { + + out->dead = TRUE; + + if (out->callback) + out->callback(c, out->id, CA_ERROR_DESTROYED, out->userdata); + + /* This will cause the thread to wakeup and terminate */ + if (out->pipe_fd[1] >= 0) { + close(out->pipe_fd[1]); + out->pipe_fd[1] = -1; + } + } + + if (p->semaphore_allocated) { + /* Now wait until all players are destroyed */ + p->signal_semaphore = TRUE; + while (p->outstanding) { + ca_mutex_unlock(p->outstanding_mutex); + sem_wait(&p->semaphore); + ca_mutex_lock(p->outstanding_mutex); + } + } + + ca_mutex_unlock(p->outstanding_mutex); + ca_mutex_free(p->outstanding_mutex); + } + + if (p->theme) + ca_theme_data_free(p->theme); + + if (p->semaphore_allocated) + sem_destroy(&p->semaphore); + + ca_free(p); + + c->private = NULL; + + return CA_SUCCESS; +} + +int driver_change_device(ca_context *c, char *device) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); + + return CA_SUCCESS; +} + +int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(changed, CA_ERROR_INVALID); + ca_return_val_if_fail(merged, CA_ERROR_INVALID); + + return CA_SUCCESS; +} + +int driver_cache(ca_context *c, ca_proplist *proplist) { + return CA_ERROR_NOTSUPPORTED; +} + +static int translate_error(int error) { + + switch (error) { + case -ENODEV: + case -ENOENT: + return CA_ERROR_NOTFOUND; + case -EACCES: + case -EPERM: + return CA_ERROR_ACCESS; + case -ENOMEM: + return CA_ERROR_OOM; + case -EBUSY: + return CA_ERROR_NOTAVAILABLE; + default: + return CA_ERROR_IO; + } +} + +static const snd_pcm_format_t sample_type_table[] = { +#ifdef WORDS_BIGENDIAN + [CA_SAMPLE_S16NE] = SND_PCM_FORMAT_S16_BE, + [CA_SAMPLE_S16RE] = SND_PCM_FORMAT_S16_LE, +#else + [CA_SAMPLE_S16NE] = SND_PCM_FORMAT_S16_LE, + [CA_SAMPLE_S16RE] = SND_PCM_FORMAT_S16_BE, +#endif + [CA_SAMPLE_U8] = SND_PCM_FORMAT_U8 +}; + +static int open_alsa(ca_context *c, struct outstanding *out) { + struct private *p; + int ret; + snd_pcm_hw_params_t *hwparams; + unsigned rate; + + snd_pcm_hw_params_alloca(&hwparams); + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); + ca_return_val_if_fail(out, CA_ERROR_INVALID); + + p = PRIVATE(c); + + if ((ret = snd_pcm_open(&out->pcm, c->device ? c->device : "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) + goto finish; + + if ((ret = snd_pcm_hw_params_any(out->pcm, hwparams)) < 0) + goto finish; + + if ((ret = snd_pcm_hw_params_set_access(out->pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + goto finish; + + if ((ret = snd_pcm_hw_params_set_format(out->pcm, hwparams, sample_type_table[ca_sound_file_get_sample_type(out->file)])) < 0) + goto finish; + + rate = ca_sound_file_get_rate(out->file); + if ((ret = snd_pcm_hw_params_set_rate_near(out->pcm, hwparams, &rate, 0)) < 0) + goto finish; + + if ((ret = snd_pcm_hw_params_set_channels(out->pcm, hwparams, ca_sound_file_get_nchannels(out->file))) < 0) + goto finish; + + if ((ret = snd_pcm_hw_params(out->pcm, hwparams)) < 0) + goto finish; + + if ((ret = snd_pcm_prepare(out->pcm)) < 0) + goto finish; + + return CA_SUCCESS; + +finish: + + return translate_error(ret); +} + +#define BUFSIZE (16*1024) + +static void* thread_func(void *userdata) { + struct outstanding *out = userdata; + int ret; + void *data, *d = NULL; + size_t fs, data_size; + size_t nbytes = 0; + struct pollfd *pfd = NULL; + nfds_t n_pfd; + struct private *p; + + p = PRIVATE(out->context); + + pthread_detach(pthread_self()); + + data_size = (BUFSIZE/fs)*fs; + + if (!(data = ca_malloc(data_size))) { + ret = CA_ERROR_OOM; + goto finish; + } + + fs = ca_sound_file_frame_size(out->file); + + if ((ret = snd_pcm_poll_descriptors_count(out->pcm)) < 0) { + ret = translate_error(ret); + goto finish; + } + + n_pfd = ret + 1; + if (!(pfd = ca_new(struct pollfd, n_pfd))) { + ret = CA_ERROR_OOM; + goto finish; + } + + if ((ret = snd_pcm_poll_descriptors(out->pcm, pfd+1, n_pfd-1)) < 0) { + ret = translate_error(ret); + goto finish; + } + + pfd[0].fd = out->pipe_fd[0]; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + + for (;;) { + unsigned short revents; + + if (out->dead) + break; + + if (poll(pfd, n_pfd, -1) < 0) { + ret = CA_ERROR_SYSTEM; + goto finish; + } + + /* We have been asked to shut down */ + if (pfd[0].revents) + break; + + if ((ret = snd_pcm_poll_descriptors_revents(out->pcm, pfd, n_pfd, &revents)) < 0) { + ret = translate_error(ret); + goto finish; + } + + if (revents != POLLOUT) { + + switch (snd_pcm_state(out->pcm)) { + + case SND_PCM_STATE_XRUN: + + if ((ret = snd_pcm_recover(out->pcm, -EPIPE, 1)) != 0) { + ret = translate_error(ret); + goto finish; + } + break; + + case SND_PCM_STATE_SUSPENDED: + + if ((ret = snd_pcm_recover(out->pcm, -ESTRPIPE, 1)) != 0) { + ret = translate_error(ret); + goto finish; + } + break; + + default: + + snd_pcm_drop(out->pcm); + + if ((ret = snd_pcm_prepare(out->pcm)) < 0) { + ret = translate_error(ret); + goto finish; + } + break; + } + + continue; + } + + if (nbytes <= 0) { + + nbytes = data_size; + + if ((ret = ca_sound_file_read_arbitrary(out->file, data, &nbytes)) < 0) + goto finish; + + d = data; + } + + if (nbytes <= 0) { + snd_pcm_drain(out->pcm); + break; + } + + if ((ret = snd_pcm_writei(out->pcm, d, nbytes/fs)) < 0) { + + if ((ret = snd_pcm_recover(out->pcm, ret, 1)) < 0) { + ret = translate_error(ret); + goto finish; + } + + continue; + } + + nbytes -= ret*fs; + d = (uint8_t*) d + ret*fs; + } + + ret = CA_SUCCESS; + +finish: + + ca_free(data); + ca_free(pfd); + + if (!out->dead) { + if (out->callback) + out->callback(out->context, out->id, ret, out->userdata); + } + + ca_mutex_lock(p->outstanding_mutex); + + CA_LLIST_REMOVE(struct outstanding, p->outstanding, out); + + if (!p->outstanding && p->signal_semaphore) + sem_post(&p->semaphore); + + ca_mutex_unlock(p->outstanding_mutex); + + outstanding_free(out); + + return NULL; +} + +int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) { + struct private *p; + struct outstanding *out = NULL; + int ret; + pthread_t thread; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(proplist, CA_ERROR_INVALID); + ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); + + p = PRIVATE(c); + + if (!(out = ca_new0(struct outstanding, 1))) { + ret = CA_ERROR_OOM; + goto finish; + } + + out->context = c; + out->id = id; + out->callback = cb; + out->userdata = userdata; + out->pipe_fd[0] = out->pipe_fd[1] = -1; + + if ((ret = ca_lookup_sound(&out->file, &p->theme, c->props, proplist)) < 0) + goto finish; + + if ((ret = open_alsa(c, out)) < 0) + goto finish; + + if (pthread_create(&thread, NULL, thread_func, out) < 0) { + ret = CA_ERROR_OOM; + goto finish; + } + + ret = CA_SUCCESS; + +finish: + + /* We keep the outstanding struct around if we need clean up later to */ + if (ret == CA_SUCCESS) { + ca_mutex_lock(p->outstanding_mutex); + CA_LLIST_PREPEND(struct outstanding, p->outstanding, out); + ca_mutex_unlock(p->outstanding_mutex); + } else + outstanding_free(out); + + return ret; +} + +int driver_cancel(ca_context *c, uint32_t id) { + struct private *p; + struct outstanding *out; + + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); + + p = PRIVATE(c); + + ca_mutex_lock(p->outstanding_mutex); + + for (out = p->outstanding; out; out = out->next) { + + if (out->id != id) + continue; + + out->dead = TRUE; + + if (out->callback) + out->callback(c, out->id, CA_ERROR_CANCELED, out->userdata); + + /* This will cause the thread to wakeup and terminate */ + if (out->pipe_fd[1] >= 0) { + close(out->pipe_fd[1]); + out->pipe_fd[1] = -1; + } + } + + ca_mutex_unlock(p->outstanding_mutex); + + return CA_SUCCESS; +} diff --git a/src/canberra-gtk-module.c b/src/canberra-gtk-module.c index 693e33b..2efe957 100644 --- a/src/canberra-gtk-module.c +++ b/src/canberra-gtk-module.c @@ -36,6 +36,10 @@ typedef struct { GdkEvent *event; } SoundEventData; +static GQueue sound_event_queue = G_QUEUE_INIT; + +static int idle_id = 0; + static guint signal_id_dialog_response, signal_id_widget_show, @@ -116,7 +120,7 @@ static GtkDialog* find_parent_dialog(GtkWidget *w) { return NULL; } -static void destroy_cb(SoundEventData *d) { +static void free_sound_event(SoundEventData *d) { g_object_unref(d->object); @@ -126,24 +130,28 @@ static void destroy_cb(SoundEventData *d) { if (d->event) gdk_event_free(d->event); - g_slice_free(d, sizeof(SoundEventData)); + g_slice_free(SoundEventData, d); } -static gboolean idle_cb(SoundEventData *d) { - int ret = CA_SUCCESS; +static void filter_sound_events(SoundEventData *d) { - idle_id = 0; + + +} + +static void dispatch_sound_event(SoundEventData *d) { + int ret = CA_SUCCESS; if (!GTK_WIDGET_DRAWABLE(d->object)) - return FALSE; + return; if (g_object_get_qdata(d->object, disable_sound_quark)) - return FALSE; + return; if (d->signal_id == signal_id_widget_show) { gboolean played_sound = FALSE; - /* Show signals for non-dialogs have already been filtered out + /* Show signals for non-windows have already been filtered out * by the emission hook! */ if (GTK_IS_MESSAGE_DIALOG(d->object)) { @@ -161,10 +169,46 @@ static gboolean idle_cb(SoundEventData *d) { played_sound = TRUE; } - } else { + } else if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) { + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, + CA_PROP_EVENT_ID, "menu-popup", + CA_PROP_EVENT_DESCRIPTION, "Menu popped up", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + played_sound = TRUE; } + if (!played_sound) + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, + CA_PROP_EVENT_ID, "window-new", + CA_PROP_EVENT_DESCRIPTION, "Window shown", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + } + + if (d->signal_id == signal_id_widget_hide) { + gboolean played_sound = FALSE; + + if (GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) { + + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, + CA_PROP_EVENT_ID, "menu-popdown", + CA_PROP_EVENT_DESCRIPTION, "Menu popped up", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + + played_sound = TRUE; + } + + if (!played_sound) + ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, + CA_PROP_EVENT_ID, "window-close", + CA_PROP_EVENT_DESCRIPTION, "Window closed", + CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", + NULL); + } + if (GTK_IS_DIALOG(d->object) && d->signal_id == signal_id_dialog_response) { int response; @@ -182,24 +226,6 @@ static gboolean idle_cb(SoundEventData *d) { } } - if (GTK_IS_WINDOW(d->object) && GTK_IS_MENU(gtk_bin_get_child(GTK_BIN(d->object)))) { - - /* This doesn't work */ - - if (d->signal_id == signal_id_widget_show) - ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, - CA_PROP_EVENT_ID, "menu-popup", - CA_PROP_EVENT_DESCRIPTION, "Menu popped up", - CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", - NULL); - else if (d->signal_id == signal_id_widget_hide) - ret = ca_gtk_play_for_widget(GTK_WIDGET(d->object), 0, - CA_PROP_EVENT_ID, "menu-popdown", - CA_PROP_EVENT_DESCRIPTION, "Menu popped up", - CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", - NULL); - } - if (GTK_IS_CHECK_MENU_ITEM(d->object) && d->signal_id == signal_id_check_menu_item_toggled) { if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(d))) @@ -270,9 +296,21 @@ static gboolean idle_cb(SoundEventData *d) { if (ret != CA_SUCCESS) g_warning("Failed to play event sound: %s", ca_strerror(ret)); +} + +static gboolean idle_cb(void *userdata) { + SoundEventData *d; + + idle_id = 0; + + while ((d = g_queue_pop_head(&sound_event_queue))) { + filter_sound_events(d); + dispatch_sound_event(d); + free_sound_event(d); + } return FALSE; - } +} static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_values, const GValue *param_values, gpointer data) { static SoundEventData *d = NULL; @@ -288,7 +326,7 @@ static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_valu !GTK_IS_WINDOW(object)) return TRUE; - d = g_slice_new0(SoundEventData, 1); + d = g_slice_new0(SoundEventData); d->object = g_object_ref(object); @@ -303,7 +341,10 @@ static gboolean emission_hook_cb(GSignalInvocationHint *hint, guint n_param_valu d->arg1_is_set = TRUE; } - idle_id = g_idle_add_full(GTK_PRIORITY_REDRAW-1, (GSourceFunc) idle_cb, d, (GDestroyNotify) destroy_cb); + g_queue_push_tail(&sound_event_queue, e); + + if (idle_id == 0) + idle_id = g_idle_add_full(GTK_PRIORITY_REDRAW-1, (GSourceFunc) idle_cb, NULL, NULL); return TRUE; } diff --git a/src/common.c b/src/common.c index 6e38c94..80be86c 100644 --- a/src/common.c +++ b/src/common.c @@ -36,6 +36,7 @@ int ca_context_create(ca_context **_c) { ca_context *c; int ret; + const char *d; ca_return_val_if_fail(_c, CA_ERROR_INVALID); @@ -43,16 +44,29 @@ int ca_context_create(ca_context **_c) { return CA_ERROR_OOM; if (!(c->mutex = ca_mutex_new())) { - ca_free(c); + ca_context_destroy(c); return CA_ERROR_OOM; } if ((ret = ca_proplist_create(&c->props)) < 0) { - ca_mutex_free(c->mutex); - ca_free(c); + ca_context_destroy(c); return ret; } + if ((d = getenv("CANBERRA_DRIVER"))) { + if ((ret = ca_context_set_driver(c, d)) < 0) { + ca_context_destroy(c); + return ret; + } + } + + if ((d = getenv("CANBERRA_DEVICE"))) { + if ((ret = ca_context_change_device(c, d)) < 0) { + ca_context_destroy(c); + return ret; + } + } + *_c = c; return CA_SUCCESS; } @@ -72,7 +86,9 @@ int ca_context_destroy(ca_context *c) { if (c->props) ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS); - ca_mutex_free(c->mutex); + if (c->mutex) + ca_mutex_free(c->mutex); + ca_free(c->driver); ca_free(c->device); ca_free(c); diff --git a/src/common.h b/src/common.h index 8c7a762..8483ba6 100644 --- a/src/common.h +++ b/src/common.h @@ -1,5 +1,5 @@ -#ifndef foocacommonh -#define foocacommonh +#ifndef foocanberracommonh +#define foocanberracommonh /* $Id$ */ diff --git a/src/dso.c b/src/dso.c index a302b27..5ff0fec 100644 --- a/src/dso.c +++ b/src/dso.c @@ -152,7 +152,6 @@ static int try_open(ca_context *c, const char *t) { int driver_open(ca_context *c) { int ret; struct private_dso *p; - const char *d; ca_return_val_if_fail(c, CA_ERROR_INVALID); ca_return_val_if_fail(!PRIVATE_DSO(c), CA_ERROR_STATE); @@ -168,12 +167,9 @@ int driver_open(ca_context *c) { p->ltdl_initialized = TRUE; - if (!(d = c->driver)) - d = getenv("CANBERRA_DRIVER"); + if (c->driver) { - if (d) { - - if ((ret = try_open(c, d)) < 0) { + if ((ret = try_open(c, c->driver)) < 0) { driver_destroy(c); return ret; } diff --git a/src/llist.h b/src/llist.h index e9122ff..839818f 100644 --- a/src/llist.h +++ b/src/llist.h @@ -1,5 +1,5 @@ -#ifndef foocallistfoo -#define foocallistfoo +#ifndef foocanberrallistfoo +#define foocanberrallistfoo /* $Id$ */ diff --git a/src/mutex.h b/src/mutex.h index 31b75b3..a00860f 100644 --- a/src/mutex.h +++ b/src/mutex.h @@ -1,5 +1,5 @@ -#ifndef foocamutexhfoo -#define foocamutexhfoo +#ifndef foocanberramutexhfoo +#define foocanberramutexhfoo /* $Id$ */ diff --git a/src/null.c b/src/null.c new file mode 100644 index 0000000..5b66c1f --- /dev/null +++ b/src/null.c @@ -0,0 +1,82 @@ +/* $Id$ */ + +/*** + This file is part of libcanberra. + + Copyright 2008 Lennart Poettering + + libcanberra is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libcanberra is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libcanberra. If not, If not, see + . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "canberra.h" +#include "common.h" +#include "driver.h" + +int driver_open(ca_context *c) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(!c->driver || streq(c->driver, "null"), CA_ERROR_NODRIVER); + + return CA_SUCCESS; +} + +int driver_destroy(ca_context *c) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + return CA_SUCCESS; +} + +int driver_change_device(ca_context *c, char *device) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + return CA_SUCCESS; +} + +int driver_change_props(ca_context *c, ca_proplist *changed, ca_proplist *merged) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(changed, CA_ERROR_INVALID); + ca_return_val_if_fail(merged, CA_ERROR_INVALID); + + return CA_SUCCESS; +} + +int driver_play(ca_context *c, uint32_t id, ca_proplist *proplist, ca_finish_callback_t cb, void *userdata) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(proplist, CA_ERROR_INVALID); + ca_return_val_if_fail(!userdata || cb, CA_ERROR_INVALID); + + if (cb) + cb(c, id, CA_SUCCESS, userdata); + + return CA_SUCCESS; +} + +int driver_cancel(ca_context *c, uint32_t id) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + + return CA_SUCCESS; +} + +int driver_cache(ca_context *c, ca_proplist *proplist) { + ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(proplist, CA_ERROR_INVALID); + + return CA_ERROR_NOTSUPPORTED; +} diff --git a/src/proplist.h b/src/proplist.h index d23e5dc..0716132 100644 --- a/src/proplist.h +++ b/src/proplist.h @@ -1,5 +1,5 @@ -#ifndef foocaproplisthfoo -#define foocaproplisthfoo +#ifndef foocanberraproplisthfoo +#define foocanberraproplisthfoo /* $Id$ */ diff --git a/src/pulse.c b/src/pulse.c index 04dc5db..a720231 100644 --- a/src/pulse.c +++ b/src/pulse.c @@ -363,6 +363,7 @@ int driver_destroy(ca_context *c) { int driver_change_device(ca_context *c, char *device) { ca_return_val_if_fail(c, CA_ERROR_INVALID); + ca_return_val_if_fail(c->private, CA_ERROR_STATE); /* We're happy with any device change. We might however add code * here eventually to move all currently played back event sounds diff --git a/src/read-sound-file.c b/src/read-sound-file.c index 3a1f93c..b3a369f 100644 --- a/src/read-sound-file.c +++ b/src/read-sound-file.c @@ -196,3 +196,13 @@ size_t ca_sound_file_get_size(ca_sound_file *f) { else return ca_vorbis_get_size(f->vorbis); } + +size_t ca_sound_file_frame_size(ca_sound_file *f) { + unsigned c; + + ca_assert(f); + + c = ca_sound_file_get_nchannels(f); + + return c * (ca_sound_file_get_sample_type(f) == CA_SAMPLE_U8 ? 1 : 2); +} diff --git a/src/read-sound-file.h b/src/read-sound-file.h index f236b13..bf39f8d 100644 --- a/src/read-sound-file.h +++ b/src/read-sound-file.h @@ -1,5 +1,5 @@ -#ifndef foocareadsoundfilehfoo -#define foocareadsoundfilehfoo +#ifndef foocanberrareadsoundfilehfoo +#define foocanberrareadsoundfilehfoo /* $Id$ */ @@ -48,4 +48,6 @@ int ca_sound_file_read_uint8(ca_sound_file *f, uint8_t *d, unsigned *n); int ca_sound_file_read_arbitrary(ca_sound_file *f, void *d, size_t *n); +size_t ca_sound_file_frame_size(ca_sound_file *f); + #endif diff --git a/src/read-vorbis.h b/src/read-vorbis.h index e23c1cc..76a54c7 100644 --- a/src/read-vorbis.h +++ b/src/read-vorbis.h @@ -1,5 +1,5 @@ -#ifndef foocareadvorbishfoo -#define foocareadvorbishfoo +#ifndef foocanberrareadvorbishfoo +#define foocanberrareadvorbishfoo /* $Id$ */ diff --git a/src/read-wav.h b/src/read-wav.h index 4287ee1..7d0fdd2 100644 --- a/src/read-wav.h +++ b/src/read-wav.h @@ -1,5 +1,5 @@ -#ifndef foocareadwavhfoo -#define foocareadwavhfoo +#ifndef foocanberrareadwavhfoo +#define foocanberrareadwavhfoo /* $Id$ */ diff --git a/src/sound-theme-spec.h b/src/sound-theme-spec.h index 652903a..a40ed00 100644 --- a/src/sound-theme-spec.h +++ b/src/sound-theme-spec.h @@ -1,5 +1,5 @@ -#ifndef foocasoundthemespechfoo -#define foocasoundthemespechfoo +#ifndef foocanberrasoundthemespechfoo +#define foocanberrasoundthemespechfoo /* $Id$ */ -- cgit