From eff00ba053cc65a46445bcb288c842b7810bde68 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Sat, 13 Sep 2008 11:45:47 +0000 Subject: Alsa based volume control API, stealed from gnome-settings-daemon. #8375 --- diff --git a/configure.ac b/configure.ac index 1d0ac21..bd052ed 100644 --- a/configure.ac +++ b/configure.ac @@ -19,7 +19,7 @@ AM_CHECK_PYTHON_HEADERS(,[AC_MSG_ERROR(could not find Python headers)]) AC_PATH_PROG(PYGTK_CODEGEN, pygtk-codegen-2.0, no) -PKG_CHECK_MODULES(EXT, pygtk-2.0 gtk+-2.0) +PKG_CHECK_MODULES(EXT, pygtk-2.0 gtk+-2.0 sm ice alsa) PYGTK_DEFSDIR=`$PKG_CONFIG --variable=defsdir pygtk-2.0` AC_SUBST(PYGTK_DEFSDIR) diff --git a/src/sugar/Makefile.am b/src/sugar/Makefile.am index 06500f7..ef7e634 100644 --- a/src/sugar/Makefile.am +++ b/src/sugar/Makefile.am @@ -14,6 +14,7 @@ pkgpyexecdir = $(pythondir)/sugar pkgpyexec_LTLIBRARIES = _sugarext.la _sugarext_la_CFLAGS = \ + -DHAVE_ALSA \ $(WARN_CFLAGS) \ $(EXT_CFLAGS) \ $(PYTHON_INCLUDES) @@ -24,6 +25,10 @@ _sugarext_la_LIBADD = $(EXT_LIBS) -lSM -lICE _sugarext_la_SOURCES = \ $(BUILT_SOURCES) \ _sugarextmodule.c \ + acme-volume.h \ + acme-volume.c \ + acme-volume-alsa.h \ + acme-volume-alsa.c \ gsm-app.h \ gsm-app.c \ gsm-client.h \ diff --git a/src/sugar/_sugarext.defs b/src/sugar/_sugarext.defs index a3d0f71..32ea18a 100644 --- a/src/sugar/_sugarext.defs +++ b/src/sugar/_sugarext.defs @@ -64,6 +64,20 @@ (gtype-id "GSM_TYPE_SESSION") ) +(define-object Volume + (in-module "Acme") + (parent "GObject") + (c-name "AcmeVolume") + (gtype-id "ACME_TYPE_VOLUME") +) + +(define-object VolumeAlsa + (in-module "Acme") + (parent "AcmeVolume") + (c-name "AcmeVolumeAlsa") + (gtype-id "ACME_TYPE_VOLUME_ALSA") +) + ;; Enumerations and flags ... (define-enum IconEntryPosition @@ -391,3 +405,57 @@ (return-type "GsmSession*") ) +;; From acme-volume.h + +(define-function acme_volume_get_type + (c-name "acme_volume_get_type") + (return-type "GType") +) + +(define-method get_volume + (of-object "AcmeVolume") + (c-name "acme_volume_get_volume") + (return-type "int") +) + +(define-method set_volume + (of-object "AcmeVolume") + (c-name "acme_volume_set_volume") + (return-type "none") + (parameters + '("int" "val") + ) +) + +(define-method get_mute + (of-object "AcmeVolume") + (c-name "acme_volume_get_mute") + (return-type "gboolean") +) + +(define-method set_mute + (of-object "AcmeVolume") + (c-name "acme_volume_set_mute") + (return-type "none") + (parameters + '("gboolean" "val") + ) +) + +(define-method mute_toggle + (of-object "AcmeVolume") + (c-name "acme_volume_mute_toggle") + (return-type "none") +) + +(define-method get_threshold + (of-object "AcmeVolume") + (c-name "acme_volume_get_threshold") + (return-type "int") +) + +(define-function acme_volume_new + (c-name "acme_volume_new") + (is-constructor-of "AcmeVolume") + (return-type "AcmeVolume*") +) diff --git a/src/sugar/_sugarext.override b/src/sugar/_sugarext.override index c6a7ae0..5a0608d 100644 --- a/src/sugar/_sugarext.override +++ b/src/sugar/_sugarext.override @@ -12,6 +12,7 @@ headers #include "sexy-icon-entry.h" #include "gsm-session.h" #include "gsm-xsmp.h" +#include "acme-volume-alsa.h" #include "eggsmclient.h" #include "eggsmclient-private.h" diff --git a/src/sugar/acme-volume-alsa.c b/src/sugar/acme-volume-alsa.c new file mode 100644 index 0000000..0462ed1 --- /dev/null +++ b/src/sugar/acme-volume-alsa.c @@ -0,0 +1,329 @@ +/* acme-volume-alsa.c + + Copyright (C) 2002, 2003 Bastien Nocera + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Bastien Nocera + */ + +#ifdef HAVE_CONFIG +#include "config.h" +#endif + +#include "acme-volume-alsa.h" + +#include + +#ifndef DEFAULT_CARD +#define DEFAULT_CARD "default" +#endif + +#undef LOG +#ifdef LOG +#define D(x...) g_message (x) +#else +#define D(x...) +#endif + +#define ROUND(x) ((x - (int)x > 0.5) ? x+1 : x) + +struct AcmeVolumeAlsaPrivate +{ + long pmin, pmax; + gboolean has_mute, has_master; + snd_mixer_t *handle; + snd_mixer_elem_t *elem; + int saved_volume; + guint timer_id; +}; + +static int acme_volume_alsa_get_volume (AcmeVolume *self); +static void acme_volume_alsa_set_volume (AcmeVolume *self, int val); +static gboolean acme_volume_alsa_open (AcmeVolumeAlsa *self); +static void acme_volume_alsa_close (AcmeVolumeAlsa *self); +static gboolean acme_volume_alsa_close_real (AcmeVolumeAlsa *self); + +G_DEFINE_TYPE (AcmeVolumeAlsa, acme_volume_alsa, ACME_TYPE_VOLUME) + +static void +acme_volume_alsa_finalize (GObject *object) +{ + AcmeVolumeAlsa *self; + + self = ACME_VOLUME_ALSA (object); + + if (self->_priv) + { + if (self->_priv->timer_id != 0) + { + g_source_remove (self->_priv->timer_id); + self->_priv->timer_id = 0; + } + + acme_volume_alsa_close_real (self); + g_free (self->_priv); + self->_priv = NULL; + } + + G_OBJECT_CLASS (acme_volume_alsa_parent_class)->finalize (object); +} + +static void +acme_volume_alsa_set_mute (AcmeVolume *vol, gboolean val) +{ + AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol; + + if (acme_volume_alsa_open (self) == FALSE) + return; + + /* If we have a hardware mute */ + if (self->_priv->has_mute) + { + snd_mixer_selem_set_playback_switch_all + (self->_priv->elem, !val); + acme_volume_alsa_close (self); + return; + } + + acme_volume_alsa_close (self); + + /* If we don't */ + if (val == TRUE) + { + self->_priv->saved_volume = acme_volume_alsa_get_volume (vol); + acme_volume_alsa_set_volume (vol, 0); + } else { + if (self->_priv->saved_volume != -1) + acme_volume_alsa_set_volume (vol, + self->_priv->saved_volume); + } +} + +static gboolean +acme_volume_alsa_get_mute (AcmeVolume *vol) +{ + AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol; + int ival; + + if (acme_volume_alsa_open (self) == FALSE) + return FALSE; + + if (self->_priv->has_mute) + { + snd_mixer_selem_get_playback_switch(self->_priv->elem, + SND_MIXER_SCHN_FRONT_LEFT, &ival); + + acme_volume_alsa_close (self); + + return !ival; + } else { + acme_volume_alsa_close (self); + + return (acme_volume_alsa_get_volume (vol) == 0); + } +} + +static int +acme_volume_alsa_get_volume (AcmeVolume *vol) +{ + AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol; + long lval, rval; + int tmp; + float alsa_vol; + + if (acme_volume_alsa_open (self) == FALSE) + return 0; + + snd_mixer_selem_get_playback_volume(self->_priv->elem, + SND_MIXER_SCHN_FRONT_LEFT, &lval); + snd_mixer_selem_get_playback_volume(self->_priv->elem, + SND_MIXER_SCHN_FRONT_RIGHT, &rval); + + acme_volume_alsa_close (self); + + alsa_vol = (lval + rval) / 2; + alsa_vol = alsa_vol * 100 / (self->_priv->pmax - self->_priv->pmin); + tmp = ROUND (alsa_vol); + + return tmp; +} + +static void +acme_volume_alsa_set_volume (AcmeVolume *vol, int val) +{ + AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol; + float volume; + int tmp; + + if (acme_volume_alsa_open (self) == FALSE) + return; + + volume = (float) val / 100 * (self->_priv->pmax - self->_priv->pmin); + volume = CLAMP (volume, self->_priv->pmin, self->_priv->pmax); + tmp = ROUND (volume); + + snd_mixer_selem_set_playback_volume_all (self->_priv->elem, tmp); + + acme_volume_alsa_close (self); +} + +static int +acme_volume_alsa_get_threshold (AcmeVolume *vol) +{ + AcmeVolumeAlsa *self = (AcmeVolumeAlsa *) vol; + int steps; + + if (acme_volume_alsa_open (self) == FALSE) + return 1; + + acme_volume_alsa_close (self); + + steps = self->_priv->pmax - self->_priv->pmin; + return (steps > 0) ? 100 / steps + 1 : 1; +} + +static gboolean +acme_volume_alsa_close_real (AcmeVolumeAlsa *self) +{ + if (self->_priv == NULL) + return FALSE; + + if (self->_priv->handle != NULL) + { + snd_mixer_detach (self->_priv->handle, DEFAULT_CARD); + snd_mixer_free (self->_priv->handle); + self->_priv->handle = NULL; + self->_priv->elem = NULL; + } + + self->_priv->timer_id = 0; + + return FALSE; +} + +static gboolean +acme_volume_alsa_open (AcmeVolumeAlsa *self) +{ + snd_mixer_selem_id_t *sid; + snd_mixer_t *handle; + snd_mixer_elem_t *elem; + + if (self->_priv->timer_id != 0) + { + g_source_remove (self->_priv->timer_id); + self->_priv->timer_id = 0; + return TRUE; + } + + /* open the mixer */ + if (snd_mixer_open (&handle, 0) < 0) + { + D("snd_mixer_open"); + return FALSE; + } + /* attach the handle to the default card */ + if (snd_mixer_attach (handle, DEFAULT_CARD) <0) + { + D("snd_mixer_attach"); + goto bail; + } + /* ? */ + if (snd_mixer_selem_register (handle, NULL, NULL) < 0) + { + D("snd_mixer_selem_register"); + goto bail; + } + if (snd_mixer_load (handle) < 0) + { + D("snd_mixer_load"); + goto bail; + } + + snd_mixer_selem_id_alloca (&sid); + snd_mixer_selem_id_set_name (sid, "Master"); + elem = snd_mixer_find_selem (handle, sid); + if (!elem) + { + snd_mixer_selem_id_alloca (&sid); + snd_mixer_selem_id_set_name (sid, "PCM"); + elem = snd_mixer_find_selem (handle, sid); + if (!elem) + { + D("snd_mixer_find_selem"); + goto bail; + } + } + + if (!snd_mixer_selem_has_playback_volume (elem)) + { + D("snd_mixer_selem_has_playback_volume"); + goto bail; + } + + snd_mixer_selem_get_playback_volume_range (elem, + &(self->_priv->pmin), + &(self->_priv->pmax)); + + self->_priv->has_mute = snd_mixer_selem_has_playback_switch (elem); + self->_priv->handle = handle; + self->_priv->elem = elem; + + return TRUE; + +bail: + acme_volume_alsa_close_real (self); + return FALSE; +} + +static void +acme_volume_alsa_close (AcmeVolumeAlsa *self) +{ + self->_priv->timer_id = g_timeout_add (4000, + (GSourceFunc) acme_volume_alsa_close_real, self); +} + +static void +acme_volume_alsa_init (AcmeVolumeAlsa *self) +{ + self->_priv = g_new0 (AcmeVolumeAlsaPrivate, 1); + + if (acme_volume_alsa_open (self) == FALSE) + { + g_free (self->_priv); + self->_priv = NULL; + return; + } + + if (self->_priv->handle != NULL) { + acme_volume_alsa_close_real (self); + return; + } +} + +static void +acme_volume_alsa_class_init (AcmeVolumeAlsaClass *klass) +{ + AcmeVolumeClass *volume_class = ACME_VOLUME_CLASS (klass); + G_OBJECT_CLASS (klass)->finalize = acme_volume_alsa_finalize; + + volume_class->set_volume = acme_volume_alsa_set_volume; + volume_class->get_volume = acme_volume_alsa_get_volume; + volume_class->set_mute = acme_volume_alsa_set_mute; + volume_class->get_mute = acme_volume_alsa_get_mute; + volume_class->get_threshold = acme_volume_alsa_get_threshold; +} + diff --git a/src/sugar/acme-volume-alsa.h b/src/sugar/acme-volume-alsa.h new file mode 100644 index 0000000..b179a24 --- /dev/null +++ b/src/sugar/acme-volume-alsa.h @@ -0,0 +1,47 @@ +/* acme-volume-alsa.h + + Copyright (C) 2002, 2003 Bastien Nocera + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Bastien Nocera + */ + +#include +#include +#include "acme-volume.h" + +#define ACME_TYPE_VOLUME_ALSA (acme_volume_alsa_get_type ()) +#define ACME_VOLUME_ALSA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_VOLUME_ALSA, AcmeVolumeAlsa)) +#define ACME_VOLUME_ALSA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ACME_TYPE_VOLUME_ALSA, AcmeVolumeAlsaClass)) +#define ACME_IS_VOLUME_ALSA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_VOLUME_ALSA)) +#define ACME_VOLUME_ALSA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_VOLUME_ALSA, AcmeVolumeAlsaClass)) + +typedef struct AcmeVolumeAlsa AcmeVolumeAlsa; +typedef struct AcmeVolumeAlsaClass AcmeVolumeAlsaClass; +typedef struct AcmeVolumeAlsaPrivate AcmeVolumeAlsaPrivate; + +struct AcmeVolumeAlsa { + AcmeVolume parent; + AcmeVolumeAlsaPrivate *_priv; +}; + +struct AcmeVolumeAlsaClass { + AcmeVolumeClass parent; +}; + +GType acme_volume_alsa_get_type (void); + diff --git a/src/sugar/acme-volume.c b/src/sugar/acme-volume.c new file mode 100644 index 0000000..09ae1d2 --- /dev/null +++ b/src/sugar/acme-volume.c @@ -0,0 +1,127 @@ +/* acme-volume.c + + Copyright (C) 2002, 2003 Bastien Nocera + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Bastien Nocera + */ + +#ifdef HAVE_CONFIG +#include "config.h" +#endif +#include "acme-volume.h" +#ifdef HAVE_OSS +#include "acme-volume-oss.h" +#endif +#ifdef HAVE_ALSA +#include "acme-volume-alsa.h" +#endif +#ifdef HAVE_GSTREAMER +#include "acme-volume-gstreamer.h" +#endif + +G_DEFINE_TYPE (AcmeVolume, acme_volume, G_TYPE_OBJECT) + +static void +acme_volume_class_init (AcmeVolumeClass *klass) +{ +} + +static void +acme_volume_init (AcmeVolume *vol) +{ +} + +int +acme_volume_get_volume (AcmeVolume *self) +{ + g_return_val_if_fail (self != NULL, 0); + g_return_val_if_fail (ACME_IS_VOLUME (self), 0); + + return ACME_VOLUME_GET_CLASS (self)->get_volume (self); +} + +void +acme_volume_set_volume (AcmeVolume *self, int val) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (ACME_IS_VOLUME (self)); + + ACME_VOLUME_GET_CLASS (self)->set_volume (self, val); +} + +gboolean +acme_volume_get_mute (AcmeVolume *self) +{ + g_return_val_if_fail (self != NULL, FALSE); + g_return_val_if_fail (ACME_IS_VOLUME (self), FALSE); + + return ACME_VOLUME_GET_CLASS (self)->get_mute (self); +} + +void +acme_volume_set_mute (AcmeVolume *self, gboolean val) +{ + g_return_if_fail (self != NULL); + g_return_if_fail (ACME_IS_VOLUME (self)); + + ACME_VOLUME_GET_CLASS (self)->set_mute (self, val); +} + +void +acme_volume_mute_toggle (AcmeVolume *self) +{ + gboolean muted; + + g_return_if_fail (self != NULL); + g_return_if_fail (ACME_IS_VOLUME (self)); + + muted = ACME_VOLUME_GET_CLASS (self)->get_mute (self); + ACME_VOLUME_GET_CLASS (self)->set_mute (self, !muted); +} + +int +acme_volume_get_threshold (AcmeVolume *self) +{ + g_return_val_if_fail (self != NULL, 0); + g_return_val_if_fail (ACME_IS_VOLUME (self), 0); + + return ACME_VOLUME_GET_CLASS (self)->get_threshold (self); +} + +AcmeVolume *acme_volume_new (void) +{ + AcmeVolume *vol; + +#ifdef HAVE_GSTREAMER + vol = ACME_VOLUME (g_object_new (acme_volume_gstreamer_get_type (), NULL)); + return vol; +#endif +#ifdef HAVE_ALSA + vol = ACME_VOLUME (g_object_new (acme_volume_alsa_get_type (), NULL)); + if (vol != NULL && ACME_VOLUME_ALSA (vol)->_priv != NULL) + return vol; + if (ACME_VOLUME_ALSA (vol)->_priv == NULL) + g_object_unref (vol); +#endif +#ifdef HAVE_OSS + vol = ACME_VOLUME (g_object_new (acme_volume_oss_get_type (), NULL)); + return vol; +#endif + return NULL; +} + diff --git a/src/sugar/acme-volume.h b/src/sugar/acme-volume.h new file mode 100644 index 0000000..ec5ee3d --- /dev/null +++ b/src/sugar/acme-volume.h @@ -0,0 +1,63 @@ +/* acme-volume.h + + Copyright (C) 2002, 2003 Bastien Nocera + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Bastien Nocera + */ + +#ifndef _ACME_VOLUME_H +#define _ACME_VOLUME_H + +#include +#include + +G_BEGIN_DECLS + +#define ACME_TYPE_VOLUME (acme_volume_get_type ()) +#define ACME_VOLUME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ACME_TYPE_VOLUME, AcmeVolume)) +#define ACME_VOLUME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ACME_TYPE_VOLUME, AcmeVolumeClass)) +#define ACME_IS_VOLUME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ACME_TYPE_VOLUME)) +#define ACME_VOLUME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ACME_TYPE_VOLUME, AcmeVolumeClass)) + +typedef struct { + GObject parent; +} AcmeVolume; + +typedef struct { + GObjectClass parent; + + void (* set_volume) (AcmeVolume *self, int val); + int (* get_volume) (AcmeVolume *self); + void (* set_mute) (AcmeVolume *self, gboolean val); + int (* get_mute) (AcmeVolume *self); + int (* get_threshold) (AcmeVolume *self); +} AcmeVolumeClass; + +GType acme_volume_get_type (void); +int acme_volume_get_volume (AcmeVolume *self); +void acme_volume_set_volume (AcmeVolume *self, int val); +gboolean acme_volume_get_mute (AcmeVolume *self); +void acme_volume_set_mute (AcmeVolume *self, + gboolean val); +void acme_volume_mute_toggle (AcmeVolume *self); +int acme_volume_get_threshold (AcmeVolume *self); +AcmeVolume *acme_volume_new (void); + +G_END_DECLS + +#endif /* _ACME_VOLUME_H */ -- cgit v0.9.1