示例#1
0
int ca_context_change_props_full(ca_context *c, ca_proplist *p) {
    int ret;
    ca_proplist *merged;

    ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
    ca_return_val_if_fail(c, CA_ERROR_INVALID);
    ca_return_val_if_fail(p, CA_ERROR_INVALID);

    ca_mutex_lock(c->mutex);

    if ((ret = ca_proplist_merge(&merged, c->props, p)) < 0)
        goto finish;

    ret = c->opened ? driver_change_props(c, p, merged) : CA_SUCCESS;

    if (ret == CA_SUCCESS) {
        ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS);
        c->props = merged;
    } else
        ca_assert_se(ca_proplist_destroy(merged) == CA_SUCCESS);

finish:

    ca_mutex_unlock(c->mutex);

    return ret;
}
示例#2
0
文件: gsound.c 项目: JackStars/gsound
/**
 * gsound_context_play_simplev:
 * @context: A #GSoundContext
 * @attrs: (element-type utf8 utf8): Attributes
 * @cancellable: (allow-none): A #GCancellable
 * @error: Return location for error
 * 
 * Returns: %TRUE on success, %FALSE on error
 * 
 * Rename to: gsound_context_play_simple
 */
gboolean
gsound_context_play_simplev(GSoundContext *self,
                            GHashTable *attrs,
                            GCancellable *cancellable,
                            GError **error)
{
    ca_proplist *proplist;
    int res = ca_proplist_create(&proplist);
    if (!test_return (res, error))
        return FALSE;
    
    hash_table_to_prop_list (attrs, proplist);

    res = ca_context_play_full(self->priv->ca,
                               GPOINTER_TO_INT(cancellable),
                               proplist, NULL, NULL);

    if (cancellable)
        g_cancellable_connect(cancellable,
                              G_CALLBACK(on_cancellable_cancelled),
                              g_object_ref(self),
                              g_object_unref);

    ca_proplist_destroy(proplist);
    
    return test_return (res, error);
}
示例#3
0
文件: gsound.c 项目: JackStars/gsound
/**
 * gsound_context_play_simple: (skip)
 * @context: A #GSoundContext
 * @cancellable: (allow-none): A #GCancellable, or %NULL
 * @error: Return location for error, or %NULL
 * @...: Arguments
 * 
 * Returns: %TRUE on success, or %FALSE, populating @error
 */
gboolean
gsound_context_play_simple (GSoundContext *self,
                            GCancellable *cancellable,
                            GError **error,
                            ...)
{
    ca_proplist *pl;
    va_list args;
    int res;
    
    g_return_val_if_fail(GSOUND_IS_CONTEXT(self), FALSE);

    if ((res = ca_proplist_create(&pl)) != CA_SUCCESS)
        return test_return(res, error);

    va_start(args, error);
    var_args_to_prop_list (args, pl);
    va_end(args);

    res = ca_context_play_full(self->priv->ca,
                               GPOINTER_TO_INT(cancellable),
                               pl, NULL, NULL);

    if (cancellable)
        g_cancellable_connect(cancellable,
                              G_CALLBACK(on_cancellable_cancelled),
                              g_object_ref(self),
                              g_object_unref);

    ca_proplist_destroy(pl);

    return test_return(res, error);
}
示例#4
0
/**
 * ca_context_destroy:
 * @c: the context to destroy.
 *
 * Destroy a (connected or unconnected) context object.
 *
 * Returns: 0 on success, negative error code on error.
 */
int ca_context_destroy(ca_context *c) {
    int ret = CA_SUCCESS;

    ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
    ca_return_val_if_fail(c, CA_ERROR_INVALID);

    /* There's no locking necessary here, because the application is
     * broken anyway if it destructs this object in one thread and
     * still is calling a method of it in another. */

    if (c->opened)
        ret = driver_destroy(c);

    if (c->props)
        ca_assert_se(ca_proplist_destroy(c->props) == CA_SUCCESS);

    if (c->mutex)
        ca_mutex_free(c->mutex);

    ca_free(c->driver);
    ca_free(c->device);
    ca_free(c);

    return ret;
}
示例#5
0
static gboolean
osk_audio_init_canberra(OskAudio* audio)
{
    GdkScreen* screen;
    ca_proplist* props;
    const char* name;
    int nr;

    if (ca_context_create(&audio->ca) != CA_SUCCESS)
        return FALSE;

    screen = gdk_screen_get_default();
    nr = gdk_screen_get_number(screen);
    name = gdk_display_get_name(gdk_screen_get_display(screen));

    /* Set default application properties */
    ca_proplist_create(&props);
    ca_proplist_sets(props, CA_PROP_APPLICATION_NAME, "Onboard");
    ca_proplist_sets(props, CA_PROP_APPLICATION_ID, "org.onboard.Onboard");
    ca_proplist_sets(props, CA_PROP_APPLICATION_ICON_NAME, "onboard");
    ca_proplist_sets(props, CA_PROP_WINDOW_X11_DISPLAY, name);
    ca_proplist_setf(props, CA_PROP_WINDOW_X11_SCREEN, "%i", nr);
    ca_context_change_props_full(audio->ca, props);
    ca_proplist_destroy(props);

    return TRUE;
}
void
screenshot_play_sound_effect (const gchar *event_id,
                              const gchar *event_desc)
{
  ca_context *c;
  ca_proplist *p = NULL;
  int res;

  c = ca_gtk_context_get ();

  res = ca_proplist_create (&p);
  if (res < 0)
    goto done;

  res = ca_proplist_sets (p, CA_PROP_EVENT_ID, event_id);
  if (res < 0)
    goto done;

  res = ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, event_desc);
  if (res < 0)
    goto done;

  res = ca_proplist_sets (p, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent");
  if (res < 0)
    goto done;

  ca_context_play_full (c, 0, p, NULL, NULL);

 done:
  if (p != NULL)
    ca_proplist_destroy (p);

}
示例#7
0
static gboolean
bell_audible_notify (MetaDisplay *display,
                     MetaWindow  *window)
{
#ifdef HAVE_LIBCANBERRA
  ca_proplist *p;
  int res;

  ca_proplist_create (&p);
  ca_proplist_sets (p, CA_PROP_EVENT_ID, "bell-window-system");
  ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Bell event"));
  ca_proplist_sets (p, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent");

  if (window)
    {
      ca_proplist_sets (p, CA_PROP_WINDOW_NAME, window->title);
      ca_proplist_setf (p, CA_PROP_WINDOW_X11_XID, "%lu", (unsigned long)window->xwindow);
      ca_proplist_sets (p, CA_PROP_APPLICATION_NAME, window->res_name);
      ca_proplist_setf (p, CA_PROP_APPLICATION_PROCESS_ID, "%d", window->net_wm_pid);
    }

  res = ca_context_play_full (ca_gtk_context_get (), 1, p, NULL, NULL);

  ca_proplist_destroy (p);

  return res == CA_SUCCESS || res == CA_ERROR_DISABLED;
#endif /* HAVE_LIBCANBERRA */

  return FALSE;
}
static gboolean
empathy_sound_play_internal (GtkWidget *widget, EmpathySound sound_id,
  ca_finish_callback_t callback, gpointer user_data)
{
  EmpathySoundEntry *entry;
  ca_context *c;
  ca_proplist *p = NULL;

  entry = &(sound_entries[sound_id]);
  g_return_val_if_fail (entry->sound_id == sound_id, FALSE);

  c = ca_gtk_context_get ();
  ca_context_cancel (c, entry->sound_id);

  DEBUG ("Play sound \"%s\" (%s)",
         entry->event_ca_id,
         entry->event_ca_description);

  if (ca_proplist_create (&p) < 0)
    goto failed;

  if (ca_proplist_sets (p, CA_PROP_EVENT_ID, entry->event_ca_id) < 0)
    goto failed;

  if (ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION,
          gettext (entry->event_ca_description)) < 0)
    goto failed;

  if (ca_gtk_proplist_set_for_widget (p, widget) < 0)
    goto failed;

  ca_context_play_full (ca_gtk_context_get (), entry->sound_id, p, callback,
      user_data);

  ca_proplist_destroy (p);

  return TRUE;

failed:
  if (p != NULL)
    ca_proplist_destroy (p);

  return FALSE;
}
示例#9
0
文件: bell.c 项目: LuckyElf/Metacity
void
meta_bell_notify (MetaDisplay *display, 
		  XkbAnyEvent *xkb_ev)
{
  /* flash something */
  if (meta_prefs_get_visual_bell ()) 
    bell_visual_notify (display, xkb_ev);

  if (meta_prefs_bell_is_audible ()) 
    {
      XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent*) xkb_ev;

#ifdef HAVE_CANBERRA
      ca_proplist *p;
      MetaWindow *window;
      int res;

      ca_proplist_create (&p);
      ca_proplist_sets (p, CA_PROP_EVENT_ID, "bell-window-system");
      ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Bell event"));
      ca_proplist_sets (p, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent");

      window = meta_display_lookup_x_window (display, xkb_bell_event->window);
      if (!window && (display->focus_window) && (display->focus_window->frame))
        window = display->focus_window;

      if (window)
        { 
          ca_proplist_sets (p, CA_PROP_WINDOW_NAME, window->title);
          ca_proplist_setf (p, CA_PROP_WINDOW_X11_XID, "%lu", (unsigned long)window->xwindow);
          ca_proplist_sets (p, CA_PROP_APPLICATION_NAME, window->res_name);
          ca_proplist_setf (p, CA_PROP_APPLICATION_PROCESS_ID, "%d", window->net_wm_pid);
        }

      /* First, we try to play a real sound ... */
      res = ca_context_play_full (ca_gtk_context_get (), 1, p, NULL, NULL);

      ca_proplist_destroy (p);

      if (res != CA_SUCCESS && res != CA_ERROR_DISABLED)
#else
      if (1)
#endif /* HAVE_CANBERRA */
        {      
          /* ...and in case that failed we use the classic X11 bell. */
          XkbForceDeviceBell (display->xdisplay, 
                              xkb_bell_event->device, 
                              xkb_bell_event->bell_class, 
                              xkb_bell_event->bell_id, 
                              xkb_bell_event->percent);
        }
    }
}
示例#10
0
static PyObject*
osk_audio_play(PyObject* self, PyObject* args)
{
    OskAudio* audio = (OskAudio*) self;
    GdkScreen* screen;
    ca_proplist* props;
    const char* event_id;
    float x, y;
    float xs, ys;
    int sw, sh, ret;

    if (!PyArg_ParseTuple(args, "sffff", &event_id, &x, &y, &xs, &ys))
        return NULL;

    screen = gdk_screen_get_default();
    sw = gdk_screen_get_width(screen);
    sh = gdk_screen_get_height(screen);

    ca_proplist_create(&props);
    ca_proplist_sets(props, CA_PROP_EVENT_ID, event_id);

    /* report mouse position for accessibility */
    if (x != -1 && y != -1)
    {
        ca_proplist_setf(props, CA_PROP_EVENT_MOUSE_X, "%0.0f", x);
        ca_proplist_setf(props, CA_PROP_EVENT_MOUSE_Y, "%0.0f", y);
    }

    /* place in space between speakers */
    if (xs != -1 && ys != -1)
    {
        /* comment from canberra-gtk.c:
         * We use these strange format strings here to avoid that libc
         * applies locale information on the formatting of floating numbers. */
        ca_proplist_setf(props, CA_PROP_EVENT_MOUSE_HPOS, "%i.%03i",
                         (int) x / (sw - 1), (int) (1000.0 * x / (sw - 1)) % 1000);
        ca_proplist_setf(props, CA_PROP_EVENT_MOUSE_VPOS, "%i.%03i",
                         (int) y / (sh - 1), (int) (1000.0 * y / (sh - 1)) % 1000);
    }

    ret = ca_context_play_full(audio->ca, DEFAULT_SOUND_ID, props, NULL, NULL);

    ca_proplist_destroy(props);

    if (ret < 0)
    {
        PyErr_SetString(OSK_EXCEPTION, ca_strerror(ret));
        return NULL;
    }

    Py_RETURN_NONE;
}
示例#11
0
文件: gsound.c 项目: JackStars/gsound
/**
 * gsound_context_play_full: (skip)
 * @context: A #GSoundContext
 * @cancellable: (allow-none): A #GCancellable, or %NULL
 * @callback: (scope async): callback
 * @user_data: User data passed to @callback
 * @...: Attributes
 * 
 */
void
gsound_context_play_full(GSoundContext *self,
                         GCancellable *cancellable,
                         GAsyncReadyCallback callback,
                         gpointer user_data,
                         ...)
{
    GError *inner_error = NULL;
    ca_proplist *proplist;
    va_list args;
    int res = ca_proplist_create(&proplist);
    if (!test_return (res, &inner_error))
    {
        g_simple_async_report_take_gerror_in_idle (G_OBJECT(self),
                                                   callback,
                                                   user_data,
                                                   inner_error);
    }

    va_start(args, user_data);
    var_args_to_prop_list (args, proplist);
    va_end(args);

    GSimpleAsyncResult *result = g_simple_async_result_new(G_OBJECT(self),
                                                           callback,
                                                           user_data,
                                                           NULL /*FIXME*/);

    res = ca_context_play_full(self->priv->ca,
                               GPOINTER_TO_INT(cancellable),
                               proplist,
                               on_ca_play_full_finished,
                               result);

    if (cancellable)
        g_cancellable_connect(cancellable,
                              G_CALLBACK(on_cancellable_cancelled),
                              g_object_ref(self),
                              g_object_unref);

    ca_proplist_destroy(proplist);

    if (!test_return (res, &inner_error))
    {
        g_simple_async_report_take_gerror_in_idle (G_OBJECT(self),
                                                   callback,
                                                   user_data,
                                                   inner_error);
        g_object_unref(result);
    }
}
示例#12
0
文件: gsound.c 项目: JackStars/gsound
/**
 * gsound_context_cachev:
 * @context: A #GSoundContext
 * @attrs: (element-type utf8 utf8): Hash table of attrerties
 * @error: Return location for error, or %NULL
 * 
 * Returns: %TRUE on success
 * 
 * Rename to: gsound_context_cache
 */
gboolean
gsound_context_cachev(GSoundContext *self,
                      GHashTable *attrs,
                      GError **error)
{
    ca_proplist *proplist;
    int res = ca_proplist_create(&proplist);
    if (!test_return (res, error))
        return FALSE;
    
    hash_table_to_prop_list (attrs, proplist);

    res = ca_context_cache_full(self->priv->ca, proplist);

    ca_proplist_destroy(proplist);

    return test_return(res, error);
}
示例#13
0
文件: gsound.c 项目: JackStars/gsound
/**
 * gsound_context_change_attrsv:
 * @context: A #GSoundContext
 * @attrs: (element-type utf8 utf8): Hash table of attributes to set
 * @error: Return location for error, or %NULL
 * 
 * Returns: %TRUE if attributes were updated successfully
 */
gboolean
gsound_context_change_attrsv (GSoundContext *self,
                              GHashTable *attrs,
                              GError **error)
{
    ca_proplist *pl;
    
    g_return_val_if_fail(GSOUND_IS_CONTEXT(self), FALSE);

    int res = ca_proplist_create(&pl);
    if (!test_return (res, error))
        return FALSE;
    
    hash_table_to_prop_list (attrs, pl);

    res = ca_context_change_props_full(self->priv->ca, pl);

    ca_proplist_destroy(pl);

    return test_return(res, error);
}
示例#14
0
int ca_context_cache(ca_context *c, ...) {
    int ret;
    va_list ap;
    ca_proplist *p = NULL;

    ca_return_val_if_fail(!ca_detect_fork(), CA_ERROR_FORKED);
    ca_return_val_if_fail(c, CA_ERROR_INVALID);

    va_start(ap, c);
    ret = ca_proplist_from_ap(&p, ap);
    va_end(ap);

    if (ret < 0)
        return ret;

    ret = ca_context_cache_full(c, p);

    ca_assert_se(ca_proplist_destroy(p) == 0);

    return ret;
}
示例#15
0
static void
play_sound_effect (GdkWindow *window)
{
  ca_context *c;
  ca_proplist *p = NULL;
  int res;

  c = ca_gtk_context_get ();

  res = ca_proplist_create (&p);
  if (res < 0)
    goto done;

  res = ca_proplist_sets (p, CA_PROP_EVENT_ID, "screen-capture");
  if (res < 0)
    goto done;

  res = ca_proplist_sets (p, CA_PROP_EVENT_DESCRIPTION, _("Screenshot taken"));
  if (res < 0)
    goto done;

  if (window != NULL)
    {
      res = ca_proplist_setf (p,
                              CA_PROP_WINDOW_X11_XID,
                              "%lu",
                              (unsigned long) GDK_WINDOW_XID (window));
      if (res < 0)
        goto done;
    }

  ca_context_play_full (c, 0, p, NULL, NULL);

 done:
  if (p != NULL)
    ca_proplist_destroy (p);

}
示例#16
0
static PyObject*
osk_audio_cache_sample(PyObject* self, PyObject* args)
{
    OskAudio* audio = (OskAudio*) self;
    ca_proplist* props;
    const char* event_id;
    int ret;

    if (!PyArg_ParseTuple(args, "s", &event_id))
        return NULL;

    ca_proplist_create(&props);
    ca_proplist_sets(props, CA_PROP_EVENT_ID, event_id);
    ret = ca_context_cache_full(audio->ca, props);
    ca_proplist_destroy(props);

    if (ret < 0)
    {
        PyErr_SetString(OSK_EXCEPTION, ca_strerror(ret));
        return NULL;
    }
    Py_RETURN_NONE;
}
示例#17
0
文件: gsound.c 项目: JackStars/gsound
static gboolean
gsound_context_real_init(GInitable *initable, GCancellable *cancellable, GError **error)
{
    GSoundContext *self = GSOUND_CONTEXT(initable);

    if (self->priv->ca)
        return TRUE;

    int success = ca_context_create(&self->priv->ca);
    
    if (!test_return(success, error))
        return FALSE;

    /* Set a couple of attributes here if we can */
    ca_proplist *pl;
    ca_proplist_create(&pl);

    ca_proplist_sets(pl, CA_PROP_APPLICATION_NAME, g_get_application_name());
    if (g_application_get_default())
    {
        GApplication *app = g_application_get_default ();
        ca_proplist_sets(pl, CA_PROP_APPLICATION_ID,
                         g_application_get_application_id(app));
    }

    success = ca_context_change_props_full(self->priv->ca, pl);

    ca_proplist_destroy(pl);

    if (!test_return(success, error))
    {
        ca_context_destroy(self->priv->ca);
        self->priv->ca = NULL;
    }

    return TRUE;
}
示例#18
0
文件: gsound.c 项目: JackStars/gsound
/**
 * gsound_context_cache: (skip)
 * @context: A #GSoundContext
 * @error: Return location for error, or %NULL
 * @...: attributes
 * 
 * Returns: %TRUE on success, %FALSE otherwise
 */
gboolean
gsound_context_cache(GSoundContext *self,
                     GError **error,
                     ...)
{
    ca_proplist *pl;
    va_list args;
    int res;
    
    g_return_val_if_fail(GSOUND_IS_CONTEXT(self), FALSE);

    if ((res = ca_proplist_create(&pl)) != CA_SUCCESS)
        return test_return(res, error);

    va_start(args, error);
    var_args_to_prop_list (args, pl);
    va_end(args);

    res = ca_context_cache_full(self->priv->ca, pl);

    ca_proplist_destroy(pl);

    return test_return(res, error);
}
/*
 * Volume is a value from 0.0 to 1.0
 */
static void
update_volume_label (MpdVolumeTile  *self,
                     double          volume)
{
  MpdVolumeTilePrivate *priv = GET_PRIVATE (self);
  char  *old_level;
  float  label_width;
  float  slider_width;
  float  x;

  g_return_if_fail (0.0 <= volume && volume <= 1.0);

  old_level = g_strdup (mx_label_get_text (MX_LABEL (priv->volume_label)));

  /* Label text */
  if (volume == 1.0)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Turned up to 11"));
  else if (volume >= 0.90)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Very loud"));
  else if (volume >= 0.75)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Loud"));
  else if (volume > 0.50)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Fairly loud"));
  else if (volume == 0.50)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Middle of the road"));
  else if (volume >= 0.25)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Fairly quiet"));
  else if (volume >= 0.10)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Quiet"));
  else if (volume > 0.0)
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Very quiet"));
  else
    mx_label_set_text (MX_LABEL (priv->volume_label), _("Silent"));

  /* Label position */
  label_width = clutter_actor_get_width (priv->volume_label);
  slider_width = clutter_actor_get_width (priv->volume_slider);
  x = slider_width * volume - label_width / 2;
  x = CLAMP (x, 0.0, slider_width - label_width);
  clutter_actor_set_x (priv->volume_label, x);

  /* Notification */
  if (0 != g_strcmp0 (old_level,
                      mx_label_get_text (MX_LABEL (priv->volume_label))))
  {
    gint res;
    ca_proplist *proplist;
    ca_context *context;

    if (g_atomic_int_get (&priv->playing_event_sound) > 0)
      return;

    context = ca_gtk_context_get ();

    ca_proplist_create (&proplist);
    ca_proplist_sets (proplist,
                      CA_PROP_EVENT_ID,
                      VOLUME_CHANGED_EVENT);

    res = ca_context_play_full (context,
                                1,
                                proplist,
                                (ca_finish_callback_t )_play_sound_completed_cb,
                                self);
    ca_proplist_destroy (proplist);

    if (res != CA_SUCCESS)
    {
      g_warning ("%s: Error playing test sound: %s",
                 G_STRLOC,
                 ca_strerror (res));
    } else {
      g_atomic_int_inc (&priv->playing_event_sound);
    }
  }
  g_free (old_level);
}
示例#20
0
文件: plugin.c 项目: jusa/ngfd
static int
canberra_sink_play (NSinkInterface *iface, NRequest *request)
{
    CanberraData *data = (CanberraData*) n_request_get_data (request, CANBERRA_KEY);
    (void) iface;

    N_DEBUG (LOG_CAT "sink play");

    g_assert (data != NULL);

    if (!data->sound_enabled)
        goto complete;

    if (canberra_connect () == FALSE)
        return FALSE;

    static GHashTable *cached_samples = NULL;
    if (!cached_samples) {
        cached_samples = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
    }

    NProplist *props = props = (NProplist*) n_request_get_properties (request);
    ca_proplist *ca_props = 0;
    int error;
    ca_proplist_create (&ca_props);

    /* TODO: don't hardcode */
    ca_proplist_sets (ca_props, CA_PROP_CANBERRA_XDG_THEME_NAME, "jolla-ambient");
    ca_proplist_sets (ca_props, CA_PROP_EVENT_ID, data->filename);

    /* convert all properties within the request that begin with
       "sound.stream." prefix. */
    n_proplist_foreach (props, proplist_to_structure_cb, ca_props);

    if (g_hash_table_lookup_extended (cached_samples, data->filename, NULL, NULL) == FALSE) {
        N_DEBUG (LOG_CAT "caching sample %s", data->filename);
        error = ca_context_cache_full (c_context, ca_props);
        if (error) {
            N_WARNING (LOG_CAT "canberra couldn't cache sample %s", data->filename);
            return FALSE;
        }

        g_hash_table_add (cached_samples, strdup(data->filename));
    } else {
        N_DEBUG (LOG_CAT "sample %s is cached", data->filename);
    }

    error = ca_context_play_full (c_context, 0, ca_props, NULL, NULL);
    ca_proplist_destroy (ca_props);

    if (error) {
        N_WARNING (LOG_CAT "sink play had a warning: %s", ca_strerror (error));

        if (error == CA_ERROR_NOTAVAILABLE ||
                error == CA_ERROR_DISCONNECTED ||
                error == CA_ERROR_STATE ||
                error == CA_ERROR_DESTROYED) {
            ca_context_destroy (c_context);
            c_context = 0;
        }

        return FALSE;
    }

complete:
    /* We do not know how long our samples play, but let's guess we
     * are done in 200ms. */
    data->complete_cb_id = g_timeout_add (200, canberra_complete_cb, data);

    return TRUE;
}