From 78f8fce7f286fd0c71774e2567404ed51f24fef3 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Fri, 26 May 2017 21:51:04 -0700 Subject: *: initial commit of stripped schism stuff Forking schism tracker's IT playback stuff into a little playback library for embedding in demos. --- src/sys/alsa/volume-alsa.c | 237 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 src/sys/alsa/volume-alsa.c (limited to 'src/sys/alsa/volume-alsa.c') diff --git a/src/sys/alsa/volume-alsa.c b/src/sys/alsa/volume-alsa.c new file mode 100644 index 0000000..9f81374 --- /dev/null +++ b/src/sys/alsa/volume-alsa.c @@ -0,0 +1,237 @@ +/* + * Schism Tracker - a cross-platform Impulse Tracker clone + * copyright (c) 2003-2005 Storlek + * copyright (c) 2005-2008 Mrs. Brisby + * copyright (c) 2009 Storlek & Mrs. Brisby + * copyright (c) 2010-2012 Storlek + * URL: http://schismtracker.org/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "headers.h" + +#include "util.h" + +#ifdef USE_ALSA +#include +#include + +static const char *alsa_card_id = "default"; + +/* --------------------------------------------------------------------- */ +#ifdef USE_DLTRICK_ALSA +#include + +/* see midi-alsa for details about how this works */ +#include + +extern void *_dltrick_handle; + +/* don't try this at home... */ +#define _void_dltrick(a,b,c) static void (*_dltrick_ ## a)b = NULL; \ +void a b { if (!_dltrick_##a) _dltrick_##a = dlsym(_dltrick_handle, #a); \ +if (!_dltrick_##a) abort(); _dltrick_ ## a c; } + +#define _any_dltrick(r,a,b,c) static r (*_dltrick_ ## a)b = NULL; \ +r a b { if (!_dltrick_##a) _dltrick_##a = dlsym(_dltrick_handle, #a); \ +if (!_dltrick_##a) abort(); return _dltrick_ ## a c; } + +_any_dltrick(snd_mixer_elem_t*, snd_mixer_find_selem, +(snd_mixer_t*e,const snd_mixer_selem_id_t*sid),(e,sid)) + +_void_dltrick(snd_mixer_selem_id_set_index, +(snd_mixer_selem_id_t*obj,unsigned int val),(obj,val)) + +_void_dltrick(snd_mixer_selem_id_set_name, +(snd_mixer_selem_id_t*obj,const char *val),(obj,val)) + +_any_dltrick(int,snd_mixer_selem_set_playback_volume, +(snd_mixer_elem_t*e,snd_mixer_selem_channel_id_t ch,long v),(e,ch,v)) + +_any_dltrick(int,snd_mixer_selem_is_playback_mono,(snd_mixer_elem_t*e),(e)) +_any_dltrick(int,snd_mixer_selem_has_playback_channel,(snd_mixer_elem_t*e,snd_mixer_selem_channel_id_t c),(e,c)) + +_any_dltrick(int,snd_ctl_card_info,(snd_ctl_t*c,snd_ctl_card_info_t*i),(c,i)) +_any_dltrick(size_t,snd_ctl_card_info_sizeof,(void),()) +_any_dltrick(size_t,snd_mixer_selem_id_sizeof,(void),()) + +_any_dltrick(int,snd_mixer_close,(snd_mixer_t*mm),(mm)) +_any_dltrick(int,snd_mixer_selem_get_playback_volume, +(snd_mixer_elem_t*e,snd_mixer_selem_channel_id_t ch,long*v),(e,ch,v)) +#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR < 10) || (SND_LIB_MAJOR < 1) +_void_dltrick(snd_mixer_selem_get_playback_volume_range, +(snd_mixer_elem_t*e,long*m,long*v),(e,m,v)) +#else +_any_dltrick(int,snd_mixer_selem_get_playback_volume_range, +(snd_mixer_elem_t*e,long*m,long*v),(e,m,v)) +#endif +_any_dltrick(int,snd_ctl_open,(snd_ctl_t**c,const char *name,int mode),(c,name,mode)) +_any_dltrick(int,snd_ctl_close,(snd_ctl_t*ctl),(ctl)) +_any_dltrick(int,snd_mixer_open,(snd_mixer_t**m,int mode),(m,mode)) +_any_dltrick(int,snd_mixer_attach,(snd_mixer_t*m,const char *name),(m,name)) +_any_dltrick(int,snd_mixer_selem_register,(snd_mixer_t*m, + struct snd_mixer_selem_regopt*opt, snd_mixer_class_t **cp),(m,opt,cp)) +_any_dltrick(int,snd_mixer_selem_is_active,(snd_mixer_elem_t*e),(e)) +_any_dltrick(int,snd_mixer_selem_has_playback_volume,(snd_mixer_elem_t*e),(e)) +_any_dltrick(int,snd_mixer_selem_has_capture_switch,(snd_mixer_elem_t*e),(e)) +_any_dltrick(int,snd_mixer_selem_has_capture_switch_joined,(snd_mixer_elem_t*e),(e)) +_any_dltrick(int,snd_mixer_selem_has_capture_switch_exclusive,(snd_mixer_elem_t*e),(e)) + +_any_dltrick(int,snd_mixer_load,(snd_mixer_t*m),(m)) +_any_dltrick(snd_mixer_elem_t*,snd_mixer_first_elem,(snd_mixer_t*m),(m)) +_any_dltrick(snd_mixer_elem_t*,snd_mixer_elem_next,(snd_mixer_elem_t*m),(m)) +_any_dltrick(snd_mixer_elem_type_t,snd_mixer_elem_get_type,(const snd_mixer_elem_t *obj),(obj)) +#endif + +/* alsa is paranoid, so snd_mixer_selem_id_alloca does an assert(&sid), which + * of course will never fail, so gcc complains. this shuts that warning up. */ +#undef assert +#define assert(x) + +/* this _could_ change */ +static int current_alsa_range = 255; + + +static void _alsa_writeout(snd_mixer_elem_t *em, + snd_mixer_selem_channel_id_t d, + int use, int lim) +{ + if (use > lim) use = lim; + (void)snd_mixer_selem_set_playback_volume(em, d, (long)use); +} +static void _alsa_write(snd_mixer_elem_t *em, int *l, int *r, long min, long range) +{ + long al, ar; + long mr, md; + + al = ((*l) * range / current_alsa_range) + min; + ar = ((*r) * range / current_alsa_range) + min; + + mr = min+range; + + if (snd_mixer_selem_is_playback_mono(em)) { + md = ((al) + (ar)) / 2; + + _alsa_writeout(em, SND_MIXER_SCHN_MONO, md, mr); + } else { + _alsa_writeout(em, SND_MIXER_SCHN_FRONT_LEFT, al, mr); + _alsa_writeout(em, SND_MIXER_SCHN_FRONT_RIGHT, ar, mr); +#if 0 +/* this was probably wrong */ + _alsa_writeout(em, SND_MIXER_SCHN_FRONT_CENTER, md, mr); + _alsa_writeout(em, SND_MIXER_SCHN_REAR_LEFT, al, mr); + _alsa_writeout(em, SND_MIXER_SCHN_REAR_RIGHT, ar, mr); + _alsa_writeout(em, SND_MIXER_SCHN_WOOFER, md, mr); +#endif + } +} +static void _alsa_readin(snd_mixer_elem_t *em, snd_mixer_selem_channel_id_t d, + int *aa, long min, long range) +{ + long v; + if (snd_mixer_selem_has_playback_channel(em, d)) { + snd_mixer_selem_get_playback_volume(em, d, &v); + v -= min; + v = (v * current_alsa_range) / range; + (*aa) = v; + } + +} +static void _alsa_config(UNUSED snd_mixer_elem_t *em, UNUSED int *l, UNUSED int *r, UNUSED long min, long range) +{ + current_alsa_range = range; +} + +static void _alsa_read(snd_mixer_elem_t *em, int *l, int *r, long min, long range) +{ + if (snd_mixer_selem_is_playback_mono(em)) { + _alsa_readin(em, SND_MIXER_SCHN_MONO, l, min, range); + _alsa_readin(em, SND_MIXER_SCHN_MONO, r, min, range); + } else { + _alsa_readin(em, SND_MIXER_SCHN_FRONT_LEFT, l, min, range); + _alsa_readin(em, SND_MIXER_SCHN_FRONT_RIGHT, r, min, range); +#if 0 +/* this was probably wrong */ + _alsa_readin(em, SND_MIXER_SCHN_REAR_LEFT, l, min, range); + _alsa_readin(em, SND_MIXER_SCHN_REAR_RIGHT, r, min, range); + _alsa_readin(em, SND_MIXER_SCHN_FRONT_CENTER, l, min, range); + _alsa_readin(em, SND_MIXER_SCHN_FRONT_CENTER, r, min, range); + _alsa_readin(em, SND_MIXER_SCHN_WOOFER, l, min, range); + _alsa_readin(em, SND_MIXER_SCHN_WOOFER, r, min, range); +#endif + } +} + +static void _alsa_doit(void (*busy)(snd_mixer_elem_t *em, + int *, int *, long, long), int *l, int *r) +{ + long ml, mr; + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *em; + snd_mixer_t *mix; + + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, "Master"); + + if (snd_mixer_open(&mix, 0) == 0) { + if (snd_mixer_attach(mix, alsa_card_id) < 0) { + snd_mixer_close(mix); + return; + } + if (snd_mixer_selem_register(mix, NULL, NULL) < 0) { + snd_mixer_close(mix); + return; + } + if (snd_mixer_load(mix) < 0) { + snd_mixer_close(mix); + return; + } + em = snd_mixer_find_selem(mix, sid); + if (em) { + ml = mr = 0; + snd_mixer_selem_get_playback_volume_range(em, &ml, &mr); + if (ml != mr) { + busy(em, l, r, ml, mr - ml); + } + } + snd_mixer_close(mix); + } + +} + + +int alsa_volume_get_max(void); +int alsa_volume_get_max(void) +{ + int a1, a2; + _alsa_doit(_alsa_config, &a1, &a2); + return current_alsa_range; +} + +void alsa_volume_read(int *left, int *right); +void alsa_volume_read(int *left, int *right) +{ + *left = *right = 0; + _alsa_doit(_alsa_read, left, right); +} + +void alsa_volume_write(int left, int right); +void alsa_volume_write(int left, int right) +{ + _alsa_doit(_alsa_write, &left, &right); +} +#endif -- cgit v1.2.3