From fa198d11f32b788ec406de1845e7c80a2ab06095 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 16 May 2000 05:40:02 +0000 Subject: [PATCH] alsa sound support for linux. added now in anticipation of autoconf --- source/snd_alsa.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 source/snd_alsa.c diff --git a/source/snd_alsa.c b/source/snd_alsa.c new file mode 100644 index 0000000..73cd314 --- /dev/null +++ b/source/snd_alsa.c @@ -0,0 +1,319 @@ +/* + snd_alsa.c + + (description) + + Copyright (C) 1999,2000 contributors of the QuakeForge project + Please see the file "AUTHORS" for a list of contributors + + 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: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + + $Id$ +*/ + +#include +#include +#include +#include +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_MMAN_H +# include +#endif +#if defined HAVE_SYS_SOUNDCARD_H +# include +#elif defined HAVE_LINUX_SOUNDCARD_H +# include +#elif HAVE_MACHINE_SOUNDCARD_H +# include +#endif + +#include + +#ifndef MAP_FAILED +# define MAP_FAILED ((void*)-1) +#endif + +static int snd_inited; + +static snd_pcm_t *pcm_handle; +static struct snd_pcm_channel_info cinfo; +static struct snd_pcm_channel_params params; +static struct snd_pcm_channel_setup setup; +static snd_pcm_mmap_control_t *mmap_control = NULL; +static char *mmap_data = NULL; +static int card=-1,dev=-1; + +int check_card(int card) +{ + snd_ctl_t *handle; + snd_ctl_hw_info_t info; + int rc; + + if ((rc = snd_ctl_open(&handle, card)) < 0) { + Con_Printf("Error: control open (%i): %s\n", card, snd_strerror(rc)); + return rc; + } + if ((rc = snd_ctl_hw_info(handle, &info)) < 0) { + Con_Printf("Error: control hardware info (%i): %s\n", card, + snd_strerror(rc)); + snd_ctl_close(handle); + return rc; + } + snd_ctl_close(handle); + if (dev==-1) { + for (dev = 0; dev < info.pcmdevs; dev++) { + if ((rc=snd_pcm_open(&pcm_handle,card,dev, + SND_PCM_OPEN_PLAYBACK + | SND_PCM_OPEN_NONBLOCK))==0) { + return 0; + } + } + } else { + if (dev>=0 && dev =2) { + stereo=1; + } else { + stereo=0; + frag_size/=2; + } + +// err_msg="audio flush"; +// if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) +// goto error; + err_msg="audio munmap"; + if ((rc=snd_pcm_munmap(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) + goto error; + + memset(¶ms, 0, sizeof(params)); + params.channel = SND_PCM_CHANNEL_PLAYBACK; + params.mode = SND_PCM_MODE_BLOCK; + params.format.interleave=1; + params.format.format=format; + params.format.rate=rate; + params.format.voices=stereo+1; + params.start_mode = SND_PCM_START_GO; + params.stop_mode = SND_PCM_STOP_ROLLOVER; + params.buf.block.frag_size=frag_size; + params.buf.block.frags_min=1; + params.buf.block.frags_max=-1; +// err_msg="audio flush"; +// if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) +// goto error; + err_msg="audio params"; + if ((rc=snd_pcm_channel_params(pcm_handle, ¶ms))<0) + goto error; + + err_msg="audio mmap"; + if ((rc=snd_pcm_mmap(pcm_handle, SND_PCM_CHANNEL_PLAYBACK, &mmap_control, (void **)&mmap_data))<0) + goto error; + err_msg="audio prepare"; + if ((rc=snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) + goto error; + + memset(&setup, 0, sizeof(setup)); + setup.mode = SND_PCM_MODE_BLOCK; + setup.channel = SND_PCM_CHANNEL_PLAYBACK; + err_msg="audio setup"; + if ((rc=snd_pcm_channel_setup(pcm_handle, &setup))<0) + goto error; + + shm=&sn; + memset((dma_t*)shm,0,sizeof(*shm)); + shm->splitbuffer = 0; + shm->channels=setup.format.voices; + shm->submission_chunk=128; // don't mix less than this # + shm->samplepos=0; // in mono samples + shm->samplebits=setup.format.format==SND_PCM_SFMT_S16_LE?16:8; + shm->samples=setup.buf.block.frags*setup.buf.block.frag_size/(shm->samplebits/8); // mono samples in buffer + shm->speed=setup.format.rate; + shm->buffer=(unsigned char*)mmap_data; + Con_Printf("%5d stereo\n", shm->channels - 1); + Con_Printf("%5d samples\n", shm->samples); + Con_Printf("%5d samplepos\n", shm->samplepos); + Con_Printf("%5d samplebits\n", shm->samplebits); + Con_Printf("%5d submission_chunk\n", shm->submission_chunk); + Con_Printf("%5d speed\n", shm->speed); + Con_Printf("0x%x dma buffer\n", (int)shm->buffer); + Con_Printf("%5d total_channels\n", total_channels); + + snd_inited=1; + return 1; + error: + Con_Printf("Error: %s: %s\n", err_msg, snd_strerror(rc)); + error_2: + snd_pcm_close(pcm_handle); + return 0; +} + +int SNDDMA_GetDMAPos(void) +{ + if (!snd_inited) return 0; + shm->samplepos=(mmap_control->status.frag_io+1)*setup.buf.block.frag_size/(shm->samplebits/8); + return shm->samplepos; +} + +void SNDDMA_Shutdown(void) +{ + if (snd_inited) + { + snd_pcm_close(pcm_handle); + snd_inited = 0; + } +} + +/* +============== +SNDDMA_Submit + +Send sound to device if buffer isn't really the dma buffer +=============== +*/ +void SNDDMA_Submit(void) +{ + int count=paintedtime-soundtime; + int i,s,e; + int rc; + + count+=setup.buf.block.frag_size-1; + count/=setup.buf.block.frag_size; + s=soundtime/setup.buf.block.frag_size; + e=s+count; + for (i=s; ifragments[i % setup.buf.block.frags].data=1; + switch (mmap_control->status.status) { + case SND_PCM_STATUS_PREPARED: + if ((rc=snd_pcm_channel_go(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) { + fprintf(stderr, "unable to start playback. %s\n", + snd_strerror(rc)); + exit(1); + } + break; + case SND_PCM_STATUS_RUNNING: + break; + case SND_PCM_STATUS_UNDERRUN: + if ((rc=snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) { + fprintf(stderr, "underrun: playback channel prepare error. %s\n", + snd_strerror(rc)); + exit(1); + } + break; + default: + break; + } +} +