Mercurial > hg > xemacs-beta
changeset 3308:34cfe24248f6
[xemacs-hg @ 2006-03-27 17:40:45 by james]
Add support for sound using the ALSA library. See xemacs-patches message
with ID <m3slp77hui.fsf@jerrypc.cs.usu.edu>.
author | james |
---|---|
date | Mon, 27 Mar 2006 17:40:59 +0000 |
parents | d81b1754aab1 |
children | ad7894a4ab41 |
files | ChangeLog configure configure.ac src/ChangeLog src/alsaplay.c src/config.h.in src/sound.c |
diffstat | 7 files changed, 734 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Mon Mar 27 15:20:31 2006 +0000 +++ b/ChangeLog Mon Mar 27 17:40:59 2006 +0000 @@ -1,3 +1,7 @@ +2006-03-24 Jerry James <james@xemacs.org> + + * configure.ac: Autodetect ALSA support and report it if detected. + 2006-02-22 Marcus Crestani <crestani@xemacs.org> * configure.ac: Remove mc-alloc, replace it with newgc, adjust
--- a/configure Mon Mar 27 15:20:31 2006 +0000 +++ b/configure Mon Mar 27 17:40:59 2006 +0000 @@ -1090,13 +1090,14 @@ ------------- --with-sound=TYPE Compile with sound support. Valid types are - `native', `nas' and `esd'. Prefix a type with 'no' - to disable. The first type can be `none' or `all'. - `none' means `nonative,nonas,noesd'. `all' means - `native,nas,esd'. Later options override earlier - ones for the same TYPE. The default is to autodetect - all sound support except for ESD which defaults to - off. + `native', `alsa', `nas' and `esd'. Prefix a type + with 'no' to disable. The first type can be `none' + or `all'. `none' means + `nonative,noalsa,nonas,noesd'. `all' means + `native,alsa,nas,esd'. Later options override + earlier ones for the same TYPE. The default is to + autodetect all sound support except for ESD which + defaults to off. --with-native-sound-lib Path to sound library (for systems with name conflicts). @@ -2926,10 +2927,11 @@ _sound_notfirst="" _sound_native_default="" +_sound_alsa_default="" _sound_nas_default="" _sound_esd_default=no -_sound_types="native nas esd" -_sound_default="native,nas,noesd" +_sound_types="native alsa nas esd" +_sound_default="native,alsa,nas,noesd" # If --with-sound or --without-sound were given then copy the value to the # equivalent enable_sound variable. @@ -34968,9 +34970,9 @@ - { echo "$as_me:$LINENO: checking for sound support..." >&5 echo "$as_me: checking for sound support..." >&6;} + test -n "$with_native_sound_lib" && enable_sound_native=yes if test "$enable_sound_native" != "no"; then @@ -35581,6 +35583,234 @@ test -n "$with_native_sound_lib" && LIBS="$with_native_sound_lib $LIBS" && if test "$verbose" = "yes"; then echo " Prepending \"$with_native_sound_lib\" to \$LIBS"; fi fi +if test "$enable_sound_alsa" != "no"; then + if test "${ac_cv_header_alsa_pcm_h+set}" = set; then + echo "$as_me:$LINENO: checking for alsa/pcm.h" >&5 +echo $ECHO_N "checking for alsa/pcm.h... $ECHO_C" >&6 +if test "${ac_cv_header_alsa_pcm_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_alsa_pcm_h" >&5 +echo "${ECHO_T}$ac_cv_header_alsa_pcm_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking alsa/pcm.h usability" >&5 +echo $ECHO_N "checking alsa/pcm.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <alsa/pcm.h> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking alsa/pcm.h presence" >&5 +echo $ECHO_N "checking alsa/pcm.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <alsa/pcm.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: alsa/pcm.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: alsa/pcm.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: alsa/pcm.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: alsa/pcm.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: alsa/pcm.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: alsa/pcm.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: alsa/pcm.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: alsa/pcm.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: alsa/pcm.h: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------- ## +## Report this to xemacs-beta@xemacs.org ## +## ------------------------------------- ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for alsa/pcm.h" >&5 +echo $ECHO_N "checking for alsa/pcm.h... $ECHO_C" >&6 +if test "${ac_cv_header_alsa_pcm_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_alsa_pcm_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_alsa_pcm_h" >&5 +echo "${ECHO_T}$ac_cv_header_alsa_pcm_h" >&6 + +fi +if test $ac_cv_header_alsa_pcm_h = yes; then + + echo "$as_me:$LINENO: checking for snd_pcm_open in -lasound" >&5 +echo $ECHO_N "checking for snd_pcm_open in -lasound... $ECHO_C" >&6 +if test "${ac_cv_lib_asound_snd_pcm_open+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lasound $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char snd_pcm_open (); +int +main () +{ +snd_pcm_open (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_asound_snd_pcm_open=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_asound_snd_pcm_open=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_asound_snd_pcm_open" >&5 +echo "${ECHO_T}$ac_cv_lib_asound_snd_pcm_open" >&6 +if test $ac_cv_lib_asound_snd_pcm_open = yes; then + have_alsa_sound=yes +fi + +fi + + + if test "$have_alsa_sound" = "yes"; then + enable_sound_alsa=yes + cat >>confdefs.h <<\_ACEOF +#define HAVE_ALSA_SOUND 1 +_ACEOF + + extra_objs="$extra_objs alsaplay.o" && if test "$verbose" = "yes"; then + echo " xemacs will be linked with \"alsaplay.o\"" + fi + LIBS="-lasound $LIBS" && if test "$verbose" = "yes"; then echo " Prepending \"-lasound\" to \$LIBS"; fi + else + test "$enable_sound_alsa" = "yes" && \ + { echo "Error:" "Required ALSA sound support cannot be provided." >&2; exit 1; } + enable_sound_alsa=no + fi +fi + if test "$enable_sound_nas" != "no"; then if test "${ac_cv_header_audio_audiolib_h+set}" = set; then echo "$as_me:$LINENO: checking for audio/audiolib.h" >&5 @@ -39401,6 +39631,7 @@ echo " Sound:" test "$enable_sound_native" = yes && echo " Compiling in support for sound (native)." +test "$enable_sound_alsa" = yes && echo " Compiling in support for ALSA (Advanced Linux Sound Architecture)." test "$enable_sound_nas" = yes && echo " Compiling in support for NAS (network audio system)." test "$old_nas" = yes && echo " - NAS library lacks error trapping; will play synchronously." test "$enable_sound_esd" = yes && echo " Compiling in support for ESD (Enlightened Sound Daemon)."
--- a/configure.ac Mon Mar 27 15:20:31 2006 +0000 +++ b/configure.ac Mon Mar 27 17:40:59 2006 +0000 @@ -791,16 +791,18 @@ XE_HELP_SUBSECTION([Sound options]) XE_COMPLEX_ARG([sound], AC_HELP_STRING([--enable-sound=TYPE],[Compile with sound support. - Valid types are `native', `nas' and `esd'. + Valid types are `native', `alsa', `nas' and `esd'. Prefix a type with 'no' to disable. The first type can be `none' or `all'. `none' means - `nonative,nonas,noesd'. `all' means `native,nas,esd'. - Later options override earlier ones for the same TYPE. - The default is to autodetect all sound support except - for ESD which defaults to off.]), + `nonative,noalsa,nonas,noesd'. `all' means + `native,alsa,nas,esd'. Later options override earlier + ones for the same TYPE. The default is to autodetect + all sound support except for ESD which defaults to + off.]), [], [enable_sound_nas=""], [XE_COMPLEX_OPTION([native],[""]), + XE_COMPLEX_OPTION([alsa],[""]), XE_COMPLEX_OPTION([nas],[""]), XE_COMPLEX_OPTION([esd],[no])]) XE_MERGED_ARG([native-sound-lib], @@ -5006,9 +5008,9 @@ AC_CHECK_HEADER(nlist.h, AC_DEFINE(NLIST_STRUCT), ) dnl Check for sound of various sorts. +AC_CHECKING([for sound support]) dnl Autodetect native sound -AC_CHECKING([for sound support]) test -n "$with_native_sound_lib" && enable_sound_native=yes if test "$enable_sound_native" != "no"; then @@ -5127,6 +5129,22 @@ test -n "$with_native_sound_lib" && XE_PREPEND($with_native_sound_lib, LIBS) fi +dnl ALSA sound support +if test "$enable_sound_alsa" != "no"; then + AC_CHECK_HEADER([alsa/pcm.h], [ + AC_CHECK_LIB(asound, snd_pcm_open, have_alsa_sound=yes)]) + if test "$have_alsa_sound" = "yes"; then + enable_sound_alsa=yes + AC_DEFINE(HAVE_ALSA_SOUND) + XE_ADD_OBJS(alsaplay.o) + XE_PREPEND(-lasound, LIBS) + else + test "$enable_sound_alsa" = "yes" && \ + XE_DIE("Required ALSA sound support cannot be provided.") + enable_sound_alsa=no + fi +fi + dnl NAS Sound support if test "$enable_sound_nas" != "no"; then AC_CHECK_HEADER(audio/audiolib.h, [ @@ -6053,6 +6071,7 @@ echo " Sound:" test "$enable_sound_native" = yes && echo " Compiling in support for sound (native)." +test "$enable_sound_alsa" = yes && echo " Compiling in support for ALSA (Advanced Linux Sound Architecture)." test "$enable_sound_nas" = yes && echo " Compiling in support for NAS (network audio system)." test "$old_nas" = yes && echo " - NAS library lacks error trapping; will play synchronously." test "$enable_sound_esd" = yes && echo " Compiling in support for ESD (Enlightened Sound Daemon)."
--- a/src/ChangeLog Mon Mar 27 15:20:31 2006 +0000 +++ b/src/ChangeLog Mon Mar 27 17:40:59 2006 +0000 @@ -1,3 +1,9 @@ +2006-03-24 Jerry James <james@xemacs.org> + + * alsaplay.c: New file providing support for sound with ALSA. + * config.h.in: New symbol HAVE_ALSA_SOUND + * sound.c: Add ALSA support. + 2006-03-27 Mike Fabian <mfabian@suse.de> * vdb-posix.c (vdb_install_signal_handler): Correct memset.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/alsaplay.c Mon Mar 27 17:40:59 2006 +0000 @@ -0,0 +1,404 @@ +/* Play sound with the ALSA library + Copyright (C) 2006 Jerry James. + +This file is part of XEmacs. + +XEmacs 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, or (at your option) any +later version. + +XEmacs 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 XEmacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* Synched up with: Not in FSF. */ + +/* TODO: Support asynchronous sound playing; see the NAS support in sound.c */ + +#include <config.h> +#include "lisp.h" +#include "sound.h" +#include "sysfile.h" + +/* We can't just include <alsa/asoundlib.h> because it tries to redefine + * several symbols defined by the previous header files. + */ +#include <alsa/input.h> +#include <alsa/output.h> +#include <alsa/conf.h> +#include <alsa/global.h> +#include <alsa/pcm.h> +#include <alsa/error.h> +#include <alsa/hwdep.h> +#include <alsa/rawmidi.h> +#include <alsa/control.h> +#include <alsa/mixer.h> + +struct mixer_state +{ + snd_mixer_t *mixer; + snd_mixer_elem_t *vol_ctl; + + /* Which channels need the old volume restored */ + int reset_front_left; + int reset_front_center; + int reset_front_right; + int reset_rear_left; + int reset_rear_center; + int reset_rear_right; + int reset_side_left; + int reset_side_right; + int reset_woofer; + + /* Old volumes for the channels */ + long front_left_vol; + long front_center_vol; + long front_right_vol; + long rear_left_vol; + long rear_center_vol; + long rear_right_vol; + long side_left_vol; + long side_right_vol; + long woofer_vol; +}; + +/* Assemble a machine half-word in little-endian order */ +#define HALF_LE(arr,start) (arr[start] + (arr[start + 1] << 8)) + +/* Assemble a machine word in little-endian order */ +#define WORD_LE(arr,start) (arr[start] + (arr[start + 1] << 8) + \ + (arr[start + 2] << 16) + (arr[start + 3] << 24)) + +/* Assemble a machine word in big-endian order */ +#define WORD_BE(arr,start) ((arr[start] << 24) + (arr[start + 1] << 16) + \ + (arr[start + 2] << 8) + arr[start + 3]) + +/* This function was inspired by miscplay.c. + * Examine sound data to determine its format. + * + * TODO: Detect other formats that ALSA can play, such as GSM and MPEG. + */ +static snd_pcm_format_t +analyze_format (const Binbyte *format, int *speed, int *tracks) +{ + if (!memcmp (format, "Creative Voice File\x1A\x1A\x00", 22) && + HALF_LE (format, 22) == ((0x1233 - HALF_LE (format, 24)) & 0xFFFF)) + { + /* VOC */ + *speed = 8000; + *tracks = 2; + return SND_PCM_FORMAT_U8; + } + else if (!memcmp (format, "RIFF", 4) && !memcmp (format + 8, "WAVEfmt ", 8)) + { + /* WAVE */ + *speed = WORD_LE (format, 24); + *tracks = format[22]; + return format[32] / format[22] == 1 + ? SND_PCM_FORMAT_U8 + : SND_PCM_FORMAT_S16_LE; + } + else if (!memcmp (format, ".snd", 4)) + { + /* Sun/NeXT Audio (big endian) */ + if (WORD_BE (format, 4) < 24) + { + *speed = 8000; + *tracks = 1; + return SND_PCM_FORMAT_MU_LAW; + } + *speed = WORD_BE (format, 16); + *tracks = format[23]; + if (!memcmp (format + 12, "\000\000\000", 3)) + { + switch (format[15]) + { + case 1: + case 17: + case 29: + return SND_PCM_FORMAT_MU_LAW; + case 2: + return SND_PCM_FORMAT_S8; + case 3: + return SND_PCM_FORMAT_S16_BE; + case 4: + return SND_PCM_FORMAT_S24_BE; + case 5: + return SND_PCM_FORMAT_S32_BE; + case 23: + case 24: + case 25: + case 26: + return SND_PCM_FORMAT_IMA_ADPCM; + case 27: + return SND_PCM_FORMAT_A_LAW; + default: + break; + } + } + return SND_PCM_FORMAT_UNKNOWN; + } + else if (!memcmp (format, ".sd", 4)) + { + /* DEC Audio (little endian) */ + if (WORD_LE (format, 4) < 24) + { + *speed = 8000; + *tracks = 1; + return SND_PCM_FORMAT_MU_LAW; + } + *speed = WORD_LE (format, 16); + *tracks = format[20]; + if (!memcmp (format + 13, "\000\000\000", 3)) + { + switch (format[12]) + { + case 1: + case 17: + case 29: + return SND_PCM_FORMAT_MU_LAW; + case 2: + return SND_PCM_FORMAT_S8; + case 3: + return SND_PCM_FORMAT_S16_LE; + case 4: + return SND_PCM_FORMAT_S24_LE; + case 5: + return SND_PCM_FORMAT_S32_LE; + case 23: + case 24: + case 25: + case 26: + return SND_PCM_FORMAT_IMA_ADPCM; + case 27: + return SND_PCM_FORMAT_A_LAW; + default: + break; + } + } + return SND_PCM_FORMAT_UNKNOWN; + } + else + { + /* We don't know what it is. Guess that it is mono audio in unsigned + * byte format. Maybe we should error if we reach this point. + */ + *speed = 8000; + *tracks = 1; + return SND_PCM_FORMAT_U8; + } +} + +/* Set the volume: if any errors occur, we accept the existing volume */ +static void +set_volume (struct mixer_state *mix, int volume) +{ + snd_mixer_selem_id_t *volume_id; + long min_vol, max_vol, dev_vol; + + if (snd_mixer_open (&mix->mixer, 0) < 0) + return; + + if (snd_mixer_attach (mix->mixer, "default") < 0) + return; + + if (snd_mixer_selem_register (mix->mixer, NULL, NULL) < 0) + return; + + if (snd_mixer_load (mix->mixer) < 0) + return; + + snd_mixer_selem_id_alloca (&volume_id); + snd_mixer_selem_id_set_name (volume_id, "PCM"); + + if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) == NULL) + { + snd_mixer_selem_id_set_name (volume_id, "Master"); + if ((mix->vol_ctl = snd_mixer_find_selem (mix->mixer, volume_id)) + == NULL) + return; + } + + /* Translate the Lisp volume range to the device volume range */ + if (snd_mixer_selem_get_playback_volume_range (mix->vol_ctl, &min_vol, + &max_vol) < 0) + return; + + dev_vol = volume * (max_vol - min_vol) / 100 + min_vol; + + /* Record the old volumes */ + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, &mix->front_left_vol) >= 0) + mix->reset_front_left = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, &mix->front_center_vol) >= 0) + mix->reset_front_center = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, &mix->front_right_vol) >= 0) + mix->reset_front_right = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, &mix->rear_left_vol) >= 0) + mix->reset_rear_left = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, &mix->rear_center_vol) >= 0) + mix->reset_rear_center = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, &mix->rear_right_vol) >= 0) + mix->reset_rear_right = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, &mix->side_left_vol) >= 0) + mix->reset_side_left = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, &mix->side_right_vol) >= 0) + mix->reset_side_right = 1; + + if (snd_mixer_selem_get_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, &mix->woofer_vol) >= 0) + mix->reset_woofer = 1; + + /* Set the volume */ + snd_mixer_selem_set_playback_volume_all (mix->vol_ctl, dev_vol); +} + +static void +reset_volume (const struct mixer_state *mix) +{ + if (mix->reset_front_left) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_FRONT_LEFT, mix->front_left_vol); + + if (mix->reset_front_center) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_FRONT_CENTER, mix->front_center_vol); + + if (mix->reset_front_right) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_FRONT_RIGHT, mix->front_right_vol); + + if (mix->reset_rear_left) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_REAR_LEFT, mix->rear_left_vol); + + if (mix->reset_rear_center) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_REAR_CENTER, mix->rear_center_vol); + + if (mix->reset_rear_right) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_REAR_RIGHT, mix->rear_right_vol); + + if (mix->reset_side_left) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_SIDE_LEFT, mix->side_left_vol); + + if (mix->reset_side_right) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_SIDE_RIGHT, mix->side_right_vol); + + if (mix->reset_woofer) + snd_mixer_selem_set_playback_volume + (mix->vol_ctl, SND_MIXER_SCHN_WOOFER, mix->woofer_vol); + + snd_mixer_close (mix->mixer); +} + +int +alsa_play_sound_data (const Binbyte *data, int length, int volume) +{ + snd_pcm_t *pcm_handle; + snd_pcm_hw_params_t *hwparams; + snd_pcm_format_t format; + struct mixer_state mix; + int speed, tracks, err; + + /* Set the PCM parameters */ + if ((err = snd_pcm_open (&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK, + 0)) < 0) + goto error_pcm_open; + + snd_pcm_hw_params_alloca (&hwparams); + + if ((err = snd_pcm_hw_params_any (pcm_handle, hwparams)) < 0) + goto error_pcm; + + format = analyze_format (data, &speed, &tracks); + + if ((err = snd_pcm_hw_params_set_access (pcm_handle, hwparams, + SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + goto error_pcm; + + if ((err = snd_pcm_hw_params_set_format (pcm_handle, hwparams, format)) < 0) + goto error_pcm; + + if ((err = snd_pcm_hw_params_set_rate (pcm_handle, hwparams, speed, 0)) < 0) + goto error_pcm; + + if ((err = snd_pcm_hw_params_set_channels (pcm_handle, hwparams, tracks)) + < 0) + goto error_pcm; + + if ((err = snd_pcm_hw_params (pcm_handle, hwparams)) < 0) + goto error_pcm; + + /* Set the volume */ + memset (&mix, 0, sizeof (mix)); + set_volume (&mix, volume); + + /* Play the sound */ + if ((err = snd_pcm_writei (pcm_handle, data, length)) < 0) + goto error_mixer; + + /* Put the volume back the way it used to be */ + reset_volume (&mix); + + /* Release resources */ + snd_pcm_close (pcm_handle); + return 1; + + error_mixer: + reset_volume (&mix); + error_pcm: + snd_pcm_close (pcm_handle); + error_pcm_open: + sound_perror (snd_strerror (err)); + return 0; +} + +/* Read the sound file into an internal buffer, then call + * alsa_play_sound_data. + */ +int +alsa_play_sound_file (const Extbyte *sound_file, int volume) +{ + Binbyte *data; + int fd, retval; + struct stat st; + + fd = retry_open (sound_file, O_RDONLY, 0); + if (fd < 0) { + sound_perror (sound_file); + return 0; + } + + qxe_fstat (fd, &st); + data = xnew_array (Binbyte, st.st_size); + retry_read (fd, data, st.st_size); + retry_close (fd); + retval = alsa_play_sound_data (data, st.st_size, volume); + xfree (data, Binbyte); + return retval; +}
--- a/src/config.h.in Mon Mar 27 15:20:31 2006 +0000 +++ b/src/config.h.in Mon Mar 27 17:40:59 2006 +0000 @@ -796,6 +796,9 @@ /* Native sound may be provided via soundcard.h, in various directories */ #undef SOUNDCARD_H_FILE +/* Compile in support for ALSA (Advanced Linux Sound Architecture) */ +#undef HAVE_ALSA_SOUND + /* Compile in support for NAS (Network Audio System)? NAS_NO_ERROR_JUMP means that the NAS libraries don't include some error handling changes. */
--- a/src/sound.c Mon Mar 27 15:20:31 2006 +0000 +++ b/src/sound.c Mon Mar 27 17:40:59 2006 +0000 @@ -70,12 +70,25 @@ #define USED_IF_HAVE_ANY(decl) UNUSED (decl) #endif +#ifdef HAVE_ALSA_SOUND +extern int alsa_play_sound_file (const Extbyte *file, int vol); +extern int alsa_play_sound_data (const Binbyte *data, int length, int vol); +# define DEVICE_CONNECTED_TO_ALSA_P(x) 1 /* #### better check */ +#endif + #ifdef HAVE_ESD_SOUND extern int esd_play_sound_file (Extbyte *file, int vol); extern int esd_play_sound_data (Binbyte *data, size_t length, int vol); # define DEVICE_CONNECTED_TO_ESD_P(x) 1 /* #### better check */ #endif +#ifdef HAVE_NAS_SOUND +extern int nas_play_sound_file (Extbyte *name, int volume); +extern int nas_play_sound_data (Binbyte *data, int length, int volume); +extern int nas_wait_for_sounds (void); +extern Extbyte *nas_init_play (Display *); +#endif + Fixnum bell_volume; Fixnum bell_inhibit_time; Lisp_Object Vsound_alist; @@ -84,14 +97,6 @@ Lisp_Object Q_volume, Q_pitch, Q_duration, Q_sound; Lisp_Object Qsound_error; - -#ifdef HAVE_NAS_SOUND -extern int nas_play_sound_file (Extbyte *name, int volume); -extern int nas_play_sound_data (Binbyte *data, int length, int volume); -extern int nas_wait_for_sounds (void); -extern Extbyte *nas_init_play (Display *); -#endif - DOESNT_RETURN report_sound_error (const Ascbyte *string, Lisp_Object data) { @@ -110,8 +115,8 @@ { /* This function can call lisp */ int vol; -#if defined (HAVE_NATIVE_SOUND) || defined (HAVE_NAS_SOUND) \ - || defined (HAVE_ESD_SOUND) +#if defined (HAVE_NATIVE_SOUND) || defined (HAVE_ALSA_SOUND) || \ + defined (HAVE_NAS_SOUND) || defined (HAVE_ESD_SOUND) struct device *d = decode_device (device); #endif struct gcpro gcpro1; @@ -148,6 +153,18 @@ } UNGCPRO; +#ifdef HAVE_ALSA_SOUND + if (DEVICE_CONNECTED_TO_ALSA_P (d)) + { + Extbyte *fileext; + + LISP_STRING_TO_EXTERNAL (file, fileext, Qfile_name); + /* #### ALSA code should allow specification of a device. */ + if (alsa_play_sound_file (fileext, vol)) + return Qnil; + } +#endif + #ifdef HAVE_NAS_SOUND if (DEVICE_CONNECTED_TO_NAS_P (d)) { @@ -355,10 +372,24 @@ pit = (INT_OR_FLOATP (pitch) ? (int) XFLOATINT (pitch) : -1); dur = (INT_OR_FLOATP (duration) ? (int) XFLOATINT (duration) : -1); - /* If the sound is a string, and we're connected to Nas, do that. - Else if the sound is a string, and we're on console, play it natively. - Else just beep. + /* If the sound is a string, and we're connected to ALSA, NAS, or ESD, do + that. Else if the sound is a string, and we're on console, play it + natively. Else just beep. */ +#ifdef HAVE_ALSA_SOUND + if (DEVICE_CONNECTED_TO_ALSA_P (d) && STRINGP (sound)) + { + Binbyte *soundext; + Bytecount soundextlen; + + TO_EXTERNAL_FORMAT (LISP_STRING, sound, + ALLOCA, (soundext, soundextlen), + Qbinary); + if (alsa_play_sound_data (soundext, soundextlen, vol)) + return Qnil; + } +#endif /* HAVE_ALSA_SOUND */ + #ifdef HAVE_NAS_SOUND if (DEVICE_CONNECTED_TO_NAS_P (d) && STRINGP (sound)) { @@ -423,6 +454,10 @@ */ (USED_IF_HAVE_NATIVE_OR_NAS (device))) { +#ifdef HAVE_ALSA_SOUND + if (DEVICE_CONNECTED_TO_ALSA_P (decode_device (device))) + return Qt; +#endif #ifdef HAVE_NAS_SOUND if (DEVICE_CONNECTED_TO_NAS_P (decode_device (device))) return Qt; @@ -654,6 +689,9 @@ void vars_of_sound (void) { +#ifdef HAVE_ALSA_SOUND + Fprovide (intern ("alsa-sound")); +#endif #ifdef HAVE_NATIVE_SOUND Fprovide (intern ("native-sound")); #endif