From be014cc2704180693b5c90ad539ca033d50a41b2 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 --- (limited to 'src/sugar/acme-volume-alsa.c') 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; +} + -- cgit v0.9.1