Sound has never worked correctly for me since moving to a Linux 2.6
kernel. Maybe I just have an ultra-cheap sound card. In any case, the
existing linuxplay.c file doesn't do what my system wants. I wind up
with distorted sound, or no sound at all, and lots of warnings that too
few bytes were written to the sound device.
Here's a quick attempt by me to add support for ALSA. It isn't
complete. I'm posting it here to ask for comments and testers. This
patch "works", in the sense that I can successfully build with it, and
then I can play all of the sounds that come with Gnus. I don't yet know
what to do with the volume argument to play-sound and play-sound-file,
and the way I manage sound files is probably horrible in some way.
Furthermore, I'm not sure if the configure magic is right; does the
existence of the ALSA library imply that it should be used?
Put the attached file, alsaplay.c, in src/ and apply this patch to try
it out.
Index: configure.ac
===================================================================
RCS file: /pack/xemacscvs/XEmacs/xemacs/configure.ac,v
retrieving revision 1.35
diff -d -u -r1.35 configure.ac
--- configure.ac 2006/02/27 16:29:06 1.35
+++ configure.ac 2006/03/20 21:44:37
@@ -5079,6 +5079,16 @@
esac
fi
+ dnl Check for ALSA sound support
+ if test -z "$sound_found"; then
+ AC_CHECK_HEADER([alsa/pcm.h], [
+ sound_found=yes
+ AC_CHECK_LIB(asound, snd_pcm_open,
+ [with_native_sound_lib=-lasound
+ XE_ADD_OBJS(alsaplay.o)],
+ [sound_found=no])])
+ fi
+
dnl Check for Linux/BSD native sound (also on recent Cygwins)
if test -z "$sound_found"; then
for dir in "machine" "sys" "linux"; do
/* 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. */
#include <config.h>
#include "lisp.h"
#include "sound.h"
#include "sysfile.h"
#include <alsa/input.h>
#include <alsa/output.h>
#include <alsa/conf.h>
#include <alsa/global.h>
#include <alsa/pcm.h>
#include <alsa/error.h>
#define WORD_LE(arr,start) (arr[start] + (arr[start + 1] << 8) + \
(arr[start + 2] << 16) + (arr[start + 3] << 24))
#define WORD_BE(arr,start) ((arr[start] << 24) + (arr[start + 1] << 16) + \
(arr[start + 2] << 8) + arr[start + 3])
/* This function was largely stolen from miscplay.c. */
static snd_pcm_format_t
analyze_format (Binbyte *format, int *speed, int *tracks)
{
/* Keep compatibility with Linux 68k, etc. by not relying on byte-sex */
if (!memcmp (format, "Creative Voice File\x1A\x1A\x00", 22) &&
(format[22] + (format[23] << 8)) ==
((0x1233 - format[24] - (format[25] << 8)) & 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
{
*speed = 8000;
*tracks = 1;
return SND_PCM_FORMAT_U8;
}
}
int
play_sound_data (Binbyte *data, int length, int volume)
{
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *hwparams;
snd_pcm_format_t format;
int speed, tracks, err;
if ((err = snd_pcm_open (&pcm_handle, "default", SND_PCM_STREAM_PLAYBACK,
0)) < 0)
goto error_early;
snd_pcm_hw_params_alloca (&hwparams);
if ((err = snd_pcm_hw_params_any (pcm_handle, hwparams)) < 0)
goto error;
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;
if ((err = snd_pcm_hw_params_set_format (pcm_handle, hwparams, format)) < 0)
goto error;
if ((err = snd_pcm_hw_params_set_rate (pcm_handle, hwparams, speed, 0)) < 0)
goto error;
if ((err = snd_pcm_hw_params_set_channels (pcm_handle, hwparams, tracks))
< 0)
goto error;
if ((err = snd_pcm_hw_params (pcm_handle, hwparams)) < 0)
goto error;
if ((err = snd_pcm_prepare (pcm_handle)) < 0)
goto error;
if ((err = snd_pcm_writei (pcm_handle, data, length)) < 0)
goto error;
snd_pcm_close (pcm_handle);
return 1;
error:
snd_pcm_close (pcm_handle);
error_early:
sound_perror (snd_strerror (err));
return 0;
}
void
play_sound_file (Extbyte *sound_file, int volume)
{
Binbyte *data;
int fd;
struct stat st;
fd = open (sound_file, O_RDONLY, 0);
if (fd < 0) {
sound_perror (sound_file);
return;
}
qxe_fstat (fd, &st);
data = xnew_array (Binbyte, st.st_size);
read (fd, data, st.st_size);
play_sound_data (data, st.st_size, volume);
xfree (data, Binbyte);
}
Regards,
--
Jerry James, Assistant Professor james(a)xemacs.org
Computer Science Department
http://www.cs.usu.edu/~jerry/
Utah State University