diff --git a/configure.in b/configure.in index ba418e0ad..8315acdc6 100644 --- a/configure.in +++ b/configure.in @@ -1395,6 +1395,7 @@ AC_OUTPUT( libs/Makefile libs/audio/Makefile libs/audio/cd/Makefile + libs/audio/targets/Makefile libs/gamecode/Makefile libs/util/Makefile qw/include/Makefile diff --git a/include/QF/sound.h b/include/QF/sound.h index 228695383..ad316c65e 100644 --- a/include/QF/sound.h +++ b/include/QF/sound.h @@ -34,6 +34,13 @@ #include "QF/cvar.h" #include "QF/zone.h" +#define AMBIENT_WATER 0 +#define AMBIENT_SKY 1 +#define AMBIENT_SLIME 2 +#define AMBIENT_LAVA 3 + +#define NUM_AMBIENTS 4 // automatic ambient sounds + // !!! if this is changed, it must be changed in asm_i386.h too !!! typedef struct { diff --git a/libs/audio/Makefile.am b/libs/audio/Makefile.am index 444dfd629..0de51f366 100644 --- a/libs/audio/Makefile.am +++ b/libs/audio/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS= cd +SUBDIRS= cd targets clean-local: rm -f *.a diff --git a/libs/audio/targets/Makefile.am b/libs/audio/targets/Makefile.am new file mode 100644 index 000000000..a70feb96f --- /dev/null +++ b/libs/audio/targets/Makefile.am @@ -0,0 +1,41 @@ +lib_LTLIBRARIES = libQFsound.la + +libQFsound_la_LDFLAGS = -version-info 1:0:0 $(CD_LIBS) + +#if ASM_ARCH +sound_ASM= snd_mixa.S +#endif + +# +# ... Audio targets +# +if SNDTYPE_SDL +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sdl.c $(sound_ASM) +endif +if SNDTYPE_ALSA_0_5 +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c $(sound_ASM) +endif +if SNDTYPE_ALSA_0_9 +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_9.c $(sound_ASM) +endif +# No idea what this is. it's in configure.in though... +#if SNDTYPE_MME +#libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_mme.c $(sound_ASM) +#endif +if SNDTYPE_OSS +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_oss.c $(sound_ASM) +endif +if SNDTYPE_SGI +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sgi.c $(sound_ASM) +endif +if SNDTYPE_SUN +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sun.c $(sound_ASM) +endif +if SNDTYPE_WIN32 +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_win.c $(sound_ASM) +endif +if SNDTYPE_NULL +libQFsound_la_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_null.c $(sound_ASM) +endif + +LIBLIST = libQFsound.la @LIBRARY_SEARCH_PATH@ diff --git a/libs/audio/targets/snd_alsa_0_5.c b/libs/audio/targets/snd_alsa_0_5.c new file mode 100644 index 000000000..784a1ff2d --- /dev/null +++ b/libs/audio/targets/snd_alsa_0_5.c @@ -0,0 +1,358 @@ +/* + snd_alsa_0_5.c + + Support for ALSA 0.5, the old stable version of ALSA. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#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 + +#include "QF/console.h" +#include "QF/qargs.h" +#include "QF/sound.h" + +#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 < info.pcmdevs) { + if ((rc = snd_pcm_open (&pcm_handle, card, dev, + SND_PCM_OPEN_PLAYBACK + | SND_PCM_OPEN_NONBLOCK)) == 0) { + return 0; + } + } + } + return 1; +} + +qboolean +SNDDMA_Init (void) +{ + int rc = 0, i; + char *err_msg = ""; + int rate = -1, format = -1, bps, stereo = -1, frag_size; + unsigned int mask; + + mask = snd_cards_mask (); + if (!mask) { + Con_Printf ("No sound cards detected\n"); + return 0; + } + if (snd_device->string[0]) { + sscanf (snd_device->string, "%d,%d", &card, &dev); + } + if (snd_bits->int_val) { + i = snd_bits->int_val; + if (i == 16) { + format = SND_PCM_SFMT_S16_LE; + } else if (i == 8) { + format = SND_PCM_SFMT_U8; + } else { + Con_Printf ("Error: invalid sample bits: %d\n", i); + return 0; + } + } + if (snd_rate->int_val) { + rate = snd_rate->int_val; + if (rate != 44100 && rate != 22050 && rate != 11025) { + Con_Printf ("Error: invalid sample rate: %d\n", rate); + return 0; + } + } + stereo = snd_stereo->int_val; + if (card == -1) { + for (card = 0; card < SND_CARDS; card++) { + if (!(mask & (1 << card))) + continue; + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } + } else { + if (dev == -1) { + rc = check_card (card); + if (rc < 0) + return 0; + if (!rc) + goto dev_openned; + } else { + if ((rc = snd_pcm_open (&pcm_handle, card, dev, + SND_PCM_OPEN_PLAYBACK + | SND_PCM_OPEN_NONBLOCK)) < 0) { + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + } + goto dev_openned; + } + } + Con_Printf ("Error: audio open error: %s\n", snd_strerror (rc)); + return 0; + + dev_openned: + Con_Printf ("Using card %d, device %d.\n", card, dev); + memset (&cinfo, 0, sizeof (cinfo)); + cinfo.channel = SND_PCM_CHANNEL_PLAYBACK; + snd_pcm_channel_info (pcm_handle, &cinfo); + Con_Printf ("%08x %08x %08x\n", cinfo.flags, cinfo.formats, cinfo.rates); + if ((rate == -1 || rate == 44100) && cinfo.rates & SND_PCM_RATE_44100) { + rate = 44100; + frag_size = 512; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 22050) + && cinfo.rates & SND_PCM_RATE_22050) { + rate = 22050; + frag_size = 256; /* assuming stereo 8 bit */ + } else if ((rate == -1 || rate == 11025) + && cinfo.rates & SND_PCM_RATE_11025) { + rate = 11025; + frag_size = 128; /* assuming stereo 8 bit */ + } else { + Con_Printf ("ALSA: desired rates not supported\n"); + goto error_2; + } + if ((format == -1 || format == SND_PCM_SFMT_S16_LE) + && cinfo.formats & SND_PCM_FMT_S16_LE) { + format = SND_PCM_SFMT_S16_LE; + bps = 16; + frag_size *= 2; + } else if ((format == -1 || format == SND_PCM_SFMT_U8) + && cinfo.formats & SND_PCM_FMT_U8) { + format = SND_PCM_SFMT_U8; + bps = 8; + } else { + Con_Printf ("ALSA: desired formats not supported\n"); + goto error_2; + } + if (stereo && cinfo.max_voices >= 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; i < e; i++) + mmap_control->fragments[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; + } +} diff --git a/libs/audio/targets/snd_alsa_0_9.c b/libs/audio/targets/snd_alsa_0_9.c new file mode 100644 index 000000000..978fdef50 --- /dev/null +++ b/libs/audio/targets/snd_alsa_0_9.c @@ -0,0 +1,313 @@ +/* + snd_alsa_0_9.c + + Support for ALSA 0.9 sound driver (cvs development version) + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include "QF/qtypes.h" +#include "QF/sound.h" +#include "QF/qargs.h" +#include "QF/console.h" + +static int snd_inited; + +static snd_pcm_t *pcm; +static const snd_pcm_channel_area_t *mmap_areas; +static char *pcmname = NULL; +size_t buffer_size; + +qboolean +SNDDMA_Init (void) +{ + int err; + int rate = -1, bps = -1, stereo = -1, frag_size; + snd_pcm_hw_params_t *hw; + snd_pcm_sw_params_t *sw; + + snd_pcm_hw_params_alloca (&hw); + snd_pcm_sw_params_alloca (&sw); + + if (snd_device->string[0]) + pcmname = snd_device->string; + if (snd_bits->int_val) { + bps = snd_bits->int_val; + if (bps != 16 && bps != 8) { + Con_Printf ("Error: invalid sample bits: %d\n", bps); + return 0; + } + } + if (snd_rate->int_val) { + rate = snd_rate->int_val; + if (rate != 44100 && rate != 22050 && rate != 11025) { + Con_Printf ("Error: invalid sample rate: %d\n", rate); + return 0; + } + } + stereo = snd_stereo->int_val; + if (!pcmname) + pcmname = "plug:0,0"; + if ((err = snd_pcm_open (&pcm, pcmname, + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) { + Con_Printf ("Error: audio open error: %s\n", snd_strerror (err)); + return 0; + } + + Con_Printf ("Using PCM %s.\n", pcmname); + snd_pcm_hw_params_any (pcm, hw); + + + switch (rate) { + case -1: + if (snd_pcm_hw_params_set_rate_near (pcm, hw, 44100, 0) >= 0) { + frag_size = 256; /* assuming stereo 8 bit */ + rate = 44100; + } else if (snd_pcm_hw_params_set_rate_near (pcm, hw, 22050, 0) >= 0) { + frag_size = 128; /* assuming stereo 8 bit */ + rate = 22050; + } else if (snd_pcm_hw_params_set_rate_near (pcm, hw, 11025, 0) >= 0) { + frag_size = 64; /* assuming stereo 8 bit */ + rate = 11025; + } else { + Con_Printf ("ALSA: no useable rates\n"); + goto error; + } + break; + case 11025: + case 22050: + case 44100: + if (snd_pcm_hw_params_set_rate_near (pcm, hw, rate, 0) >= 0) { + frag_size = 64 * rate / 11025; /* assuming stereo 8 bit */ + break; + } + /* Fall through */ + default: + Con_Printf ("ALSA: desired rate not supported\n"); + goto error; + } + + switch (bps) { + case -1: + if (snd_pcm_hw_params_set_format (pcm, hw, SND_PCM_FORMAT_S16_LE) >= + 0) { + bps = 16; + } else if (snd_pcm_hw_params_set_format (pcm, hw, SND_PCM_FORMAT_U8) + >= 0) { + bps = 8; + } else { + Con_Printf ("ALSA: no useable formats\n"); + goto error; + } + break; + case 8: + case 16: + if (snd_pcm_hw_params_set_format (pcm, hw, + bps == 8 ? SND_PCM_FORMAT_U8 : + SND_PCM_FORMAT_S16) >= 0) { + break; + } + /* Fall through */ + default: + Con_Printf ("ALSA: desired format not supported\n"); + goto error; + } + + if (snd_pcm_hw_params_set_access (pcm, hw, + SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) { + Con_Printf ("ALSA: interleaved is not supported\n"); + goto error; + } + + switch (stereo) { + case -1: + if (snd_pcm_hw_params_set_channels (pcm, hw, 2) >= 0) { + stereo = 1; + } else if (snd_pcm_hw_params_set_channels (pcm, hw, 1) >= 0) { + stereo = 0; + } else { + Con_Printf ("ALSA: no useable channels\n"); + goto error; + } + break; + case 0: + case 1: + if (snd_pcm_hw_params_set_channels (pcm, hw, stereo ? 2 : 1) >= 0) + break; + /* Fall through */ + default: + Con_Printf ("ALSA: desired channels not supported\n"); + goto error; + } + + snd_pcm_hw_params_set_period_size_near (pcm, hw, frag_size, 0); + + err = snd_pcm_hw_params (pcm, hw); + if (err < 0) { + Con_Printf ("ALSA: unable to install hw params\n"); + goto error; + } + + snd_pcm_sw_params_current (pcm, sw); + snd_pcm_sw_params_set_start_mode (pcm, sw, SND_PCM_START_EXPLICIT); + snd_pcm_sw_params_set_xrun_mode (pcm, sw, SND_PCM_XRUN_NONE); + + err = snd_pcm_sw_params (pcm, sw); + if (err < 0) { + Con_Printf ("ALSA: unable to install sw params\n"); + goto error; + } + + mmap_areas = snd_pcm_mmap_running_areas (pcm); + + shm = &sn; + memset ((dma_t *) shm, 0, sizeof (*shm)); + shm->splitbuffer = 0; + shm->channels = stereo + 1; + shm->submission_chunk = snd_pcm_hw_params_get_period_size (hw, 0); // don't + // mix + // less + // than + // this + // # + shm->samplepos = 0; // in mono samples + shm->samplebits = bps; + buffer_size = snd_pcm_hw_params_get_buffer_size (hw); + shm->samples = buffer_size * shm->channels; // mono samples in buffer + shm->speed = rate; + shm->buffer = (unsigned char *) mmap_areas->addr; + 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: + snd_pcm_close (pcm); + return 0; +} + +static inline int +get_hw_ptr () +{ + size_t app_ptr; + snd_pcm_sframes_t delay; + int hw_ptr; + + if (snd_pcm_state (pcm) != SND_PCM_STATE_RUNNING) + return 0; + app_ptr = snd_pcm_mmap_offset (pcm); + snd_pcm_delay (pcm, &delay); + hw_ptr = app_ptr - delay; + if (hw_ptr < 0) + hw_ptr += buffer_size; + return hw_ptr; +} + +int +SNDDMA_GetDMAPos (void) +{ + int hw_ptr; + + if (!snd_inited) + return 0; + + hw_ptr = get_hw_ptr (); + hw_ptr *= shm->channels; + shm->samplepos = hw_ptr; + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + snd_pcm_close (pcm); + 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 avail; + int missed; + int state; + int hw_ptr; + int offset; + + state = snd_pcm_state (pcm); + + switch (state) { + case SND_PCM_STATE_PREPARED: + snd_pcm_mmap_forward (pcm, count); + snd_pcm_start (pcm); + break; + case SND_PCM_STATE_RUNNING: + hw_ptr = get_hw_ptr (); + missed = hw_ptr - shm->samplepos / shm->channels; + if (missed < 0) + missed += buffer_size; + count -= missed; + offset = snd_pcm_mmap_offset (pcm); + if (offset > hw_ptr) + count -= (offset - hw_ptr); + else + count -= (buffer_size - hw_ptr + offset); + if (count < 0) { + snd_pcm_rewind (pcm, -count); + } else { + avail = snd_pcm_avail_update (pcm); + if (avail < 0) + avail = buffer_size; + if (count > avail) + count = avail; + snd_pcm_mmap_forward (pcm, count); + } + break; + default: + break; + } +} diff --git a/libs/audio/targets/snd_disk.c b/libs/audio/targets/snd_disk.c new file mode 100644 index 000000000..68e2c7169 --- /dev/null +++ b/libs/audio/targets/snd_disk.c @@ -0,0 +1,108 @@ +/* + snd_disk.c + + write sound to a disk file + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include + +#include "QF/console.h" +#include "QF/sound.h" +#include "QF/qargs.h" + +static int snd_inited; +QFile *snd_file; + +qboolean +SNDDMA_Init (void) +{ + shm = &sn; + memset ((dma_t *) shm, 0, sizeof (*shm)); + shm->splitbuffer = 0; + shm->channels = 2; + shm->submission_chunk = 1; // don't mix less than this # + shm->samplepos = 0; // in mono samples + shm->samplebits = 16; + shm->samples = 16384; // mono samples in buffer + shm->speed = 44100; + shm->buffer = malloc (shm->samples * shm->channels * shm->samplebits / 8); + + 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); + + if (!(snd_file = Qopen ("qf.raw", "wb"))) + return 0; + + snd_inited = 1; + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + shm->samplepos = 0; + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + Qclose (snd_file); + snd_file = 0; + free (shm->buffer); + 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) * shm->samplebits / 8; + + Qwrite (snd_file, shm->buffer, count); +} diff --git a/libs/audio/targets/snd_dma.c b/libs/audio/targets/snd_dma.c new file mode 100644 index 000000000..caa3abf32 --- /dev/null +++ b/libs/audio/targets/snd_dma.c @@ -0,0 +1,1020 @@ +/* + snd_dma.c + + main control for any streaming sound output device + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/cmd.h" +#include "QF/console.h" +#include "QF/model.h" +#include "QF/qargs.h" +#include "QF/sys.h" +#include "QF/sound.h" + +#ifdef _WIN32 +#include "winquake.h" +#include "in_win.h" +#endif + +void S_Play (void); +void S_PlayVol (void); +void S_SoundList (void); +void S_Update_ (void); +void S_StopAllSounds (qboolean clear); +void S_StopAllSoundsC (void); + +// QuakeWorld hack... +//#define viewentity playernum+1 + +// ======================================================================= +// Internal sound data & structures +// ======================================================================= + +channel_t channels[MAX_CHANNELS]; +int total_channels; +extern double host_frametime; // From host.h + +int snd_blocked = 0; +static qboolean snd_ambient = 1; +qboolean snd_initialized = false; + +// pointer should go away +volatile dma_t *shm = 0; +volatile dma_t sn; + +vec3_t listener_origin; +vec3_t listener_forward; +vec3_t listener_right; +vec3_t listener_up; +vec_t sound_nominal_clip_dist = 1000.0; + +int soundtime; // sample PAIRS +int paintedtime; // sample PAIRS + + +#define MAX_SFX 512 +sfx_t *known_sfx; // hunk allocated [MAX_SFX] +int num_sfx; + +sfx_t *ambient_sfx[NUM_AMBIENTS]; + +int desired_speed = 11025; +int desired_bits = 16; + +int sound_started = 0; + +cvar_t *bgmvolume; +cvar_t *volume; + +cvar_t *snd_device; +cvar_t *snd_rate; +cvar_t *snd_bits; +cvar_t *snd_stereo; +cvar_t *nosound; +cvar_t *precache; +cvar_t *loadas8bit; +cvar_t *ambient_level; +cvar_t *ambient_fade; +cvar_t *snd_noextraupdate; +cvar_t *snd_show; +cvar_t *snd_interp; +cvar_t *snd_phasesep; +cvar_t *snd_volumesep; +cvar_t *_snd_mixahead; + + +// ==================================================================== +// User-setable variables +// ==================================================================== + + +// +// Fake dma is a synchronous faking of the DMA progress used for +// isolating performance in the renderer. The fakedma_updates is +// number of times S_Update() is called per second. +// + +qboolean fakedma = false; +int fakedma_updates = 15; + +// FIXME: Evil hack that doesn't deserve to see the light of day. +// (pending merge of nq and qw client_stat_t's) +extern int snd_viewentity; +extern struct model_s *snd_worldmodel; + +void +S_AmbientOff (void) +{ + snd_ambient = false; +} + + +void +S_AmbientOn (void) +{ + snd_ambient = true; +} + + +void +S_SoundInfo_f (void) +{ + if (!sound_started || !shm) { + Con_Printf ("sound system not started\n"); + return; + } + + 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%lx dma buffer\n", (unsigned long) shm->buffer); + Con_Printf ("%5d total_channels\n", total_channels); +} + + +/* + S_Startup +*/ + +void +S_Startup (void) +{ + int rc; + + if (!snd_initialized) + return; + + if (!fakedma) { + rc = SNDDMA_Init (); + + if (!rc) { +#ifndef _WIN32 + Con_Printf ("S_Startup: SNDDMA_Init failed.\n"); +#endif + sound_started = 0; + return; + } + } + + sound_started = 1; +} + + +/* + S_Init +*/ +void +S_Init (void) +{ + + Con_Printf ("\nSound Initialization\n"); + + Cmd_AddCommand ("play", S_Play, + "Play selected sound effect (play pathto/sound.wav)"); + Cmd_AddCommand ("playvol", S_PlayVol, + "Play selected sound effect at selected volume (playvol pathto/sound.wav num"); + Cmd_AddCommand ("stopsound", S_StopAllSoundsC, + "Stops all sounds currently being played"); + Cmd_AddCommand ("soundlist", S_SoundList, + "Reports a list of sounds in the cache"); + Cmd_AddCommand ("soundinfo", S_SoundInfo_f, + "Report information on the sound system"); + + if (COM_CheckParm ("-nosound")) + return; + + if (COM_CheckParm ("-simsound")) + fakedma = true; + +// FIXME +// if (host_parms.memsize < 0x800000) { +// Cvar_Set (loadas8bit, "1"); +// Con_Printf ("loading all sounds as 8bit\n"); +// } + + + snd_initialized = true; + + S_Startup (); + + if (sound_started == 0) // sound startup failed? Bail out. + return; + + SND_InitScaletable (); + + known_sfx = Hunk_AllocName (MAX_SFX * sizeof (sfx_t), "sfx_t"); + + num_sfx = 0; + +// create a piece of DMA memory + + if (fakedma) { + shm = (void *) Hunk_AllocName (sizeof (*shm), "shm"); + shm->splitbuffer = 0; + shm->samplebits = 16; + shm->speed = 22050; + shm->channels = 2; + shm->samples = 32768; + shm->samplepos = 0; + shm->soundalive = true; + shm->gamealive = true; + shm->submission_chunk = 1; + shm->buffer = Hunk_AllocName (1 << 16, "shmbuf"); + } +// Con_Printf ("Sound sampling rate: %i\n", shm->speed); + + // provides a tick sound until washed clean + +// if (shm->buffer) +// shm->buffer[4] = shm->buffer[5] = 0x7f; // force a pop for debugging + + ambient_sfx[AMBIENT_WATER] = S_PrecacheSound ("ambience/water1.wav"); + ambient_sfx[AMBIENT_SKY] = S_PrecacheSound ("ambience/wind2.wav"); + + S_StopAllSounds (true); +} + +void +S_Init_Cvars (void) +{ + snd_device = Cvar_Get ("snd_device", "", CVAR_ROM, 0, + "sound device. \"\" is system default"); + snd_rate = Cvar_Get ("snd_rate", "0", CVAR_ROM, 0, + "sound playback rate. 0 is system default"); + snd_bits = Cvar_Get ("snd_bits", "0", CVAR_ROM, 0, + "sound sample depth. 0 is system default"); + snd_stereo = Cvar_Get ("snd_stereo", "1", CVAR_ROM, 0, + "sound stereo output"); + nosound = Cvar_Get ("nosound", "0", CVAR_NONE, 0, "Set to turn sound off"); + volume = + Cvar_Get ("volume", "0.7", CVAR_ARCHIVE, 0, + "Set the volume for sound playback"); + precache = + Cvar_Get ("precache", "1", CVAR_NONE, 0, "Toggle the use of a precache"); + loadas8bit = + Cvar_Get ("loadas8bit", "0", CVAR_NONE, 0, + "Toggles if sounds are loaded as 8-bit samples"); + bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, 0, "Volume of CD music"); + ambient_level = + Cvar_Get ("ambient_level", "0.3", CVAR_NONE, 0, "Ambient sounds' volume"); + ambient_fade = + Cvar_Get ("ambient_fade", "100", CVAR_NONE, 0, + "How quickly ambient sounds fade in or out"); + snd_noextraupdate = + Cvar_Get ("snd_noextraupdate", "0", CVAR_NONE, 0, + "Toggles the correct value display in host_speeds. Usually messes up sound playback when in effect"); + snd_show = + Cvar_Get ("snd_show", "0", CVAR_NONE, 0, + "Toggles the display of sounds currently being played"); + snd_interp = + Cvar_Get ("snd_interp", "1", CVAR_ARCHIVE, 0, + "control sample interpolation"); + snd_phasesep = + Cvar_Get ("snd_phasesep", "0.0", CVAR_ARCHIVE, 0, + "max stereo phase separation in ms. 0.6 is for 20cm head"); + snd_volumesep = + Cvar_Get ("snd_volumesep", "1.0", CVAR_ARCHIVE, 0, + "max stereo volume separation in ms. 1.0 is max"); + _snd_mixahead = + Cvar_Get ("_snd_mixahead", "0.1", CVAR_ARCHIVE, 0, + "Delay time for sounds"); +} + + +// ======================================================================= +// Shutdown sound engine +// ======================================================================= + +void +S_Shutdown (void) +{ + + if (!sound_started) + return; + + if (shm) + shm->gamealive = 0; + + shm = 0; + sound_started = 0; + + if (!fakedma) { + SNDDMA_Shutdown (); + } +} + + +// ======================================================================= +// Load a sound +// ======================================================================= + +/* + S_FindName +*/ +sfx_t * +S_FindName (char *name) +{ + int i; + sfx_t *sfx; + + if (!name) + Sys_Error ("S_FindName: NULL\n"); + + if (strlen (name) >= MAX_QPATH) + Sys_Error ("Sound name too long: %s", name); + +// see if already loaded + for (i = 0; i < num_sfx; i++) + if (!strcmp (known_sfx[i].name, name)) { + return &known_sfx[i]; + } + + if (num_sfx == MAX_SFX) + Sys_Error ("S_FindName: out of sfx_t"); + + sfx = &known_sfx[i]; + strcpy (sfx->name, name); + + num_sfx++; + + return sfx; +} + + +/* + S_TouchSound +*/ +void +S_TouchSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started) + return; + + sfx = S_FindName (name); + Cache_Check (&sfx->cache); +} + +/* + S_PrecacheSound +*/ +sfx_t * +S_PrecacheSound (char *name) +{ + sfx_t *sfx; + + if (!sound_started || nosound->int_val) + return NULL; + + sfx = S_FindName (name); + +// cache it in + if (precache->int_val) + S_LoadSound (sfx); + + return sfx; +} + + +//============================================================================= + +/* + SND_PickChannel +*/ +channel_t * +SND_PickChannel (int entnum, int entchannel) +{ + int ch_idx; + int first_to_die; + int life_left; + +// Check for replacement sound, or find the best one to replace + first_to_die = -1; + life_left = 0x7fffffff; + for (ch_idx = NUM_AMBIENTS; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; + ch_idx++) { + if (entchannel != 0 // channel 0 never overrides + && channels[ch_idx].entnum == entnum + && (channels[ch_idx].entchannel == entchannel || entchannel == -1)) { + // always override sound from same entity + first_to_die = ch_idx; + break; + } + // don't let monster sounds override player sounds + if (channels[ch_idx].entnum == snd_viewentity && entnum != snd_viewentity + && channels[ch_idx].sfx) + continue; + + if (channels[ch_idx].end - paintedtime < life_left) { + life_left = channels[ch_idx].end - paintedtime; + first_to_die = ch_idx; + } + } + + if (first_to_die == -1) + return NULL; + + if (channels[first_to_die].sfx) + channels[first_to_die].sfx = NULL; + + return &channels[first_to_die]; +} + +/* + SND_Spatialize +*/ +void +SND_Spatialize (channel_t *ch) +{ + vec_t dot; + vec_t dist; + int phase; // in samples + vec_t lscale, rscale, scale; + vec3_t source_vec; + sfx_t *snd; + +// anything coming from the view entity will always be full volume + if (ch->entnum == snd_viewentity) { + ch->leftvol = ch->master_vol; + ch->rightvol = ch->master_vol; + ch->phase = 0; + return; + } +// calculate stereo seperation and distance attenuation + + snd = ch->sfx; + VectorSubtract (ch->origin, listener_origin, source_vec); + + dist = VectorNormalize (source_vec) * ch->dist_mult; + + dot = DotProduct (listener_right, source_vec); + + if (shm->channels == 1) { + rscale = 1.0; + lscale = 1.0; + phase = 0; + } else { + rscale = 1.0 + dot * snd_volumesep->value; + lscale = 1.0 - dot * snd_volumesep->value; + phase = snd_phasesep->value * 0.001 * shm->speed * dot; + } + +// add in distance effect + scale = (1.0 - dist) * rscale; + ch->rightvol = (int) (ch->master_vol * scale); + if (ch->rightvol < 0) + ch->rightvol = 0; + + scale = (1.0 - dist) * lscale; + ch->leftvol = (int) (ch->master_vol * scale); + if (ch->leftvol < 0) + ch->leftvol = 0; + + ch->phase = phase; +} + + +// ======================================================================= +// Start a sound effect +// ======================================================================= + +void +S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, + float attenuation) +{ + channel_t *target_chan, *check; + sfxcache_t *sc; + int vol; + int ch_idx; + int skip; + + if (!sound_started) + return; + + if (!sfx) + return; + + if (nosound->int_val) + return; + + vol = fvol * 255; + +// pick a channel to play on + target_chan = SND_PickChannel (entnum, entchannel); + if (!target_chan) + return; + +// spatialize + memset (target_chan, 0, sizeof (*target_chan)); + VectorCopy (origin, target_chan->origin); + target_chan->dist_mult = attenuation / sound_nominal_clip_dist; + target_chan->master_vol = vol; + target_chan->entnum = entnum; + target_chan->entchannel = entchannel; + SND_Spatialize (target_chan); + + if (!target_chan->leftvol && !target_chan->rightvol) + return; // not audible at all + +// new channel + sc = S_LoadSound (sfx); + if (!sc) { + target_chan->sfx = NULL; + return; // couldn't load the sound's data + } + + target_chan->sfx = sfx; + target_chan->pos = 0.0; + target_chan->end = paintedtime + sc->length; + +// if an identical sound has also been started this frame, offset the pos +// a bit to keep it from just making the first one louder + check = &channels[NUM_AMBIENTS]; + for (ch_idx = NUM_AMBIENTS; ch_idx < NUM_AMBIENTS + MAX_DYNAMIC_CHANNELS; + ch_idx++, check++) { + if (check == target_chan) + continue; + if (check->sfx == sfx && !check->pos) { + skip = rand () % (int) (0.1 * shm->speed); + if (skip >= target_chan->end) + skip = target_chan->end - 1; + target_chan->pos += skip; + target_chan->end -= skip; + break; + } + + } +} + +void +S_StopSound (int entnum, int entchannel) +{ + int i; + + for (i = 0; i < MAX_DYNAMIC_CHANNELS; i++) { + if (channels[i].entnum == entnum + && channels[i].entchannel == entchannel) { + channels[i].end = 0; + channels[i].sfx = NULL; + return; + } + } +} + +void +S_StopAllSounds (qboolean clear) +{ + int i; + + if (!sound_started) + return; + + total_channels = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; // no statics + + for (i = 0; i < MAX_CHANNELS; i++) + if (channels[i].sfx) + channels[i].sfx = NULL; + + memset (channels, 0, MAX_CHANNELS * sizeof (channel_t)); + + if (clear) + S_ClearBuffer (); +} + +void +S_StopAllSoundsC (void) +{ + S_StopAllSounds (true); +} + +void +S_ClearBuffer (void) +{ + int clear; + +#ifdef _WIN32 + if (!sound_started || !shm || (!shm->buffer && !pDSBuf)) +#else + if (!sound_started || !shm || !shm->buffer) +#endif + return; + + if (shm->samplebits == 8) + clear = 0x80; + else + clear = 0; + +#ifdef _WIN32 + if (pDSBuf) + DSOUND_ClearBuffer (clear); + else +#endif + { + memset (shm->buffer, clear, shm->samples * shm->samplebits / 8); + } +} + + +/* + S_StaticSound +*/ +void +S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) +{ + channel_t *ss; + sfxcache_t *sc; + + if (!sfx) + return; + + if (total_channels == MAX_CHANNELS) { + Con_Printf ("total_channels == MAX_CHANNELS\n"); + return; + } + + ss = &channels[total_channels]; + total_channels++; + + sc = S_LoadSound (sfx); + if (!sc) + return; + + if (sc->loopstart == -1) { + Con_Printf ("Sound %s not looped\n", sfx->name); + return; + } + + ss->sfx = sfx; + VectorCopy (origin, ss->origin); + ss->master_vol = vol; + ss->dist_mult = (attenuation / 64) / sound_nominal_clip_dist; + ss->end = paintedtime + sc->length; + + SND_Spatialize (ss); + ss->oldphase = ss->phase; +} + + +//============================================================================= + +/* + S_UpdateAmbientSounds +*/ +void +S_UpdateAmbientSounds (void) +{ + mleaf_t *l; + float vol; + int ambient_channel; + channel_t *chan; + + if (!snd_ambient) + return; + +// calc ambient sound levels + if (!snd_worldmodel) + return; + + l = Mod_PointInLeaf (listener_origin, snd_worldmodel); + if (!l || !ambient_level->value) { + for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; + ambient_channel++) + channels[ambient_channel].sfx = NULL; + return; + } + + for (ambient_channel = 0; ambient_channel < NUM_AMBIENTS; ambient_channel++) { + chan = &channels[ambient_channel]; + chan->sfx = ambient_sfx[ambient_channel]; + + vol = ambient_level->value * l->ambient_sound_level[ambient_channel]; + if (vol < 8) + vol = 0; + + // don't adjust volume too fast + if (chan->master_vol < vol) { + chan->master_vol += host_frametime * ambient_fade->value; + if (chan->master_vol > vol) + chan->master_vol = vol; + } else if (chan->master_vol > vol) { + chan->master_vol -= host_frametime * ambient_fade->value; + if (chan->master_vol < vol) + chan->master_vol = vol; + } + + chan->leftvol = chan->rightvol = chan->master_vol; + } +} + + +/* + S_Update + + Called once each time through the main loop +*/ +void +S_Update (vec3_t origin, vec3_t forward, vec3_t right, vec3_t up) +{ + int i, j; + int total; + channel_t *ch; + channel_t *combine; + + if (!sound_started || (snd_blocked > 0)) + return; + + VectorCopy (origin, listener_origin); + VectorCopy (forward, listener_forward); + VectorCopy (right, listener_right); + VectorCopy (up, listener_up); + +// update general area ambient sound sources + S_UpdateAmbientSounds (); + + combine = NULL; + +// update spatialization for static and dynamic sounds + ch = channels + NUM_AMBIENTS; + for (i = NUM_AMBIENTS; i < total_channels; i++, ch++) { + if (!ch->sfx) + continue; + ch->oldphase = ch->phase; // prepare to lerp from prev to next + // phase + SND_Spatialize (ch); // respatialize channel + if (!ch->leftvol && !ch->rightvol) + continue; + + // try to combine static sounds with a previous channel of the same + // sound effect so we don't mix five torches every frame + + if (i >= MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS) { + // see if it can just use the last one + if (combine && combine->sfx == ch->sfx) { + combine->leftvol += ch->leftvol; + combine->rightvol += ch->rightvol; + ch->leftvol = ch->rightvol = 0; + continue; + } + // search for one + combine = channels + MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; + for (j = MAX_DYNAMIC_CHANNELS + NUM_AMBIENTS; j < i; j++, combine++) + if (combine->sfx == ch->sfx) + break; + + if (j == total_channels) { + combine = NULL; + } else { + if (combine != ch) { + combine->leftvol += ch->leftvol; + combine->rightvol += ch->rightvol; + ch->leftvol = ch->rightvol = 0; + } + continue; + } + } + + + } + +// +// debugging output +// + if (snd_show->int_val) { + total = 0; + ch = channels; + for (i = 0; i < total_channels; i++, ch++) + if (ch->sfx && (ch->leftvol || ch->rightvol)) { + // Con_Printf ("%3i %3i %s\n", ch->leftvol, ch->rightvol, + // ch->sfx->name); + total++; + } + + Con_Printf ("----(%i)----\n", total); + } +// mix some sound + S_Update_ (); +} + +void +GetSoundtime (void) +{ + int samplepos; + static int buffers; + static int oldsamplepos; + int fullsamples; + + fullsamples = shm->samples / shm->channels; + +// it is possible to miscount buffers if it has wrapped twice between +// calls to S_Update. Oh well. + samplepos = SNDDMA_GetDMAPos (); + + if (samplepos < oldsamplepos) { + buffers++; // buffer wrapped + + if (paintedtime > 0x40000000) { // time to chop things off to avoid + // 32 bit limits + buffers = 0; + paintedtime = fullsamples; + S_StopAllSounds (true); + } + } + oldsamplepos = samplepos; + + soundtime = buffers * fullsamples + samplepos / shm->channels; +} + +void +S_ExtraUpdate (void) +{ + +#ifdef _WIN32 + IN_Accumulate (); +#endif + + if (snd_noextraupdate->int_val) + return; // don't pollute timings + S_Update_ (); +} + + + +void +S_Update_ (void) +{ + unsigned int endtime; + int samps; + + if (!sound_started || (snd_blocked > 0)) + return; + +// Updates DMA time + GetSoundtime (); + +// check to make sure that we haven't overshot + if (paintedtime < soundtime) { + // Con_Printf ("S_Update_ : overflow\n"); + paintedtime = soundtime; + } +// mix ahead of current position + endtime = soundtime + _snd_mixahead->value * shm->speed; + samps = shm->samples >> (shm->channels - 1); + if (endtime - soundtime > samps) + endtime = soundtime + samps; + +#ifdef _WIN32 + if (pDSBuf) + DSOUND_Restore (); +#endif + + S_PaintChannels (endtime); + + SNDDMA_Submit (); +} + +/* + console functions +*/ + +void +S_Play (void) +{ + static int hash = 345; + int i; + char name[256]; + sfx_t *sfx; + + i = 1; + while (i < Cmd_Argc ()) { + if (!strrchr (Cmd_Argv (i), '.')) { + strcpy (name, Cmd_Argv (i)); + strncat (name, ".wav", sizeof (name) - strlen (name)); + } else + strcpy (name, Cmd_Argv (i)); + sfx = S_PrecacheSound (name); + S_StartSound (hash++, 0, sfx, listener_origin, 1.0, 1.0); + i++; + } +} + +void +S_PlayVol (void) +{ + static int hash = 543; + int i; + float vol; + char name[256]; + sfx_t *sfx; + + i = 1; + while (i < Cmd_Argc ()) { + if (!strrchr (Cmd_Argv (i), '.')) { + strcpy (name, Cmd_Argv (i)); + strncat (name, ".wav", sizeof (name) - strlen (name)); + } else + strcpy (name, Cmd_Argv (i)); + sfx = S_PrecacheSound (name); + vol = atof (Cmd_Argv (i + 1)); + S_StartSound (hash++, 0, sfx, listener_origin, vol, 1.0); + i += 2; + } +} + +void +S_SoundList (void) +{ + int i; + sfx_t *sfx; + sfxcache_t *sc; + int size, total; + + total = 0; + for (sfx = known_sfx, i = 0; i < num_sfx; i++, sfx++) { + sc = Cache_Check (&sfx->cache); + if (!sc) + continue; + size = sc->length * sc->width * (sc->stereo + 1); + total += size; + if (sc->loopstart >= 0) + Con_Printf ("L"); + else + Con_Printf (" "); + Con_Printf ("(%2db) %6i : %s\n", sc->width * 8, size, sfx->name); + } + Con_Printf ("Total resident: %i\n", total); +} + + +void +S_LocalSound (char *sound) +{ + sfx_t *sfx; + + if (nosound->int_val) + return; + if (!sound_started) + return; + + sfx = S_PrecacheSound (sound); + if (!sfx) { + Con_Printf ("S_LocalSound: can't cache %s\n", sound); + return; + } + S_StartSound (snd_viewentity, -1, sfx, vec3_origin, 1, 1); +} + + +void +S_ClearPrecache (void) +{ +} + + +void +S_BeginPrecaching (void) +{ +} + + +void +S_EndPrecaching (void) +{ +} diff --git a/libs/audio/targets/snd_gus.c b/libs/audio/targets/snd_gus.c new file mode 100644 index 000000000..9eb0db4b5 --- /dev/null +++ b/libs/audio/targets/snd_gus.c @@ -0,0 +1,1281 @@ + +/* + snd_gus.c + + @description@ + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "dosisms.h" + +//============================================================================= +// Author(s): Jayeson Lee-Steere + +#define INI_STRING_SIZE 0x100 + +QFile *ini_fopen (const char *filename, const char *modes); +int ini_fclose (QFile *f); +void ini_fgets (QFile *f, const char *section, const char *field, + + char *s); + +// Routines for reading from .INI files +// The read routines are fairly efficient. +// +// Author(s): Jayeson Lee-Steere + +#define MAX_SECTION_WIDTH 20 +#define MAX_FIELD_WIDTH 20 + +#define NUM_SECTION_BUFFERS 10 +#define NUM_FIELD_BUFFERS 20 + +struct section_buffer { + long offset; + char name[MAX_SECTION_WIDTH + 1]; +}; + +struct field_buffer { + long offset; + int section; + char name[MAX_FIELD_WIDTH + 1]; +}; + +static QFile *current_file = NULL; +static int current_section; + +static int current_section_buffer = 0; +static int current_field_buffer = 0; + +static struct section_buffer section_buffers[NUM_SECTION_BUFFERS]; +static struct field_buffer field_buffers[NUM_FIELD_BUFFERS]; + +//*************************************************************************** +// Internal routines +//*************************************************************************** +static char +toupper (char c) +{ + if (c >= 'a' && c <= 'z') + c -= ('a' - 'A'); + return (c); +} + +static void +reset_buffer (QFile *f) +{ + int i; + + for (i = 0; i < NUM_SECTION_BUFFERS; i++) + section_buffers[i].name[0] = 0; + for (i = 0; i < NUM_FIELD_BUFFERS; i++) + field_buffers[i].name[0] = 0; + + current_file = f; +} + +// Sees if the current string is section "name" (i.e. ["name"]). +// If "name"=="*", sees if the current string is any section +// (i.e. [....]). Returns 1 if true else 0 if false. +static int +is_section (char *s, const char *name) +{ + int wild = 0; + + // See if wild search + if (strcmp ("*", name) == 0) + wild = 1; + + // Skip leading spaces + while (s[0] == ' ') + s++; + // Look for leading "[" + if (s[0] != '[') + return (0); + s++; + // Make sure name matches + while (s[0] != ']' && s[0] != 13 && s[0] != 10 && s[0] != 0 && name[0] != 0) { + if (!wild) + if (toupper (s[0]) != toupper (name[0])) + return (0); + s++; + if (!wild) + name++; + } + if (!wild) + if (name[0] != 0) + return (0); + // Skip trailing spaces + while (s[0] == ' ') + s++; + // Make sure we have trailing "]" + if (s[0] != ']') + return (0); + return (1); +} + +// Sees if the current string is field "name" (i.e. "name"=...). +// If "name"=="*", sees if the current string is any field +// (i.e. ...=...). Returns 1 if true else 0 if false. +static int +is_field (char *s, const char *name) +{ + int wild = 0; + + // See if wild search + if (strcmp ("*", name) == 0) + wild = 1; + + // Skip leading spaces + while (s[0] == ' ') + s++; + + // Make sure name matches + while (s[0] != '=' && s[0] != 13 && s[0] != 10 && s[0] != 0 && name[0] != 0) { + if (!wild) + if (toupper (s[0]) != toupper (name[0])) + return (0); + s++; + if (!wild) + name++; + } + if (!wild) + if (name[0] != 0) + return (0); + // Skip trailing spaces + while (s[0] == ' ') + s++; + // Make sure we have an "=" + if (s[0] != '=') + return (0); + + return (1); +} + +// Extracts the section name from a section heading +// e.g. in="[hey man]" gives out="hey man" +static void +get_section_name (char *out, char *in) +{ + int i = 0; + + // Skip spaces before '[' + while (in[0] == ' ') + in++; + // Make sure there is a '[' + if (in[0] != '[') { + out[0] = 0; + return; + } + // Skip past '[' + in++; + // Copy string if any to output string. + while (in[0] != ']' && in[0] != 13 && in[0] != 10 && in[0] != 0) { + if (i < MAX_SECTION_WIDTH) { + out[i] = in[0]; + i++; + } + in++; + } + // Make sure string was terminated with ']' + if (in[0] != ']') { + out[0] = 0; + return; + } + // Remove trailing spaces + while (i > 0 && out[i - 1] == ' ') + i--; + // Null terminate the output string. + out[i] = 0; +} + +// Extracts the field name from a field line +// e.g. in="sooty=life be in it" gives out="sooty" +static void +get_field_name (char *out, char *in) +{ + int i = 0; + + // Skip leading spaces + while (in[0] == ' ') + in++; + // Copy name to output string + while (in[0] != '=' && in[0] != 13 && in[0] != 10 && in[0] != 0) { + if (i < MAX_FIELD_WIDTH) { + out[i] = in[0]; + i++; + } + in++; + } + // Make sure we stopped on "=" + if (in[0] != '=') { + out[0] = 0; + return; + } + // Remove trailing spaces + while (i > 0 && out[i - 1] == ' ') + i--; + // Null terminate the output string. + out[i] = 0; +} + +// Returns the field data from string s. +// e.g. in="wally = golly man" gives out="golly man" +static void +get_field_string (char *out, char *in) +{ + int i = 0; + + // Find '=' if it exists + while (in[0] != '=' && in[0] != 13 && in[0] != 10 && in[0] != 0) + in++; + // If there is an '=', skip past it. + if (in[0] == '=') + in++; + // Skip any spaces between the '=' and string. + while (in[0] == ' ' || in[0] == '[') + in++; + // Copy string, if there is one, to the output string. + while (in[0] != 13 && in[0] != 10 && in[0] != 0 + && i < (INI_STRING_SIZE - 1)) { + out[i] = in[0]; + in++; + i++; + } + // Null terminate the output string. + out[i] = 0; +} + +// Adds a section to the buffer +static int +add_section (char *instring, long offset) +{ + int i; + char section[MAX_SECTION_WIDTH + 1]; + + // Extract section name + get_section_name (section, instring); + // See if section already exists. + for (i = 0; i < NUM_SECTION_BUFFERS; i++) + if (strcasecmp (section, section_buffers[i].name) == 0) + return (i); + // Increment current_section_buffer + current_section_buffer++; + if (current_section_buffer > NUM_SECTION_BUFFERS) + current_section_buffer = 0; + // Delete any field buffers that correspond to this section + for (i = 0; i < NUM_FIELD_BUFFERS; i++) + if (field_buffers[i].section == current_section_buffer) + field_buffers[i].name[0] = 0; + // Set buffer information + strcpy (section_buffers[current_section_buffer].name, section); + section_buffers[current_section_buffer].offset = offset; + return (current_section_buffer); +} + +// Adds a field to the buffer +static void +add_field (char *instring, int section, long offset) +{ + int i; + char field[MAX_FIELD_WIDTH + 1]; + + // Extract field name + get_field_name (field, instring); + // See if field already exists + for (i = 0; i < NUM_FIELD_BUFFERS; i++) + if (field_buffers[i].section == section) + if (strcasecmp (field_buffers[i].name, field) == 0) + return; + // Increment current_field_buffer + current_field_buffer++; + if (current_field_buffer > NUM_FIELD_BUFFERS) + current_field_buffer = 0; + // Set buffer information + strcpy (field_buffers[current_field_buffer].name, field); + field_buffers[current_field_buffer].section = section; + field_buffers[current_field_buffer].offset = offset; +} + +// Identical to fgets except the string is trucated at the first ';', +// carriage return or line feed. +static char * +stripped_fgets (char *s, int n, QFile *f) +{ + int i = 0; + + if (fgets (s, n, f) == NULL) + return (NULL); + + while (s[i] != ';' && s[i] != 13 && s[i] != 10 && s[i] != 0) + i++; + s[i] = 0; + + return (s); +} + +//*************************************************************************** +// Externally accessable routines +//*************************************************************************** +// Opens an .INI file. Works like fopen +QFile * +ini_fopen (const char *filename, const char *modes) +{ + return (Qopen (filename, modes)); +} + +// Closes a .INI file. Works like fclose +int +ini_fclose (QFile *f) +{ + if (f == current_file) + reset_buffer (NULL); + return (Qclose (f)); +} + +// Puts "field" from "section" from .ini file "f" into "s". +// If "section" does not exist or "field" does not exist in +// section then s=""; +void +ini_fgets (QFile *f, const char *section, const char *field, char *s) +{ + int i; + long start_pos, string_start_pos; + char ts[INI_STRING_SIZE * 2]; + + if (f != current_file) + reset_buffer (f); + + // Default to "Not found" + s[0] = 0; + + // See if section is in buffer + for (i = 0; i < NUM_SECTION_BUFFERS; i++) + if (strnicmp (section_buffers[i].name, section, MAX_SECTION_WIDTH) == 0) + break; + + // If section is in buffer, seek to it if necessary + if (i < NUM_SECTION_BUFFERS) { + if (i != current_section) { + current_section = i; + fseek (f, section_buffers[i].offset, SEEK_SET); + } + } + // else look through .ini file for it. + else { + // Make sure we are not at eof or this will cause trouble. + if (feof (f)) + rewind (f); + start_pos = ftell (f); + while (1) { + stripped_fgets (ts, INI_STRING_SIZE * 2, f); + // If it is a section, add it to the section buffer + if (is_section (ts, "*")) + current_section = add_section (ts, ftell (f)); + // If it is the section we are looking for, break. + if (is_section (ts, section)) + break; + // If we reach the end of the file, rewind to the start. + if (feof (f)) + rewind (f); + if (ftell (f) == start_pos) + return; + } + } + + // See if field is in buffer + for (i = 0; i < NUM_FIELD_BUFFERS; i++) + if (field_buffers[i].section == current_section) + if (strnicmp (field_buffers[i].name, field, MAX_FIELD_WIDTH) == 0) + break; + + // If field is in buffer, seek to it and read it + if (i < NUM_FIELD_BUFFERS) { + fseek (f, field_buffers[i].offset, SEEK_SET); + stripped_fgets (ts, INI_STRING_SIZE * 2, f); + get_field_string (s, ts); + } else + // else search through section for field. + { + // Make sure we do not start at eof or this will cause problems. + if (feof (f)) + fseek (f, section_buffers[current_section].offset, SEEK_SET); + start_pos = ftell (f); + while (1) { + string_start_pos = ftell (f); + stripped_fgets (ts, INI_STRING_SIZE * 2, f); + // If it is a field, add it to the buffer + if (is_field (ts, "*")) + add_field (ts, current_section, string_start_pos); + // If it is the field we are looking for, save it + if (is_field (ts, field)) { + get_field_string (s, ts); + break; + } + // If we reach the end of the section, start over + if (feof (f) || is_section (ts, "*")) + fseek (f, section_buffers[current_section].offset, SEEK_SET); + if (ftell (f) == start_pos) + return; + } + } +} + +//============================================================================= + +#define BYTE unsigned char +#define WORD unsigned short +#define DWORD unsigned long + +#define BUFFER_SIZE 4096 + +#define CODEC_ADC_INPUT_CONTROL_LEFT 0x00 +#define CODEC_ADC_INPUT_CONTROL_RIGHT 0x01 +#define CODEC_AUX1_INPUT_CONTROL_LEFT 0x02 +#define CODEC_AUX1_INPUT_CONTROL_RIGHT 0x03 +#define CODEC_AUX2_INPUT_CONTROL_LEFT 0x04 +#define CODEC_AUX2_INPUT_CONTROL_RIGHT 0x05 +#define CODEC_DAC_OUTPUT_CONTROL_LEFT 0x06 +#define CODEC_DAC_OUTPUT_CONTROL_RIGHT 0x07 +#define CODEC_FS_FORMAT 0x08 +#define CODEC_INTERFACE_CONFIG 0x09 +#define CODEC_PIN_CONTROL 0x0A +#define CODEC_ERROR_STATUS_AND_INIT 0x0B +#define CODEC_MODE_AND_ID 0x0C +#define CODEC_LOOPBACK_CONTROL 0x0D +#define CODEC_PLAYBACK_UPPER_BASE_COUNT 0x0E +#define CODEC_PLAYBACK_LOWER_BASE_COUNT 0x0F + +#define SET_CONTROL 0x00 +#define SET_FREQUENCY 0x01 +#define SET_START_HIGH 0x02 +#define SET_START_LOW 0x03 +#define SET_END_HIGH 0x04 +#define SET_END_LOW 0x05 +#define SET_VOLUME_RATE 0x06 +#define SET_VOLUME_START 0x07 +#define SET_VOLUME_END 0x08 +#define SET_CURR_VOLUME 0x09 +#define SET_VOLUME 0x09 +#define SET_ACC_HIGH 0x0A +#define SET_ACC_LOW 0x0B +#define SET_BALANCE 0x0C +#define SET_VOLUME_CONTROL 0x0D +#define SET_VOICES 0x0E + +#define DMA_CONTROL 0x41 +#define SET_DMA_ADDRESS 0x42 +#define SET_DRAM_LOW 0x43 +#define SET_DRAM_HIGH 0x44 +#define ADLIB_CONTROL 0x45 +#define ADLIB_TIMER1 0x46 +#define ADLIB_TIMER2 0x47 +#define SET_RECORD_RATE 0x48 +#define RECORD_CONTROL 0x49 +#define SET_JOYSTICK 0x4B +#define MASTER_RESET 0x4C + +#define GET_CONTROL 0x80 +#define GET_FREQUENCY 0x81 +#define GET_START_HIGH 0x82 +#define GET_START_LOW 0x83 +#define GET_END_HIGH 0x84 +#define GET_END_LOW 0x85 +#define GET_VOLUME_RATE 0x86 +#define GET_VOLUME_START 0x87 +#define GET_VOLUME_END 0x88 +#define GET_VOLUME 0x89 +#define GET_ACC_HIGH 0x8A +#define GET_ACC_LOW 0x8B +#define GET_BALANCE 0x8C +#define GET_VOLUME_CONTROL 0x8D +#define GET_VOICES 0x8E +#define GET_IRQV 0x8F + +struct CodecRateStruct { + WORD Rate; + BYTE FSVal; +}; + +struct Gf1RateStruct { + WORD Rate; + BYTE Voices; +}; + +//============================================================================= +// Reference variables in SND_DOS.C +//============================================================================= +extern short *dma_buffer; + +//============================================================================= +// GUS-only variables +//============================================================================= +static BYTE HaveCodec = 0; + +static WORD CodecRegisterSelect; +static WORD CodecData; +static WORD CodecStatus; +static WORD Gf1TimerControl; +static WORD Gf1PageRegister; +static WORD Gf1RegisterSelect; +static WORD Gf1DataLow; +static WORD Gf1DataHigh; + +static BYTE DmaChannel; + +static BYTE PageRegs[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; +static BYTE AddrRegs[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc }; +static BYTE CountRegs[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce }; + +static WORD AddrReg; +static WORD CountReg; +static WORD ModeReg; +static WORD DisableReg; +static WORD ClearReg; + +static struct CodecRateStruct CodecRates[] = { + {5512, 0x01}, + {6620, 0x0F}, + {8000, 0x00}, + {9600, 0x0E}, + {11025, 0x03}, + {16000, 0x02}, + {18900, 0x05}, + {22050, 0x07}, + {27420, 0x04}, + {32000, 0x06}, + {33075, 0x0D}, + {37800, 0x09}, + {44100, 0x0B}, + {48000, 0x0C}, + {0, 0x00} // End marker +}; + +static struct Gf1RateStruct Gf1Rates[] = { + {19293, 32}, + {19916, 31}, + {20580, 30}, + {21289, 29}, + {22050, 28}, + {22866, 27}, + {23746, 26}, + {24696, 25}, + {25725, 24}, + {26843, 23}, + {28063, 22}, + {29400, 21}, + {30870, 20}, + {32494, 19}, + {34300, 18}, + {36317, 17}, + {38587, 16}, + {41160, 15}, + {44100, 14}, + {0, 0} +}; + +//============================================================================= +// Basic GF1 functions +//============================================================================= +void +SetGf18 (BYTE reg, BYTE data) +{ + dos_outportb (Gf1RegisterSelect, reg); + dos_outportb (Gf1DataHigh, data); +} + +void +SetGf116 (BYTE reg, WORD data) +{ + dos_outportb (Gf1RegisterSelect, reg); + dos_outportw (Gf1DataLow, data); +} + +BYTE +GetGf18 (BYTE reg) +{ + dos_outportb (Gf1RegisterSelect, reg); + return (dos_inportb (Gf1DataHigh)); +} + +WORD +GetGf116 (BYTE reg) +{ + dos_outportb (Gf1RegisterSelect, reg); + return (dos_inportw (Gf1DataLow)); +} + +void +Gf1Delay (void) +{ + int i; + + for (i = 0; i < 27; i++) + dos_inportb (Gf1TimerControl); +} + +DWORD +ConvertTo16 (DWORD Address) +{ + return (((Address >> 1) & 0x0001FFFF) | (Address & 0x000C0000L)); +} + +void +ClearGf1Ints (void) +{ + int i; + + SetGf18 (DMA_CONTROL, 0x00); + SetGf18 (ADLIB_CONTROL, 0x00); + SetGf18 (RECORD_CONTROL, 0x00); + + GetGf18 (DMA_CONTROL); + GetGf18 (RECORD_CONTROL); + for (i = 0; i < 32; i++); + GetGf18 (GET_IRQV); +} + + +//============================================================================= +// Get Interwave (UltraSound PnP) configuration if any +//============================================================================= +static qboolean +GUS_GetIWData (void) +{ + char *Interwave, s[INI_STRING_SIZE]; + QFile *IwFile; + int CodecBase, CodecDma, i; + + Interwave = getenv ("INTERWAVE"); + if (Interwave == NULL) + return (false); + + // Open IW.INI + IwFile = ini_fopen (Interwave, "rt"); + if (IwFile == NULL) + return (false); + + // Read codec base and codec DMA + ini_fgets (IwFile, "setup 0", "CodecBase", s); + sscanf (s, "%X", &CodecBase); + ini_fgets (IwFile, "setup 0", "DMA2", s); + sscanf (s, "%i", &CodecDma); + + ini_fclose (IwFile); + + // Make sure numbers OK + if (CodecBase == 0 || CodecDma == 0) + return (false); + + CodecRegisterSelect = CodecBase; + CodecData = CodecBase + 1; + CodecStatus = CodecBase + 2; + DmaChannel = CodecDma; + + // Make sure there is a CODEC at the CODEC base + + // Clear any pending IRQs + dos_inportb (CodecStatus); + dos_outportb (CodecStatus, 0); + + // Wait for 'INIT' bit to clear + for (i = 0; i < 0xFFFF; i++) + if ((dos_inportb (CodecRegisterSelect) & 0x80) == 0) + break; + if (i == 0xFFFF) + return (false); + + // Get chip revision - can not be zero + dos_outportb (CodecRegisterSelect, CODEC_MODE_AND_ID); + if ((dos_inportb (CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) + return (false); + if ((dos_inportb (CodecData) & 0x0F) == 0) + return (false); + + HaveCodec = 1; + Con_Printf ("Sound Card is UltraSound PnP\n"); + return (true); +} + +//============================================================================= +// Get UltraSound MAX configuration if any +//============================================================================= +static qboolean +GUS_GetMAXData (void) +{ + char *Ultrasnd, *Ultra16; + int i; + int GusBase, Dma1, Dma2, Irq1, Irq2; + int CodecBase, CodecDma, CodecIrq, CodecType; + BYTE MaxVal; + + Ultrasnd = getenv ("ULTRASND"); + Ultra16 = getenv ("ULTRA16"); + if (Ultrasnd == NULL || Ultra16 == NULL) + return (false); + + sscanf (Ultrasnd, "%x,%i,%i,%i,%i", &GusBase, &Dma1, &Dma2, &Irq1, &Irq2); + sscanf (Ultra16, "%x,%i,%i,%i", &CodecBase, &CodecDma, &CodecIrq, + &CodecType); + + if (CodecType == 0 && CodecDma != 0) + DmaChannel = CodecDma & 0x07; + else + DmaChannel = Dma2 & 0x07; + + // Make sure there is a GUS at GUS base + dos_outportb (GusBase + 0x08, 0x55); + if (dos_inportb (GusBase + 0x0A) != 0x55) + return (false); + dos_outportb (GusBase + 0x08, 0xAA); + if (dos_inportb (GusBase + 0x0A) != 0xAA) + return (false); + + // Program CODEC control register + MaxVal = ((CodecBase & 0xF0) >> 4) | 0x40; + if (Dma1 > 3) + MaxVal |= 0x10; + if (Dma2 > 3) + MaxVal |= 0x20; + dos_outportb (GusBase + 0x106, MaxVal); + + CodecRegisterSelect = CodecBase; + CodecData = CodecBase + 1; + CodecStatus = CodecBase + 2; + + // Make sure there is a CODEC at the CODEC base + + // Clear any pending IRQs + dos_inportb (CodecStatus); + dos_outportb (CodecStatus, 0); + + // Wait for 'INIT' bit to clear + for (i = 0; i < 0xFFFF; i++) + if ((dos_inportb (CodecRegisterSelect) & 0x80) == 0) + break; + if (i == 0xFFFF) + return (false); + + // Get chip revision - can not be zero + dos_outportb (CodecRegisterSelect, CODEC_MODE_AND_ID); + if ((dos_inportb (CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) + return (false); + if ((dos_inportb (CodecData) & 0x0F) == 0) + return (false); + + HaveCodec = 1; + Con_Printf ("Sound Card is UltraSound MAX\n"); + return (true); +} + +//============================================================================= +// Get regular UltraSound configuration if any +//============================================================================= +static qboolean +GUS_GetGUSData (void) +{ + char *Ultrasnd; + int GusBase, Dma1, Dma2, Irq1, Irq2, i; + + Ultrasnd = getenv ("ULTRASND"); + if (Ultrasnd == NULL) + return (false); + + sscanf (Ultrasnd, "%x,%i,%i,%i,%i", &GusBase, &Dma1, &Dma2, &Irq1, &Irq2); + + DmaChannel = Dma1 & 0x07; + + // Make sure there is a GUS at GUS base + dos_outportb (GusBase + 0x08, 0x55); + if (dos_inportb (GusBase + 0x0A) != 0x55) + return (false); + dos_outportb (GusBase + 0x08, 0xAA); + if (dos_inportb (GusBase + 0x0A) != 0xAA) + return (false); + + Gf1TimerControl = GusBase + 0x008; + Gf1PageRegister = GusBase + 0x102; + Gf1RegisterSelect = GusBase + 0x103; + Gf1DataLow = GusBase + 0x104; + Gf1DataHigh = GusBase + 0x105; + + // Reset the GUS + SetGf18 (MASTER_RESET, 0x00); + Gf1Delay (); + Gf1Delay (); + SetGf18 (MASTER_RESET, 0x01); + Gf1Delay (); + Gf1Delay (); + + // Set to max (32) voices + SetGf18 (SET_VOICES, 0xDF); + + // Clear any pending IRQ's + ClearGf1Ints (); + + // Set all registers to known values + for (i = 0; i < 32; i++) { + dos_outportb (Gf1PageRegister, i); + SetGf18 (SET_CONTROL, 0x03); + SetGf18 (SET_VOLUME_CONTROL, 0x03); + Gf1Delay (); + SetGf18 (SET_CONTROL, 0x03); + SetGf18 (SET_VOLUME_CONTROL, 0x03); + SetGf116 (SET_START_HIGH, 0); + SetGf116 (SET_START_LOW, 0); + SetGf116 (SET_END_HIGH, 0); + SetGf116 (SET_END_LOW, 0); + SetGf116 (SET_ACC_HIGH, 0); + SetGf116 (SET_ACC_LOW, 0); + SetGf18 (SET_VOLUME_RATE, 63); + SetGf18 (SET_VOLUME_START, 5); + SetGf18 (SET_VOLUME_END, 251); + SetGf116 (SET_VOLUME, 5 << 8); + } + + // Clear any pending IRQ's + ClearGf1Ints (); + + // Enable DAC etc. + SetGf18 (MASTER_RESET, 0x07); + + // Enable line output so we can hear something + dos_outportb (GusBase, 0x08); + + HaveCodec = 0; + Con_Printf ("Sound Card is UltraSound\n"); + return (true); +} + + +//============================================================================= +// Programs the DMA controller to start DMAing in Auto-init mode +//============================================================================= +static void +GUS_StartDMA (BYTE DmaChannel, short *dma_buffer, int count) +{ + int mode; + int RealAddr; + + RealAddr = ptr2real (dma_buffer); + + if (DmaChannel <= 3) { + ModeReg = 0x0B; + DisableReg = 0x0A; + ClearReg = 0x0E; + } else { + ModeReg = 0xD6; + DisableReg = 0xD4; + ClearReg = 0xDC; + } + CountReg = CountRegs[DmaChannel]; + AddrReg = AddrRegs[DmaChannel]; + + dos_outportb (DisableReg, DmaChannel | 4); // disable channel + + // set mode- see "undocumented pc", p.876 + mode = (1 << 6) // single-cycle + + (0 << 5) // address increment + + (1 << 4) // auto-init dma + + (2 << 2) // read + + (DmaChannel & 0x03); // channel # + dos_outportb (ModeReg, mode); + + // set page + dos_outportb (PageRegs[DmaChannel], RealAddr >> 16); + + if (DmaChannel <= 3) { // address is in bytes + dos_outportb (0x0C, 0); // prepare to send 16-bit value + dos_outportb (AddrReg, RealAddr & 0xff); + dos_outportb (AddrReg, (RealAddr >> 8) & 0xff); + + dos_outportb (0x0C, 0); // prepare to send 16-bit value + dos_outportb (CountReg, (count - 1) & 0xff); + dos_outportb (CountReg, (count - 1) >> 8); + } else { // address is in words + dos_outportb (0xD8, 0); // prepare to send 16-bit value + dos_outportb (AddrReg, (RealAddr >> 1) & 0xff); + dos_outportb (AddrReg, (RealAddr >> 9) & 0xff); + + dos_outportb (0xD8, 0); // prepare to send 16-bit value + dos_outportb (CountReg, ((count >> 1) - 1) & 0xff); + dos_outportb (CountReg, ((count >> 1) - 1) >> 8); + } + + dos_outportb (ClearReg, 0); // clear write mask + dos_outportb (DisableReg, DmaChannel & ~4); +} + +//============================================================================= +// Starts the CODEC playing +//============================================================================= +static void +GUS_StartCODEC (int count, BYTE FSVal) +{ + int i, j; + + // Clear any pending IRQs + dos_inportb (CodecStatus); + dos_outportb (CodecStatus, 0); + + // Set mode to 2 + dos_outportb (CodecRegisterSelect, CODEC_MODE_AND_ID); + dos_outportb (CodecData, 0xC0); + + // Stop any playback or capture which may be happening + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG); + dos_outportb (CodecData, dos_inportb (CodecData) & 0xFC); + + // Set FS + dos_outportb (CodecRegisterSelect, CODEC_FS_FORMAT | 0x40); + dos_outportb (CodecData, FSVal | 0x50); // Or in stereo and 16 bit bits + + // Wait a bit + for (i = 0; i < 10; i++) + dos_inportb (CodecData); + + // Routine 1 to counter CODEC bug - wait for init bit to clear and then a + // bit longer (i=min loop count, j=timeout + for (i = 0, j = 0; i < 1000 && j < 0x7FFFF; j++) + if ((dos_inportb (CodecRegisterSelect) & 0x80) == 0) + i++; + + // Routine 2 to counter CODEC bug - this is from Forte's code. For me it + // does not seem to cure the problem, but is added security + // Waits till we can modify index register + for (j = 0; j < 0x7FFFF; j++) { + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG | 0x40); + if (dos_inportb (CodecRegisterSelect) == + (CODEC_INTERFACE_CONFIG | 0x40)) break; + } + + // Perform ACAL + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG | 0x40); + dos_outportb (CodecData, 0x08); + + // Clear MCE bit - this makes ACAL happen + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG); + + // Wait for ACAL to finish + for (j = 0; j < 0x7FFFF; j++) { + if ((dos_inportb (CodecRegisterSelect) & 0x80) != 0) + continue; + dos_outportb (CodecRegisterSelect, CODEC_ERROR_STATUS_AND_INIT); + if ((dos_inportb (CodecData) & 0x20) == 0) + break; + } + + // Clear ACAL bit + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG | 0x40); + dos_outportb (CodecData, 0x00); + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG); + + // Set some other junk + dos_outportb (CodecRegisterSelect, CODEC_LOOPBACK_CONTROL); + dos_outportb (CodecData, 0x00); + dos_outportb (CodecRegisterSelect, CODEC_PIN_CONTROL); + dos_outportb (CodecData, 0x08); // IRQ is disabled in PIN control + + // Set count (it doesn't really matter what value we stuff in here + dos_outportb (CodecRegisterSelect, CODEC_PLAYBACK_LOWER_BASE_COUNT); + dos_outportb (CodecData, count & 0xFF); + dos_outportb (CodecRegisterSelect, CODEC_PLAYBACK_UPPER_BASE_COUNT); + dos_outportb (CodecData, count >> 8); + + // Start playback + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG); + dos_outportb (CodecData, 0x01); +} + +//============================================================================= +// Starts the GF1 playing +//============================================================================= +static void +GUS_StartGf1 (int count, BYTE Voices) +{ + DWORD StartAddressL, EndAddressL, StartAddressR, EndAddressR; + + // Set number of voices to give us the sampling rate we want + SetGf18 (SET_VOICES, 0xC0 | (Voices - 1)); + + // Figure out addresses + StartAddressL = ConvertTo16 (0); + EndAddressL = ConvertTo16 (count - 2 - 2); + StartAddressR = ConvertTo16 (2); + EndAddressR = ConvertTo16 (count - 2); + + // Set left voice addresses + dos_outportb (Gf1PageRegister, 0); + SetGf116 (SET_START_LOW, StartAddressL << 9); + SetGf116 (SET_START_HIGH, StartAddressL >> 7); + SetGf116 (SET_ACC_LOW, StartAddressL << 9); + SetGf116 (SET_ACC_HIGH, StartAddressL >> 7); + SetGf116 (SET_END_LOW, EndAddressL << 9); + SetGf116 (SET_END_HIGH, EndAddressL >> 7); + // Set balance to full left + SetGf18 (SET_BALANCE, 0); + // Set volume to full + SetGf116 (SET_VOLUME, 0xFFF0); + // Set FC to 2 (so we play every second sample) + SetGf116 (SET_FREQUENCY, 0x0800); + + // Set right voice addresses + dos_outportb (Gf1PageRegister, 1); + SetGf116 (SET_START_LOW, StartAddressR << 9); + SetGf116 (SET_START_HIGH, StartAddressR >> 7); + SetGf116 (SET_ACC_LOW, StartAddressR << 9); + SetGf116 (SET_ACC_HIGH, StartAddressR >> 7); + SetGf116 (SET_END_LOW, EndAddressR << 9); + SetGf116 (SET_END_HIGH, EndAddressR >> 7); + // Set balance to full right + SetGf18 (SET_BALANCE, 15); + // Set volume to full + SetGf116 (SET_VOLUME, 0xFFF0); + // Set FC to 2 (so we play every second sample) + SetGf116 (SET_FREQUENCY, 0x0800); + + // Start voices + dos_outportb (Gf1PageRegister, 0); + SetGf18 (SET_CONTROL, 0x0C); + dos_outportb (Gf1PageRegister, 1); + SetGf18 (SET_CONTROL, 0x0C); + Gf1Delay (); + dos_outportb (Gf1PageRegister, 0); + SetGf18 (SET_CONTROL, 0x0C); + dos_outportb (Gf1PageRegister, 1); + SetGf18 (SET_CONTROL, 0x0C); +} + + +//============================================================================= +// Figures out what kind of UltraSound we have, if any, and starts it playing +//============================================================================= +qboolean +GUS_Init (void) +{ + int rc; + int RealAddr; + BYTE FSVal, Voices; + struct CodecRateStruct *CodecRate; + struct Gf1RateStruct *Gf1Rate; + + // See what kind of UltraSound we have, if any + if (GUS_GetIWData () == false) + if (GUS_GetMAXData () == false) + if (GUS_GetGUSData () == false) + return (false); + + shm = &sn; + + if (HaveCodec) { + // do 11khz sampling rate unless command line parameter wants + // different + shm->speed = 11025; + FSVal = 0x03; + rc = COM_CheckParm ("-sspeed"); + if (rc) { + shm->speed = Q_atoi (com_argv[rc + 1]); + + // Make sure rate not too high + if (shm->speed > 48000) + shm->speed = 48000; + + // Adjust speed to match one of the possible CODEC rates + for (CodecRate = CodecRates; CodecRate->Rate != 0; CodecRate++) { + if (shm->speed <= CodecRate->Rate) { + shm->speed = CodecRate->Rate; + FSVal = CodecRate->FSVal; + break; + } + } + } + // Always do 16 bit stereo + shm->channels = 2; + shm->samplebits = 16; + + // allocate buffer twice the size we need so we can get aligned + // buffer + dma_buffer = dos_getmemory (BUFFER_SIZE * 2); + if (dma_buffer == NULL) { + Con_Printf ("Couldn't allocate sound dma buffer"); + return false; + } + + RealAddr = ptr2real (dma_buffer); + RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE - 1); + dma_buffer = (short *) real2ptr (RealAddr); + + // Zero off DMA buffer + memset (dma_buffer, 0, BUFFER_SIZE); + + shm->soundalive = true; + shm->splitbuffer = false; + + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + shm->samples = BUFFER_SIZE / (shm->samplebits / 8); + + GUS_StartDMA (DmaChannel, dma_buffer, BUFFER_SIZE); + GUS_StartCODEC (BUFFER_SIZE, FSVal); + } else { + // do 19khz sampling rate unless command line parameter wants + // different + shm->speed = 19293; + Voices = 32; + rc = COM_CheckParm ("-sspeed"); + if (rc) { + shm->speed = Q_atoi (com_argv[rc + 1]); + + // Make sure rate not too high + if (shm->speed > 44100) + shm->speed = 44100; + + // Adjust speed to match one of the possible GF1 rates + for (Gf1Rate = Gf1Rates; Gf1Rate->Rate != 0; Gf1Rate++) { + if (shm->speed <= Gf1Rate->Rate) { + shm->speed = Gf1Rate->Rate; + Voices = Gf1Rate->Voices; + break; + } + } + } + // Always do 16 bit stereo + shm->channels = 2; + shm->samplebits = 16; + + // allocate buffer twice the size we need so we can get aligned + // buffer + dma_buffer = dos_getmemory (BUFFER_SIZE * 2); + if (dma_buffer == NULL) { + Con_Printf ("Couldn't allocate sound dma buffer"); + return false; + } + + RealAddr = ptr2real (dma_buffer); + RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE - 1); + dma_buffer = (short *) real2ptr (RealAddr); + + // Zero off DMA buffer + memset (dma_buffer, 0, BUFFER_SIZE); + + shm->soundalive = true; + shm->splitbuffer = false; + + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + shm->samples = BUFFER_SIZE / (shm->samplebits / 8); + + GUS_StartDMA (DmaChannel, dma_buffer, BUFFER_SIZE); + SetGf116 (SET_DMA_ADDRESS, 0x0000); + if (DmaChannel <= 3) + SetGf18 (DMA_CONTROL, 0x41); + else + SetGf18 (DMA_CONTROL, 0x45); + GUS_StartGf1 (BUFFER_SIZE, Voices); + } + return (true); +} + +//============================================================================= +// Returns the current playback position +//============================================================================= +int +GUS_GetDMAPos (void) +{ + int count; + + if (HaveCodec) { + // clear 16-bit reg flip-flop + // load the current dma count register + if (DmaChannel < 4) { + dos_outportb (0x0C, 0); + count = dos_inportb (CountReg); + count += dos_inportb (CountReg) << 8; + if (shm->samplebits == 16) + count /= 2; + count = shm->samples - (count + 1); + } else { + dos_outportb (0xD8, 0); + count = dos_inportb (CountReg); + count += dos_inportb (CountReg) << 8; + if (shm->samplebits == 8) + count *= 2; + count = shm->samples - (count + 1); + } + + } else { + // Read current position from GF1 + dos_outportb (Gf1PageRegister, 0); + count = (GetGf116 (GET_ACC_HIGH) << 7) & 0xFFFF; + // See which half of buffer we are in. Note that since this is 16 bit + // data we are playing, position is in 16 bit samples + if (GetGf18 (DMA_CONTROL) & 0x40) { + GUS_StartDMA (DmaChannel, dma_buffer, BUFFER_SIZE); + SetGf116 (SET_DMA_ADDRESS, 0x0000); + if (DmaChannel <= 3) + SetGf18 (DMA_CONTROL, 0x41); + else + SetGf18 (DMA_CONTROL, 0x45); + } + } + + shm->samplepos = count & (shm->samples - 1); + return (shm->samplepos); +} + +//============================================================================= +// Stops the UltraSound playback +//============================================================================= +void +GUS_Shutdown (void) +{ + if (HaveCodec) { + // Stop CODEC + dos_outportb (CodecRegisterSelect, CODEC_INTERFACE_CONFIG); + dos_outportb (CodecData, 0x01); + } else { + // Stop Voices + dos_outportb (Gf1PageRegister, 0); + SetGf18 (SET_CONTROL, 0x03); + dos_outportb (Gf1PageRegister, 1); + SetGf18 (SET_CONTROL, 0x03); + Gf1Delay (); + dos_outportb (Gf1PageRegister, 0); + SetGf18 (SET_CONTROL, 0x03); + dos_outportb (Gf1PageRegister, 1); + SetGf18 (SET_CONTROL, 0x03); + + // Stop any DMA + SetGf18 (DMA_CONTROL, 0x00); + GetGf18 (DMA_CONTROL); + } + + dos_outportb (DisableReg, DmaChannel | 4); // disable dma channel +} diff --git a/libs/audio/targets/snd_mem.c b/libs/audio/targets/snd_mem.c new file mode 100644 index 000000000..ccbc32d2d --- /dev/null +++ b/libs/audio/targets/snd_mem.c @@ -0,0 +1,405 @@ +/* + snd_mem.c + + sound caching + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/console.h" +#include "QF/qendian.h" +#include "QF/quakefs.h" +#include "QF/sound.h" +#include "QF/sys.h" + +int cache_full_cycle; + +byte *S_Alloc (int size); + +/* + ResampleSfx +*/ +void +ResampleSfx (sfx_t *sfx, int inrate, int inwidth, byte * data) +{ + int outcount; + int srcsample; + float stepscale; + int i; + int sample, samplefrac, fracstep; + sfxcache_t *sc; + short *is, *os; + unsigned char *ib, *ob; + + sc = Cache_Check (&sfx->cache); + if (!sc) + return; + + is = (short *) data; + os = (short *) sc->data; + ib = data; + ob = sc->data; + + stepscale = (float) inrate / shm->speed; // this is usually 0.5, 1, or + // + // 2 + + outcount = sc->length / stepscale; + + sc->speed = shm->speed; + if (loadas8bit->int_val) + sc->width = 1; + else + sc->width = 2; + sc->stereo = 0; + + // resample / decimate to the current source rate + if (stepscale == 1) { + if (inwidth == 1 && sc->width == 1) { + for (i = 0; i < outcount; i++) { + *ob++ = *ib++ - 128; + } + } else if (inwidth == 1 && sc->width == 2) { + for (i = 0; i < outcount; i++) { + *os++ = (*ib++ - 128) << 8; + } + } else if (inwidth == 2 && sc->width == 1) { + for (i = 0; i < outcount; i++) { + *ob++ = LittleShort (*is++) >> 8; + } + } else if (inwidth == 2 && sc->width == 2) { + for (i = 0; i < outcount; i++) { + *os++ = LittleShort (*is++); + } + } + } else { + // general case + if (snd_interp->int_val && stepscale < 1) { + int points = 1 / stepscale; + int j; + + for (i = 0; i < sc->length; i++) { + int s1, s2; + + if (inwidth == 2) { + s2 = s1 = LittleShort (is[0]); + if (i < sc->length - 1) + s2 = LittleShort (is[1]); + is++; + } else { + s2 = s1 = (ib[0] - 128) << 8; + if (i < sc->length - 1) + s2 = (ib[1] - 128) << 8; + ib++; + } + for (j = 0; j < points; j++) { + sample = s1 + (s2 - s1) * ((float) j) / points; + if (sc->width == 2) { + os[j] = sample; + } else { + ob[j] = sample >> 8; + } + } + if (sc->width == 2) { + os += points; + } else { + ob += points; + } + } + } else { + samplefrac = 0; + fracstep = stepscale * 256; + for (i = 0; i < outcount; i++) { + srcsample = samplefrac >> 8; + samplefrac += fracstep; + if (inwidth == 2) + sample = LittleShort (((short *) data)[srcsample]); + else + sample = + (int) ((unsigned char) (data[srcsample]) - 128) << 8; + if (sc->width == 2) + ((short *) sc->data)[i] = sample; + else + ((signed char *) sc->data)[i] = sample >> 8; + } + } + } + + sc->length = outcount; + if (sc->loopstart != -1) + sc->loopstart = sc->loopstart / stepscale; +} + +//============================================================================= + +/* + S_LoadSound +*/ +sfxcache_t * +S_LoadSound (sfx_t *s) +{ + char namebuffer[256]; + byte *data; + wavinfo_t info; + int len; + float stepscale; + sfxcache_t *sc; + byte stackbuf[1 * 1024]; // avoid dirtying the cache heap + +// see if still in memory + sc = Cache_Check (&s->cache); + if (sc) + return sc; + +//Con_Printf ("S_LoadSound: %x\n", (int)stackbuf); +// load it in + strcpy (namebuffer, "sound/"); + strncat (namebuffer, s->name, sizeof (namebuffer) - strlen (namebuffer)); + +// Con_Printf ("loading %s\n",namebuffer); + + data = COM_LoadStackFile (namebuffer, stackbuf, sizeof (stackbuf)); + + if (!data) { + Con_Printf ("Couldn't load %s\n", namebuffer); + return NULL; + } + + info = GetWavinfo (s->name, data, com_filesize); + if (info.channels != 1) { + Con_Printf ("%s is a stereo sample\n", s->name); + return NULL; + } + + stepscale = (float) info.rate / shm->speed; + len = info.samples / stepscale; + + if (loadas8bit->int_val) { + len = len * info.channels; + } else { + len = len * 2 * info.channels; + } + + sc = Cache_Alloc (&s->cache, len + sizeof (sfxcache_t), s->name); + + if (!sc) + return NULL; + + sc->length = info.samples; + sc->loopstart = info.loopstart; + sc->speed = info.rate; + sc->width = info.width; + sc->stereo = info.channels; + + ResampleSfx (s, sc->speed, sc->width, data + info.dataofs); + + return sc; +} + + + +/* + WAV loading +*/ + + +byte *data_p; +byte *iff_end; +byte *last_chunk; +byte *iff_data; +int iff_chunk_len; + + +short +GetLittleShort (void) +{ + short val = 0; + + val = *data_p; + val = val + (*(data_p + 1) << 8); + data_p += 2; + return val; +} + +int +GetLittleLong (void) +{ + int val = 0; + + val = *data_p; + val = val + (*(data_p + 1) << 8); + val = val + (*(data_p + 2) << 16); + val = val + (*(data_p + 3) << 24); + data_p += 4; + return val; +} + +void +FindNextChunk (char *name) +{ + while (1) { + data_p = last_chunk; + + if (data_p >= iff_end) { // didn't find the chunk + data_p = NULL; + return; + } + + data_p += 4; + iff_chunk_len = GetLittleLong (); + if (iff_chunk_len < 0) { + data_p = NULL; + return; + } +// if (iff_chunk_len > 1024*1024) +// Sys_Error ("FindNextChunk: %i length is past the 1 meg sanity limit", iff_chunk_len); + data_p -= 8; + last_chunk = data_p + 8 + ((iff_chunk_len + 1) & ~1); + if (!strncmp (data_p, name, 4)) + return; + } +} + +void +FindChunk (char *name) +{ + last_chunk = iff_data; + FindNextChunk (name); +} + + +void +DumpChunks (void) +{ + char str[5]; + + str[4] = 0; + data_p = iff_data; + do { + memcpy (str, data_p, 4); + data_p += 4; + iff_chunk_len = GetLittleLong (); + Con_Printf ("0x%x : %s (%d)\n", (int) (data_p - 4), str, iff_chunk_len); + data_p += (iff_chunk_len + 1) & ~1; + } while (data_p < iff_end); +} + +/* + GetWavinfo +*/ +wavinfo_t +GetWavinfo (char *name, byte * wav, int wavlength) +{ + wavinfo_t info; + int i; + int format; + int samples; + + memset (&info, 0, sizeof (info)); + + if (!wav) + return info; + + iff_data = wav; + iff_end = wav + wavlength; + +// find "RIFF" chunk + FindChunk ("RIFF"); + if (!(data_p && !strncmp (data_p + 8, "WAVE", 4))) { + Con_Printf ("Missing RIFF/WAVE chunks\n"); + return info; + } +// get "fmt " chunk + iff_data = data_p + 12; +// DumpChunks (); + + FindChunk ("fmt "); + if (!data_p) { + Con_Printf ("Missing fmt chunk\n"); + return info; + } + data_p += 8; + format = GetLittleShort (); + if (format != 1) { + Con_Printf ("Microsoft PCM format only\n"); + return info; + } + + info.channels = GetLittleShort (); + info.rate = GetLittleLong (); + data_p += 4 + 2; + info.width = GetLittleShort () / 8; + +// get cue chunk + FindChunk ("cue "); + if (data_p) { + data_p += 32; + info.loopstart = GetLittleLong (); +// Con_Printf("loopstart=%d\n", sfx->loopstart); + + // if the next chunk is a LIST chunk, look for a cue length marker + FindNextChunk ("LIST"); + if (data_p) { + if (!strncmp (data_p + 28, "mark", 4)) { // this is not a + // proper parse, but + // it works with + // cooledit... + data_p += 24; + i = GetLittleLong (); // samples in loop + info.samples = info.loopstart + i; +// Con_Printf("looped length: %i\n", i); + } + } + } else + info.loopstart = -1; + +// find data chunk + FindChunk ("data"); + if (!data_p) { + Con_Printf ("Missing data chunk\n"); + return info; + } + + data_p += 4; + samples = GetLittleLong () / info.width; + + if (info.samples) { + if (samples < info.samples) + Sys_Error ("Sound %s has a bad loop length", name); + } else + info.samples = samples; + + info.dataofs = data_p - wav; + + return info; +} diff --git a/libs/audio/targets/snd_mix.c b/libs/audio/targets/snd_mix.c new file mode 100644 index 000000000..654dbae0a --- /dev/null +++ b/libs/audio/targets/snd_mix.c @@ -0,0 +1,429 @@ +/* + snd_mix.c + + portable code to mix sounds for snd_dma.c + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/compat.h" +#include "QF/console.h" +#include "QF/sound.h" + +#ifdef _WIN32 +# include "winquake.h" +#else +# define DWORD unsigned long +#endif + +#define PAINTBUFFER_SIZE 512 +portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE * 2]; +int max_overpaint; // number of extra samples painted + + // due to phase shift +int snd_scaletable[32][256]; +int *snd_p, snd_linear_count, snd_vol; +short *snd_out; + +void Snd_WriteLinearBlastStereo16 (void); + +#ifndef USE_INTEL_ASM +void +Snd_WriteLinearBlastStereo16 (void) +{ + int i; + int val; + + for (i = 0; i < snd_linear_count; i += 2) { + val = (snd_p[i] * snd_vol) >> 8; + if (val > 0x7fff) + snd_out[i] = 0x7fff; + else if (val < (short) 0x8000) + snd_out[i] = (short) 0x8000; + else + snd_out[i] = val; + + val = (snd_p[i + 1] * snd_vol) >> 8; + if (val > 0x7fff) + snd_out[i + 1] = 0x7fff; + else if (val < (short) 0x8000) + snd_out[i + 1] = (short) 0x8000; + else + snd_out[i + 1] = val; + } +} +#endif + +void +S_TransferStereo16 (int endtime) +{ + int lpos; + int lpaintedtime; + DWORD *pbuf; + + snd_vol = volume->value * 256; + + snd_p = (int *) paintbuffer; + lpaintedtime = paintedtime; + +#ifdef _WIN32 + if (pDSBuf) { + pbuf = DSOUND_LockBuffer (true); + if (!pbuf) { + Con_Printf ("DSOUND_LockBuffer fails!\n"); + return; + } + } else +#endif + { + pbuf = (DWORD *) shm->buffer; + } + + while (lpaintedtime < endtime) { + // handle recirculating buffer issues + lpos = lpaintedtime & ((shm->samples >> 1) - 1); + + snd_out = (short *) pbuf + (lpos << 1); + + snd_linear_count = (shm->samples >> 1) - lpos; + if (lpaintedtime + snd_linear_count > endtime) + snd_linear_count = endtime - lpaintedtime; + + snd_linear_count <<= 1; + + // write a linear blast of samples + Snd_WriteLinearBlastStereo16 (); + + snd_p += snd_linear_count; + lpaintedtime += (snd_linear_count >> 1); + } + +#ifdef _WIN32 + if (pDSBuf) + DSOUND_LockBuffer (false); +#endif +} + +void +S_TransferPaintBuffer (int endtime) +{ + int out_idx; + int count; + int out_mask; + int *p; + int step; + int val; + int snd_vol; + DWORD *pbuf; + + if (shm->samplebits == 16 && shm->channels == 2) { + S_TransferStereo16 (endtime); + return; + } + + p = (int *) paintbuffer; + count = (endtime - paintedtime) * shm->channels; + out_mask = shm->samples - 1; + out_idx = paintedtime * shm->channels & out_mask; + step = 3 - shm->channels; + snd_vol = volume->value * 256; + +#ifdef _WIN32 + if (pDSBuf) { + pbuf = DSOUND_LockBuffer (true); + if (!pbuf) { + Con_Printf ("DSOUND_LockBuffer fails!\n"); + return; + } + } else +#endif + { + pbuf = (DWORD *) shm->buffer; + } + + if (shm->samplebits == 16) { + short *out = (short *) pbuf; + + while (count--) { + val = (*p * snd_vol) >> 8; + p += step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < (short) 0x8000) + val = (short) 0x8000; + out[out_idx] = val; + out_idx = (out_idx + 1) & out_mask; + } + } else if (shm->samplebits == 8) { + unsigned char *out = (unsigned char *) pbuf; + + while (count--) { + val = (*p * snd_vol) >> 8; + p += step; + if (val > 0x7fff) + val = 0x7fff; + else if (val < (short) 0x8000) + val = (short) 0x8000; + out[out_idx] = (val >> 8) + 128; + out_idx = (out_idx + 1) & out_mask; + } + } +#ifdef _WIN32 + if (pDSBuf) + DSOUND_LockBuffer (false); +#endif +} + + +/* + CHANNEL MIXING +*/ + +void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); +void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); + +void +S_PaintChannels (int endtime) +{ + int i; + int end; + channel_t *ch; + sfxcache_t *sc; + int ltime, count; + + while (paintedtime < endtime) { + // if paintbuffer is smaller than DMA buffer + end = endtime; + if (endtime - paintedtime > PAINTBUFFER_SIZE) + end = paintedtime + PAINTBUFFER_SIZE; + + // clear the paint buffer +// memset (paintbuffer, 0, +// (end - paintedtime) * sizeof (portable_samplepair_t)); + max_overpaint = 0; + + // paint in the channels. + ch = channels; + for (i = 0; i < total_channels; i++, ch++) { + if (!ch->sfx) + continue; + if (!ch->leftvol && !ch->rightvol) + continue; + sc = S_LoadSound (ch->sfx); + if (!sc) + continue; + + ltime = paintedtime; + + while (ltime < end) { // paint up to end + if (ch->end < end) + count = ch->end - ltime; + else + count = end - ltime; + + if (count > 0) { + if (sc->width == 1) + SND_PaintChannelFrom8 (ch, sc, count); + else + SND_PaintChannelFrom16 (ch, sc, count); + + ltime += count; + } + // if at end of loop, restart + if (ltime >= ch->end) { + if (sc->loopstart >= 0) { + ch->pos = sc->loopstart; + ch->end = ltime + sc->length - ch->pos; + } else { // channel just stopped + ch->sfx = NULL; + break; + } + } + } + + } + + // transfer out according to DMA format + S_TransferPaintBuffer (end); + + memmove (paintbuffer, paintbuffer + end - paintedtime, + max_overpaint * sizeof (paintbuffer[0])); + memset (paintbuffer + max_overpaint, 0, sizeof (paintbuffer) + - max_overpaint * sizeof (paintbuffer[0])); + + paintedtime = end; + } +} + +void +SND_InitScaletable (void) +{ + int i, j; + + for (i = 0; i < 32; i++) + for (j = 0; j < 256; j++) + snd_scaletable[i][j] = ((signed char) j) * i * 8; +} + + +#ifndef USE_INTEL_ASM + +void +SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) +{ + int data; + int *lscale, *rscale; + unsigned char *sfx; + int i; + + if (ch->leftvol > 255) + ch->leftvol = 255; + if (ch->rightvol > 255) + ch->rightvol = 255; + + lscale = snd_scaletable[ch->leftvol >> 3]; + rscale = snd_scaletable[ch->rightvol >> 3]; + sfx = (signed char *) sc->data + ch->pos; + + for (i = 0; i < count; i++) { + data = sfx[i]; + paintbuffer[i].left += lscale[data]; + paintbuffer[i].right += rscale[data]; + } + + ch->pos += count; +} + +#endif // !USE_INTEL_ASM + + +void +SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) +{ + int data; + int left, right; + int leftvol, rightvol; + signed short *sfx; + unsigned int i = 0; + unsigned int left_phase, right_phase; // Never allowed < 0 anyway + + leftvol = ch->leftvol; + rightvol = ch->rightvol; + + max_overpaint = max (abs (ch->phase), + max (abs (ch->oldphase), max_overpaint)); + + sfx = (signed short *) sc->data + ch->pos; + ch->pos += count; + + if (ch->phase >= 0) { + left_phase = ch->phase; + right_phase = 0; + } else { + left_phase = 0; + right_phase = -ch->phase; + } + + if (ch->oldphase != ch->phase) { + unsigned int old_phase_left, old_phase_right; + unsigned int new_phase_left, new_phase_right; + unsigned int count_left, count_right, c; + + if (ch->oldphase >= 0) { + old_phase_left = ch->oldphase; + old_phase_right = 0; + } else { + old_phase_left = 0; + old_phase_right = -ch->oldphase; + } + new_phase_left = left_phase; + new_phase_right = right_phase; + + if (new_phase_left > old_phase_left) + count_left = 2 * (new_phase_left - old_phase_left); + else + count_left = old_phase_left - new_phase_left; + + if (new_phase_right > old_phase_right) + count_right = 2 * (new_phase_right - old_phase_right); + else + count_right = old_phase_right - new_phase_right; + + c = min (count, max (count_right, count_left)); + count -= c; + while (c) { + int data = sfx[i]; + int left = (data * leftvol) >> 8; + int right = (data * rightvol) >> 8; + + if (new_phase_left < old_phase_left) { + if (!(count_left & 1)) { + paintbuffer[i + old_phase_left].left += left; + old_phase_left--; + } + count_left--; + } else if (new_phase_left > old_phase_left) { + paintbuffer[i + old_phase_left].left += left; + old_phase_left++; + paintbuffer[i + old_phase_left].left += left; + } else { + paintbuffer[i + old_phase_left].left += left; + } + + if (new_phase_right < old_phase_right) { + if (!(count_right & 1)) { + paintbuffer[i + old_phase_right].right += right; + old_phase_right--; + } + count_right--; + } else if (new_phase_right > old_phase_right) { + paintbuffer[i + old_phase_right].right += right; + old_phase_right++; + paintbuffer[i + old_phase_right].right += right; + } else { + paintbuffer[i + old_phase_right].right += right; + } + + c--; + i++; + } + } + + for (i = 0; i < count; i++) { + data = sfx[i]; + left = (data * leftvol) >> 8; + right = (data * rightvol) >> 8; + paintbuffer[i + left_phase].left += left; + paintbuffer[i + right_phase].right += right; + } +} diff --git a/libs/audio/targets/snd_mixa.S b/libs/audio/targets/snd_mixa.S new file mode 100644 index 000000000..e135fc383 --- /dev/null +++ b/libs/audio/targets/snd_mixa.S @@ -0,0 +1,232 @@ +/* + snd_mixa.S + + x86 assembly-language sound code + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "QF/asm_i386.h" +// #include "quakeasm.h" + +#ifdef USE_INTEL_ASM + + .text + + .extern C(snd_scaletable) + .extern C(paintbuffer) + .extern C(snd_linear_count) + .extern C(snd_p) + .extern C(snd_vol) + .extern C(snd_out) + +//---------------------------------------------------------------------- +// 8-bit sound-mixing code +//---------------------------------------------------------------------- + +#define ch 4+16 +#define sc 8+16 +#define count 12+16 + +.globl C(SND_PaintChannelFrom8) +C(SND_PaintChannelFrom8): + pushl %esi // preserve register variables + pushl %edi + pushl %ebx + pushl %ebp + +// int data; +// short *lscale, *rscale; +// unsigned char *sfx; +// int i; + + movl ch(%esp),%ebx + movl sc(%esp),%esi + +// if (ch->leftvol > 255) +// ch->leftvol = 255; +// if (ch->rightvol > 255) +// ch->rightvol = 255; + movl ch_leftvol(%ebx),%eax + movl ch_rightvol(%ebx),%edx + cmpl $255,%eax + jna LLeftSet + movl $255,%eax +LLeftSet: + cmpl $255,%edx + jna LRightSet + movl $255,%edx +LRightSet: + +// lscale = snd_scaletable[ch->leftvol >> 3]; +// rscale = snd_scaletable[ch->rightvol >> 3]; +// sfx = (signed char *)sc->data + ch->pos; +// ch->pos += count; + andl $0xF8,%eax + addl $(sfxc_data),%esi + andl $0xF8,%edx + movl ch_pos(%ebx),%edi + movl count(%esp),%ecx + addl %edi,%esi + shll $7,%eax + addl %ecx,%edi + shll $7,%edx + movl %edi,ch_pos(%ebx) + addl $(C(snd_scaletable)),%eax + addl $(C(snd_scaletable)),%edx + subl %ebx,%ebx + movb -1(%esi,%ecx,1),%bl + + testl $1,%ecx + jz LMix8Loop + + movl (%eax,%ebx,4),%edi + movl (%edx,%ebx,4),%ebp + addl C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size),%edi + addl C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size),%ebp + movl %edi,C(paintbuffer)+psp_left-psp_size(,%ecx,psp_size) + movl %ebp,C(paintbuffer)+psp_right-psp_size(,%ecx,psp_size) + movb -2(%esi,%ecx,1),%bl + + decl %ecx + jz LDone + +// for (i=0 ; i>8; +// if (val > 0x7fff) +// snd_out[i] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i] = (short)0x8000; +// else +// snd_out[i] = val; + movl -8(%ebx,%ecx,4),%eax + imull %esi,%eax + sarl $8,%eax + cmpl $0x7FFF,%eax + jg LClampHigh + cmpl $0xFFFF8000,%eax + jnl LClampDone + movl $0xFFFF8000,%eax + jmp LClampDone +LClampHigh: + movl $0x7FFF,%eax +LClampDone: + +// val = (snd_p[i+1]*snd_vol)>>8; +// if (val > 0x7fff) +// snd_out[i+1] = 0x7fff; +// else if (val < (short)0x8000) +// snd_out[i+1] = (short)0x8000; +// else +// snd_out[i+1] = val; + movl -4(%ebx,%ecx,4),%edx + imull %esi,%edx + sarl $8,%edx + cmpl $0x7FFF,%edx + jg LClampHigh2 + cmpl $0xFFFF8000,%edx + jnl LClampDone2 + movl $0xFFFF8000,%edx + jmp LClampDone2 +LClampHigh2: + movl $0x7FFF,%edx +LClampDone2: + shll $16,%edx + andl $0xFFFF,%eax + orl %eax,%edx + movl %edx,-4(%edi,%ecx,2) + +// } + subl $2,%ecx + jnz LWLBLoopTop + +// snd_p += snd_linear_count; + + popl %ebx + popl %edi + popl %esi + + ret + + +#endif // USE_INTEL_ASM + diff --git a/libs/audio/targets/snd_null.c b/libs/audio/targets/snd_null.c new file mode 100644 index 000000000..5d60afa67 --- /dev/null +++ b/libs/audio/targets/snd_null.c @@ -0,0 +1,145 @@ +/* + snd_null.c + + include this instead of all the other snd_* files to have no sound + code whatsoever + + Copyright (C) 1996-1997 Id Software, Inc. + 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 "QF/qtypes.h" +#include "QF/sound.h" + +// ======================================================================= +// Various variables also defined in snd_dma.c +// FIXME - should be put in one place +// ======================================================================= +channel_t channels[MAX_CHANNELS]; +int total_channels; +volatile dma_t *shm = 0; +cvar_t *loadas8bit; +int paintedtime; // sample PAIRS + + +cvar_t *bgmvolume; +cvar_t *volume; + + +void +S_Init (void) +{ + S_Init_Cvars (); +} + +void +S_Init_Cvars (void) +{ + volume = Cvar_Get ("volume", "0.7", CVAR_ARCHIVE, 0, "Volume level of sounds"); + loadas8bit = + Cvar_Get ("loadas8bit", "0", CVAR_NONE, 0, "Load samples as 8-bit"); + bgmvolume = Cvar_Get ("bgmvolume", "1", CVAR_ARCHIVE, 0, "CD music volume"); +} + +void +S_AmbientOff (void) +{ +} + +void +S_AmbientOn (void) +{ +} + +void +S_Shutdown (void) +{ +} + +void +S_TouchSound (char *sample) +{ +} + +void +S_ClearBuffer (void) +{ +} + +void +S_StaticSound (sfx_t *sfx, vec3_t origin, float vol, float attenuation) +{ +} + +void +S_StartSound (int entnum, int entchannel, sfx_t *sfx, vec3_t origin, float fvol, + float attenuation) +{ +} + +void +S_StopSound (int entnum, int entchannel) +{ +} + +sfx_t * +S_PrecacheSound (char *sample) +{ + return NULL; +} + +void +S_ClearPrecache (void) +{ +} + +void +S_Update (vec3_t origin, vec3_t v_forward, vec3_t v_right, vec3_t v_up) +{ +} + +void +S_StopAllSounds (qboolean clear) +{ +} + +void +S_BeginPrecaching (void) +{ +} + +void +S_EndPrecaching (void) +{ +} + +void +S_ExtraUpdate (void) +{ +} + +void +S_LocalSound (char *s) +{ +} diff --git a/libs/audio/targets/snd_oss.c b/libs/audio/targets/snd_oss.c new file mode 100644 index 000000000..2661fd6a1 --- /dev/null +++ b/libs/audio/targets/snd_oss.c @@ -0,0 +1,297 @@ +/* + snd_oss.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#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 "QF/cmd.h" +#include "QF/console.h" +#include "QF/qargs.h" +#include "QF/sound.h" + +#ifndef MAP_FAILED +# define MAP_FAILED ((void *) -1) +#endif + +static int audio_fd; +static int snd_inited; +static char *snd_dev = "/dev/dsp"; + +static int tryrates[] = { 11025, 22050, 22051, 44100, 8000 }; + +qboolean +SNDDMA_Init (void) +{ + int rc; + int fmt; + int tmp; + int i; + struct audio_buf_info info; + int caps; + int retries = 3; + + snd_inited = 0; + + // open snd_dev, confirm capability to mmap, and get size of dma buffer + if (snd_device->string[0]) + snd_dev = snd_device->string; + + audio_fd = open (snd_dev, O_RDWR); + if (audio_fd < 0) { // Failed open, retry up to 3 times + // if it's busy + while ((audio_fd < 0) && retries-- && + ((errno == EAGAIN) || (errno == EBUSY))) { + sleep (1); + audio_fd = open (snd_dev, O_RDWR); + } + if (audio_fd < 0) { + perror (snd_dev); + Con_Printf ("Could not open %s\n", snd_dev); + return 0; + } + } + + if ((rc = ioctl (audio_fd, SNDCTL_DSP_RESET, 0)) < 0) { + perror (snd_dev); + Con_Printf ("Could not reset %s\n", snd_dev); + close (audio_fd); + return 0; + } + + if (ioctl (audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) { + perror (snd_dev); + Con_Printf ("Sound driver too old\n"); + close (audio_fd); + return 0; + } + + if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) { + Con_Printf ("Sound device can't do memory-mapped I/O.\n"); + close (audio_fd); + return 0; + } + + if (ioctl (audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1) { + perror ("GETOSPACE"); + Con_Printf ("Um, can't do GETOSPACE?\n"); + close (audio_fd); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + + // set sample bits & speed + shm->samplebits = snd_bits->int_val; + + if (shm->samplebits != 16 && shm->samplebits != 8) { + ioctl (audio_fd, SNDCTL_DSP_GETFMTS, &fmt); + + if (fmt & AFMT_S16_LE) { // little-endian 16-bit signed + shm->samplebits = 16; + } else { + if (fmt & AFMT_U8) { // unsigned 8-bit ulaw + shm->samplebits = 8; + } + } + } + + if (snd_rate->int_val) { + shm->speed = snd_rate->int_val; + } else { + for (i = 0; i < (sizeof (tryrates) / 4); i++) + if (!ioctl (audio_fd, SNDCTL_DSP_SPEED, &tryrates[i])) + break; + shm->speed = tryrates[i]; + } + + if (!snd_stereo->int_val) { + shm->channels = 1; + } else { + shm->channels = 2; + } + + shm->samples = info.fragstotal * info.fragsize / (shm->samplebits / 8); + shm->submission_chunk = 1; + + // memory map the dma buffer + shm->buffer = (unsigned char *) mmap (NULL, info.fragstotal + * info.fragsize, + PROT_READ | PROT_WRITE, + MAP_FILE | MAP_SHARED, audio_fd, 0); + + if (shm->buffer == MAP_FAILED) { + perror (snd_dev); + Con_Printf ("Could not mmap %s\n", snd_dev); + close (audio_fd); + return 0; + } + + tmp = 0; + if (shm->channels == 2) + tmp = 1; + rc = ioctl (audio_fd, SNDCTL_DSP_STEREO, &tmp); + if (rc < 0) { + perror (snd_dev); + Con_Printf ("Could not set %s to stereo=%d", snd_dev, shm->channels); + close (audio_fd); + return 0; + } + + if (tmp) + shm->channels = 2; + else + shm->channels = 1; + + rc = ioctl (audio_fd, SNDCTL_DSP_SPEED, &shm->speed); + if (rc < 0) { + perror (snd_dev); + Con_Printf ("Could not set %s speed to %d", snd_dev, shm->speed); + close (audio_fd); + return 0; + } + + if (shm->samplebits == 16) { + rc = AFMT_S16_LE; + rc = ioctl (audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) { + perror (snd_dev); + Con_Printf ("Could not support 16-bit data. Try 8-bit.\n"); + close (audio_fd); + return 0; + } + } else if (shm->samplebits == 8) { + rc = AFMT_U8; + rc = ioctl (audio_fd, SNDCTL_DSP_SETFMT, &rc); + if (rc < 0) { + perror (snd_dev); + Con_Printf ("Could not support 8-bit data.\n"); + close (audio_fd); + return 0; + } + } else { + perror (snd_dev); + Con_Printf ("%d-bit sound not supported.", shm->samplebits); + close (audio_fd); + return 0; + } + +// toggle the trigger & start her up + + tmp = 0; + rc = ioctl (audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) { + perror (snd_dev); + Con_Printf ("Could not toggle.\n"); + close (audio_fd); + return 0; + } + tmp = PCM_ENABLE_OUTPUT; + rc = ioctl (audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp); + if (rc < 0) { + perror (snd_dev); + Con_Printf ("Could not toggle.\n"); + close (audio_fd); + return 0; + } + + shm->samplepos = 0; + + snd_inited = 1; + return 1; + +} + +int +SNDDMA_GetDMAPos (void) +{ + + struct count_info count; + + if (!snd_inited) + return 0; + + if (ioctl (audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) { + perror (snd_dev); + Con_Printf ("Uh, sound dead.\n"); + close (audio_fd); + snd_inited = 0; + return 0; + } +// shm->samplepos = (count.bytes / (shm->samplebits / 8)) & (shm->samples-1); +// fprintf(stderr, "%d \r", count.ptr); + shm->samplepos = count.ptr / (shm->samplebits / 8); + + return shm->samplepos; + +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + close (audio_fd); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ +} diff --git a/libs/audio/targets/snd_sdl.c b/libs/audio/targets/snd_sdl.c new file mode 100644 index 000000000..dbad4178e --- /dev/null +++ b/libs/audio/targets/snd_sdl.c @@ -0,0 +1,158 @@ +/* + snd_sdl.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include + +#include "QF/cmd.h" +#include "QF/console.h" +#include "QF/qargs.h" +#include "QF/sound.h" + +static dma_t the_shm; +static int snd_inited; + +extern int desired_speed; +extern int desired_bits; + +static void +paint_audio (void *unused, Uint8 * stream, int len) +{ + if (shm) { + shm->buffer = stream; + shm->samplepos += len / (shm->samplebits / 8); + // Check for samplepos overflow? + S_PaintChannels (shm->samplepos); + } +} + +qboolean +SNDDMA_Init (void) +{ + SDL_AudioSpec desired, obtained; + + snd_inited = 0; + + /* Set up the desired format */ + desired.freq = desired_speed; + switch (desired_bits) { + case 8: + desired.format = AUDIO_U8; + break; + case 16: + if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + desired.format = AUDIO_S16MSB; + else + desired.format = AUDIO_S16LSB; + break; + default: + Con_Printf ("Unknown number of audio bits: %d\n", desired_bits); + return 0; + } + desired.channels = 2; + desired.samples = 512; + desired.callback = paint_audio; + + /* Open the audio device */ + if (SDL_OpenAudio (&desired, &obtained) < 0) { + Con_Printf ("Couldn't open SDL audio: %s\n", SDL_GetError ()); + return 0; + } + + /* Make sure we can support the audio format */ + switch (obtained.format) { + case AUDIO_U8: + /* Supported */ + break; + case AUDIO_S16LSB: + case AUDIO_S16MSB: + if (((obtained.format == AUDIO_S16LSB) && + (SDL_BYTEORDER == SDL_LIL_ENDIAN)) || + ((obtained.format == AUDIO_S16MSB) && + (SDL_BYTEORDER == SDL_BIG_ENDIAN))) { + /* Supported */ + break; + } + /* Unsupported, fall through */ ; + default: + /* Not supported -- force SDL to do our bidding */ + SDL_CloseAudio (); + if (SDL_OpenAudio (&desired, NULL) < 0) { + Con_Printf ("Couldn't open SDL audio: %s\n", SDL_GetError ()); + return 0; + } + memcpy (&obtained, &desired, sizeof (desired)); + break; + } + SDL_PauseAudio (0); + + /* Fill the audio DMA information block */ + shm = &the_shm; + shm->splitbuffer = 0; + shm->samplebits = (obtained.format & 0xFF); + shm->speed = obtained.freq; + shm->channels = obtained.channels; + shm->samples = obtained.samples * shm->channels; + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = NULL; + + snd_inited = 1; + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + SDL_CloseAudio (); + snd_inited = 0; + } +} + +/* + + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer + +*/ +void +SNDDMA_Submit (void) +{ +} diff --git a/libs/audio/targets/snd_sgi.c b/libs/audio/targets/snd_sgi.c new file mode 100644 index 000000000..5048f17ed --- /dev/null +++ b/libs/audio/targets/snd_sgi.c @@ -0,0 +1,311 @@ +/* + snd_sgi.c + + sound support for sgi + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "QF/console.h" +#include "QF/qtypes.h" +#include "QF/qargs.h" +#include "QF/sound.h" + +static int snd_inited = 0; +static ALconfig alc; +static ALport alp; + +static int tryrates[] = { 11025, 22050, 44100, 8000 }; + +static unsigned char *dma_buffer, *write_buffer; +static int bufsize; +static int wbufp; +static int framecount; + +qboolean +SNDDMA_Init (void) +{ + ALpv alpv; + int i; + char *s; + + alc = alNewConfig (); + + if (!alc) { + Con_Printf ("Could not make an new sound config: %s\n", + alGetErrorString (oserror ())); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + + /* get & probe settings */ + /* sample format */ + if (alSetSampFmt (alc, AL_SAMPFMT_TWOSCOMP) < 0) { + Con_Printf ("Could not sample format of default output to two's " + "complement\n"); + alFreeConfig (alc); + return 0; + } + + /* sample bits */ + s = getenv ("QUAKE_SOUND_SAMPLEBITS"); + if (s) + shm->samplebits = atoi (s); + else if ((i = COM_CheckParm ("-sndbits")) != 0) + shm->samplebits = atoi (com_argv[i + 1]); + + if (shm->samplebits != 16 && shm->samplebits != 8) { + alpv.param = AL_WORDSIZE; + + if (alGetParams (AL_DEFAULT_OUTPUT, &alpv, 1) < 0) { + Con_Printf ("Could not get supported wordsize of default " + "output: %s\n", alGetErrorString (oserror ())); + return 0; + } + + if (alpv.value.i >= 16) { + shm->samplebits = 16; + } else { + if (alpv.value.i >= 8) + shm->samplebits = 8; + else { + Con_Printf ("Sound disabled since interface " + "doesn't even support 8 bit."); + alFreeConfig (alc); + return 0; + } + } + } + + /* sample rate */ + s = getenv ("QUAKE_SOUND_SPEED"); + if (s) + shm->speed = atoi (s); + else if ((i = COM_CheckParm ("-sndspeed")) != 0) + shm->speed = atoi (com_argv[i + 1]); + else { + alpv.param = AL_RATE; + + for (i = 0; i < sizeof (tryrates) / sizeof (int); i++) { + alpv.value.ll = alDoubleToFixed (tryrates[i]); + + if (alSetParams (AL_DEFAULT_OUTPUT, &alpv, 1) >= 0) + break; + } + + if (i >= sizeof (tryrates) / sizeof (int)) { + Con_Printf ("Sound disabled since interface doesn't even " + "support a sample rate of %d\n", tryrates[i - 1]); + alFreeConfig (alc); + return 0; + } + + shm->speed = tryrates[i]; + } + + /* channels */ + s = getenv ("QUAKE_SOUND_CHANNELS"); + if (s) + shm->channels = atoi (s); + else if ((i = COM_CheckParm ("-sndmono")) != 0) + shm->channels = 1; + else if ((i = COM_CheckParm ("-sndstereo")) != 0) + shm->channels = 2; + else + shm->channels = 2; + + /* set 'em */ + + /* channels */ + while (shm->channels > 0) { + if (alSetChannels (alc, shm->channels) < 0) { + Con_Printf ("Unable to set number of channels to %d, trying half\n", + shm->channels); + shm->channels /= 2; + } else + break; + } + + if (shm->channels <= 0) { + Con_Printf ("Sound disabled since interface doesn't even support 1 " + "channel\n"); + alFreeConfig (alc); + return 0; + } + + /* sample rate */ + alpv.param = AL_RATE; + alpv.value.ll = alDoubleToFixed (shm->speed); + + if (alSetParams (AL_DEFAULT_OUTPUT, &alpv, 1) < 0) { + Con_Printf ("Could not set samplerate of default output to %d: %s\n", + shm->speed, alGetErrorString (oserror ())); + alFreeConfig (alc); + return 0; + } + + /* set sizes of buffers relative to sizes of those for ** the 'standard' + frequency of 11025 ** ** use *huge* buffers since at least my indigo2 + has enough ** to do to get sound on the way anyway */ + bufsize = 32768 * (int) ((double) shm->speed / 11025.0); + + dma_buffer = malloc (bufsize); + + if (dma_buffer == NULL) { + Con_Printf ("Could not get %d bytes of memory for audio dma buffer\n", + bufsize); + alFreeConfig (alc); + return 0; + } + + write_buffer = malloc (bufsize); + + if (write_buffer == NULL) { + Con_Printf ("Could not get %d bytes of memory for audio write buffer\n", + bufsize); + free (dma_buffer); + alFreeConfig (alc); + return 0; + } + + /* sample bits */ + switch (shm->samplebits) { + case 24: + i = AL_SAMPLE_24; + break; + + case 16: + i = AL_SAMPLE_16; + break; + + default: + i = AL_SAMPLE_8; + break; + } + + if (alSetWidth (alc, i) < 0) { + Con_Printf ("Could not set wordsize of default output to %d: %s\n", + shm->samplebits, alGetErrorString (oserror ())); + free (write_buffer); + free (dma_buffer); + alFreeConfig (alc); + return 0; + } + + alp = alOpenPort ("quakeforge", "w", alc); + + if (!alp) { + Con_Printf ("Could not open sound port: %s\n", + alGetErrorString (oserror ())); + free (write_buffer); + free (dma_buffer); + alFreeConfig (alc); + return 0; + } + + shm->soundalive = true; + shm->samples = bufsize / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = dma_buffer; + + framecount = 0; + + snd_inited = 1; + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + /* Con_Printf("framecount: %d %d\n", (framecount * shm->channels) % + shm->samples, alGetFilled(alp)); */ + shm->samplepos = ((framecount - alGetFilled (alp)) + * shm->channels) % shm->samples; + return shm->samplepos; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + free (write_buffer); + free (dma_buffer); + alClosePort (alp); + alFreeConfig (alc); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + int bsize; + int bytes, b; + unsigned char *p; + int idx; + int stop = paintedtime; + + if (paintedtime < wbufp) + wbufp = 0; // reset + + bsize = shm->channels * (shm->samplebits / 8); + bytes = (paintedtime - wbufp) * bsize; + + if (!bytes) + return; + + if (bytes > bufsize) { + bytes = bufsize; + stop = wbufp + bytes / bsize; + } + + p = write_buffer; + idx = (wbufp * bsize) & (bufsize - 1); + + for (b = bytes; b; b--) { + *p++ = dma_buffer[idx]; + idx = (idx + 1) & (bufsize - 1); + } + + wbufp = stop; + + alWriteFrames (alp, write_buffer, bytes / bsize); + framecount += bytes / bsize; +} + +/* end of file */ diff --git a/libs/audio/targets/snd_sun.c b/libs/audio/targets/snd_sun.c new file mode 100644 index 000000000..5d6bf164e --- /dev/null +++ b/libs/audio/targets/snd_sun.c @@ -0,0 +1,228 @@ +/* + snd_sun.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#ifdef HAVE_UNISTD_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QF/qtypes.h" +#include "QF/sound.h" +#include "QF/qargs.h" +#include "QF/console.h" + +int audio_fd; +int snd_inited; + +static int wbufp; +static audio_info_t info; + +#define BUFFER_SIZE 8192 + +unsigned char dma_buffer[BUFFER_SIZE]; +unsigned char pend_buffer[BUFFER_SIZE]; +int pending; + +qboolean +SNDDMA_Init (void) +{ + if (snd_inited) { + printf ("Sound already init'd\n"); + return 0; + } + + shm = &sn; + shm->splitbuffer = 0; + + audio_fd = open ("/dev/audio", O_WRONLY | O_NDELAY); + + if (audio_fd < 0) { + if (errno == EBUSY) { + Con_Printf ("Audio device is being used by another process\n"); + } + perror ("/dev/audio"); + Con_Printf ("Could not open /dev/audio\n"); + return (0); + } + + if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) { + perror ("/dev/audio"); + Con_Printf ("Could not communicate with audio device.\n"); + close (audio_fd); + return 0; + } + // + // set to nonblock + // + if (fcntl (audio_fd, F_SETFL, O_NONBLOCK) < 0) { + perror ("/dev/audio"); + close (audio_fd); + return 0; + } + + AUDIO_INITINFO (&info); + + shm->speed = 11025; + + // try 16 bit stereo + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.sample_rate = 11025; + info.play.channels = 2; + info.play.precision = 16; + + if (ioctl (audio_fd, AUDIO_SETINFO, &info) < 0) { + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.sample_rate = 11025; + info.play.channels = 1; + info.play.precision = 16; + if (ioctl (audio_fd, AUDIO_SETINFO, &info) < 0) { + Con_Printf ("Incapable sound hardware.\n"); + close (audio_fd); + return 0; + } + Con_Printf ("16 bit mono sound initialized\n"); + shm->samplebits = 16; + shm->channels = 1; + } else { // 16 bit stereo + Con_Printf ("16 bit stereo sound initialized\n"); + shm->samplebits = 16; + shm->channels = 2; + } + + shm->soundalive = true; + shm->samples = sizeof (dma_buffer) / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + + snd_inited = 1; + + return 1; +} + +int +SNDDMA_GetDMAPos (void) +{ + if (!snd_inited) + return (0); + + if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) { + perror ("/dev/audio"); + Con_Printf ("Could not communicate with audio device.\n"); + close (audio_fd); + snd_inited = 0; + return (0); + } + + return ((info.play.samples * shm->channels) % shm->samples); +} + +int +SNDDMA_GetSamples (void) +{ + if (!snd_inited) + return (0); + + if (ioctl (audio_fd, AUDIO_GETINFO, &info) < 0) { + perror ("/dev/audio"); + Con_Printf ("Could not communicate with audio device.\n"); + close (audio_fd); + snd_inited = 0; + return (0); + } + + return info.play.samples; +} + +void +SNDDMA_Shutdown (void) +{ + if (snd_inited) { + close (audio_fd); + snd_inited = 0; + } +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + int bsize; + int bytes, b; + static unsigned char writebuf[1024]; + unsigned char *p; + int idx; + int stop = paintedtime; + + if (paintedtime < wbufp) + wbufp = 0; // reset + + bsize = shm->channels * (shm->samplebits / 8); + bytes = (paintedtime - wbufp) * bsize; + + if (!bytes) + return; + + if (bytes > sizeof (writebuf)) { + bytes = sizeof (writebuf); + stop = wbufp + bytes / bsize; + } + + p = writebuf; + idx = (wbufp * bsize) & (BUFFER_SIZE - 1); + + for (b = bytes; b; b--) { + *p++ = dma_buffer[idx]; + idx = (idx + 1) & (BUFFER_SIZE - 1); + } + + wbufp = stop; + + if (write (audio_fd, writebuf, bytes) < bytes) + printf ("audio can't keep up!\n"); + +} diff --git a/libs/audio/targets/snd_win.c b/libs/audio/targets/snd_win.c new file mode 100644 index 000000000..3fadfb114 --- /dev/null +++ b/libs/audio/targets/snd_win.c @@ -0,0 +1,725 @@ +/* + snd_win.c + + (description) + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#define CINTERFACE + +#include "winquake.h" +#include "QF/qargs.h" +#include "QF/console.h" +#include "QF/sound.h" + +#define iDirectSoundCreate(a,b,c) pDirectSoundCreate(a,b,c) + +HRESULT (WINAPI * pDirectSoundCreate) (GUID FAR * lpGUID, + LPDIRECTSOUND FAR * lplpDS, + IUnknown FAR * pUnkOuter); + +// 64K is > 1 second at 16-bit, 22050 Hz +#define WAV_BUFFERS 64 +#define WAV_MASK 0x3F +#define WAV_BUFFER_SIZE 0x0400 +#define SECONDARY_BUFFER_SIZE 0x10000 + +typedef enum { SIS_SUCCESS, SIS_FAILURE, SIS_NOTAVAIL } sndinitstat; + +static qboolean wavonly; +static qboolean dsound_init; +static qboolean wav_init; +static qboolean snd_firsttime = true, snd_isdirect, snd_iswave; +static qboolean primary_format_set; + +static int sample16; +static int snd_sent, snd_completed; + +/* + * Global variables. Must be visible to window-procedure function + * so it can unlock and free the data block after it has been played. + */ + +HANDLE hData; +HPSTR lpData, lpData2; + +HGLOBAL hWaveHdr; +LPWAVEHDR lpWaveHdr; + +HWAVEOUT hWaveOut; + +WAVEOUTCAPS wavecaps; + +DWORD gSndBufSize; + +MMTIME mmstarttime; + +LPDIRECTSOUND pDS; +LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; + +HINSTANCE hInstDS; + +sndinitstat SNDDMA_InitDirect (void); +qboolean SNDDMA_InitWav (void); + + +/* + S_BlockSound +*/ +void +S_BlockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) { + snd_blocked++; + + if (snd_blocked == 1) + waveOutReset (hWaveOut); + } +} + + +/* + S_UnblockSound +*/ +void +S_UnblockSound (void) +{ + +// DirectSound takes care of blocking itself + if (snd_iswave) { + snd_blocked--; + } +} + + +/* + FreeSound +*/ +void +FreeSound (void) +{ + int i; + + if (pDSBuf) { + IDirectSoundBuffer_Stop (pDSBuf); + IDirectSound_Release (pDSBuf); + } +// only release primary buffer if it's not also the mixing buffer we just released + if (pDSPBuf && (pDSBuf != pDSPBuf)) { + IDirectSound_Release (pDSPBuf); + } + + if (pDS) { + IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_NORMAL); + IDirectSound_Release (pDS); + } + + if (hWaveOut) { + waveOutReset (hWaveOut); + + if (lpWaveHdr) { + for (i = 0; i < WAV_BUFFERS; i++) + waveOutUnprepareHeader (hWaveOut, lpWaveHdr + i, + sizeof (WAVEHDR)); + } + + waveOutClose (hWaveOut); + + if (hWaveHdr) { + GlobalUnlock (hWaveHdr); + GlobalFree (hWaveHdr); + } + + if (hData) { + GlobalUnlock (hData); + GlobalFree (hData); + } + + } + + pDS = NULL; + pDSBuf = NULL; + pDSPBuf = NULL; + hWaveOut = 0; + hData = 0; + hWaveHdr = 0; + lpData = NULL; + lpWaveHdr = NULL; + dsound_init = false; + wav_init = false; +} + + +/* + SNDDMA_InitDirect + + Direct-Sound support +*/ +sndinitstat SNDDMA_InitDirect (void) +{ + DSBUFFERDESC dsbuf; + DSBCAPS dsbcaps; + DWORD dwSize, dwWrite; + DSCAPS dscaps; + WAVEFORMATEX format, pformat; + HRESULT hresult; + int reps; + + memset ((void *) &sn, 0, sizeof (sn)); + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof (format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + + if (!hInstDS) { + hInstDS = LoadLibrary ("dsound.dll"); + + if (hInstDS == NULL) { + Con_Printf ("Couldn't load dsound.dll\n"); + return SIS_FAILURE; + } + + pDirectSoundCreate = + (void *) GetProcAddress (hInstDS, "DirectSoundCreate"); + + if (!pDirectSoundCreate) { + Con_Printf ("Couldn't get DS proc addr\n"); + return SIS_FAILURE; + } + } + + while ((hresult = iDirectSoundCreate (NULL, &pDS, NULL)) != DS_OK) { + if (hresult != DSERR_ALLOCATED) { + Con_Printf ("DirectSound create failed\n"); + return SIS_FAILURE; + } + Con_Printf ("DirectSoundCreate failure\n" + " hardware already in use\n"); + return SIS_NOTAVAIL; + } + + dscaps.dwSize = sizeof (dscaps); + if (DS_OK != IDirectSound_GetCaps (pDS, &dscaps)) { + Con_Printf ("Couldn't get DS caps\n"); + } + + if (dscaps.dwFlags & DSCAPS_EMULDRIVER) { + Con_Printf ("No DirectSound driver installed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != + IDirectSound_SetCooperativeLevel (pDS, mainwindow, DSSCL_EXCLUSIVE)) { + Con_Printf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } +// get access to the primary buffer, if possible, so we can set the +// sound hardware format + memset (&dsbuf, 0, sizeof (dsbuf)); + dsbuf.dwSize = sizeof (DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER; + dsbuf.dwBufferBytes = 0; + dsbuf.lpwfxFormat = NULL; + + memset (&dsbcaps, 0, sizeof (dsbcaps)); + dsbcaps.dwSize = sizeof (dsbcaps); + primary_format_set = false; + + if (!COM_CheckParm ("-snoforceformat")) { + if (DS_OK == + IDirectSound_CreateSoundBuffer (pDS, &dsbuf, &pDSPBuf, NULL)) { + pformat = format; + + if (DS_OK != IDirectSoundBuffer_SetFormat (pDSPBuf, &pformat)) { + } else + primary_format_set = true; + } + } + + if (!primary_format_set || !COM_CheckParm ("-primarysound")) { + // create the secondary buffer we'll actually work with + memset (&dsbuf, 0, sizeof (dsbuf)); + dsbuf.dwSize = sizeof (DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; + dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; + dsbuf.lpwfxFormat = &format; + + memset (&dsbcaps, 0, sizeof (dsbcaps)); + dsbcaps.dwSize = sizeof (dsbcaps); + + if (DS_OK != + IDirectSound_CreateSoundBuffer (pDS, &dsbuf, &pDSBuf, NULL)) { + Con_Printf ("DS:CreateSoundBuffer Failed"); + FreeSound (); + return SIS_FAILURE; + } + + shm->channels = format.nChannels; + shm->samplebits = format.wBitsPerSample; + shm->speed = format.nSamplesPerSec; + + if (DS_OK != IDirectSound_GetCaps (pDSBuf, &dsbcaps)) { + Con_Printf ("DS:GetCaps failed\n"); + FreeSound (); + return SIS_FAILURE; + } + } else { + if (DS_OK != + IDirectSound_SetCooperativeLevel (pDS, mainwindow, + DSSCL_WRITEPRIMARY)) { + Con_Printf ("Set coop level failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (DS_OK != IDirectSound_GetCaps (pDSPBuf, &dsbcaps)) { + Con_Printf ("DS:GetCaps failed\n"); + return SIS_FAILURE; + } + + pDSBuf = pDSPBuf; + } + + // Make sure mixer is active + IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING); + + gSndBufSize = dsbcaps.dwBufferBytes; + +// initialize the buffer + reps = 0; + + while ((hresult = IDirectSoundBuffer_Lock (pDSBuf, 0, gSndBufSize, + (LPVOID *) & lpData, &dwSize, + NULL, NULL, 0)) != DS_OK) { + if (hresult != DSERR_BUFFERLOST) { + Con_Printf ("SNDDMA_InitDirect: DS::Lock Sound Buffer Failed\n"); + FreeSound (); + return SIS_FAILURE; + } + + if (++reps > 10000) { + Con_Printf ("SNDDMA_InitDirect: DS: couldn't restore buffer\n"); + FreeSound (); + return SIS_FAILURE; + } + + } + + memset (lpData, 0, dwSize); +// lpData[4] = lpData[5] = 0x7f; // force a pop for debugging + + IDirectSoundBuffer_Unlock (pDSBuf, lpData, dwSize, NULL, 0); + + /* we don't want anyone to access the buffer directly w/o locking it + first. */ + lpData = NULL; + + IDirectSoundBuffer_Stop (pDSBuf); + IDirectSoundBuffer_GetCurrentPosition (pDSBuf, &mmstarttime.u.sample, + &dwWrite); + IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING); + + shm->soundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits / 8) - 1; + + dsound_init = true; + + return SIS_SUCCESS; +} + + +/* + SNDDM_InitWav + + Crappy windows multimedia base +*/ +qboolean +SNDDMA_InitWav (void) +{ + WAVEFORMATEX format; + int i; + HRESULT hr; + + snd_sent = 0; + snd_completed = 0; + + shm = &sn; + + shm->channels = 2; + shm->samplebits = 16; + shm->speed = 11025; + + memset (&format, 0, sizeof (format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = shm->channels; + format.wBitsPerSample = shm->samplebits; + format.nSamplesPerSec = shm->speed; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.cbSize = 0; + format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; + + /* Open a waveform device for output using window callback. */ + while ((hr = waveOutOpen ((LPHWAVEOUT) & hWaveOut, WAVE_MAPPER, + &format, 0, 0L, + CALLBACK_NULL)) != MMSYSERR_NOERROR) { + if (hr != MMSYSERR_ALLOCATED) { + Con_Printf ("waveOutOpen failed\n"); + return false; + } + Con_Printf ("waveOutOpen failure;\n" " hardware already in use\n"); + return false; + } + + /* + * Allocate and lock memory for the waveform data. The memory + * for waveform data must be globally allocated with + * GMEM_MOVEABLE and GMEM_SHARE flags. + + */ + gSndBufSize = WAV_BUFFERS * WAV_BUFFER_SIZE; + hData = GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize); + if (!hData) { + Con_Printf ("Sound: Out of memory.\n"); + FreeSound (); + return false; + } + lpData = GlobalLock (hData); + if (!lpData) { + Con_Printf ("Sound: Failed to lock.\n"); + FreeSound (); + return false; + } + memset (lpData, 0, gSndBufSize); + + /* + * Allocate and lock memory for the header. This memory must + * also be globally allocated with GMEM_MOVEABLE and + * GMEM_SHARE flags. + */ + hWaveHdr = GlobalAlloc (GMEM_MOVEABLE | GMEM_SHARE, + (DWORD) sizeof (WAVEHDR) * WAV_BUFFERS); + + if (hWaveHdr == NULL) { + Con_Printf ("Sound: Failed to Alloc header.\n"); + FreeSound (); + return false; + } + + lpWaveHdr = (LPWAVEHDR) GlobalLock (hWaveHdr); + + if (lpWaveHdr == NULL) { + Con_Printf ("Sound: Failed to lock header.\n"); + FreeSound (); + return false; + } + + memset (lpWaveHdr, 0, sizeof (WAVEHDR) * WAV_BUFFERS); + + /* After allocation, set up and prepare headers. */ + for (i = 0; i < WAV_BUFFERS; i++) { + lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE; + lpWaveHdr[i].lpData = lpData + i * WAV_BUFFER_SIZE; + + if (waveOutPrepareHeader (hWaveOut, lpWaveHdr + i, sizeof (WAVEHDR)) != + MMSYSERR_NOERROR) { + Con_Printf ("Sound: failed to prepare wave headers\n"); + FreeSound (); + return false; + } + } + + shm->soundalive = true; + shm->splitbuffer = false; + shm->samples = gSndBufSize / (shm->samplebits / 8); + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) lpData; + sample16 = (shm->samplebits / 8) - 1; + + wav_init = true; + + return true; +} + +/* + SNDDMA_Init + + Try to find a sound device to mix for. + Returns false if nothing is found. +*/ + +qboolean +SNDDMA_Init (void) +{ + sndinitstat stat; + + if (COM_CheckParm ("-wavonly")) + wavonly = true; + + dsound_init = wav_init = 0; + + stat = SIS_FAILURE; // assume DirectSound won't + // initialize + + /* Init DirectSound */ + if (!wavonly) { + if (snd_firsttime || snd_isdirect) { + stat = SNDDMA_InitDirect ();; + + if (stat == SIS_SUCCESS) { + snd_isdirect = true; + + if (snd_firsttime) + Con_Printf ("DirectSound initialized\n"); + } else { + snd_isdirect = false; + Con_Printf ("DirectSound failed to init\n"); + } + } + } +// if DirectSound didn't succeed in initializing, try to initialize +// waveOut sound, unless DirectSound failed because the hardware is +// already allocated (in which case the user has already chosen not +// to have sound) + if (!dsound_init && (stat != SIS_NOTAVAIL)) { + if (snd_firsttime || snd_iswave) { + + snd_iswave = SNDDMA_InitWav (); + + if (snd_iswave) { + if (snd_firsttime) + Con_Printf ("Wave sound initialized\n"); + } else { + Con_Printf ("Wave sound failed to init\n"); + } + } + } + + snd_firsttime = false; + + if (!dsound_init && !wav_init) { + if (snd_firsttime) + Con_Printf ("No sound device initialized\n"); + + return 0; + } + + return 1; +} + +/* + SNDDMA_GetDMAPos + + return the current sample position (in mono samples read) + inside the recirculating dma buffer, so the mixing code will know + how many sample are required to fill it up. +*/ +int +SNDDMA_GetDMAPos (void) +{ + MMTIME mmtime; + int s = 0; + DWORD dwWrite; + + if (dsound_init) { + mmtime.wType = TIME_SAMPLES; + IDirectSoundBuffer_GetCurrentPosition (pDSBuf, &mmtime.u.sample, + &dwWrite); + s = mmtime.u.sample - mmstarttime.u.sample; + } else if (wav_init) { + s = snd_sent * WAV_BUFFER_SIZE; + } + + + s >>= sample16; + + s &= (shm->samples - 1); + + return s; +} + +/* + SNDDMA_Submit + + Send sound to device if buffer isn't really the dma buffer +*/ +void +SNDDMA_Submit (void) +{ + LPWAVEHDR h; + int wResult; + + if (!wav_init) + return; + + // + // find which sound blocks have completed + // + while (1) { + if (snd_completed == snd_sent) { + Con_DPrintf ("Sound overrun\n"); + break; + } + + if (!(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE)) { + break; + } + + snd_completed++; // this buffer has been played + } + + // + // submit two new sound blocks + // + while (((snd_sent - snd_completed) >> sample16) < 4) { + h = lpWaveHdr + (snd_sent & WAV_MASK); + + snd_sent++; + /* + * Now the data block can be sent to the output device. The + * waveOutWrite function returns immediately and waveform + * data is sent to the output device in the background. + */ + wResult = waveOutWrite (hWaveOut, h, sizeof (WAVEHDR)); + + if (wResult != MMSYSERR_NOERROR) { + Con_Printf ("Failed to write block to device\n"); + FreeSound (); + return; + } + } +} + +/* + SNDDMA_Shutdown + + Reset the sound device for exiting +*/ +void +SNDDMA_Shutdown (void) +{ + FreeSound (); +} + +DWORD * +DSOUND_LockBuffer (qboolean lockit) +{ + int reps; + + static DWORD dwSize; + static DWORD dwSize2; + static DWORD *pbuf1; + static DWORD *pbuf2; + HRESULT hresult; + + if (!pDSBuf) + return NULL; + + if (lockit) { + reps = 0; + while ((hresult = IDirectSoundBuffer_Lock (pDSBuf, 0, gSndBufSize, + (LPVOID *) & pbuf1, &dwSize, + (LPVOID *) & pbuf2, &dwSize2, + 0)) != DS_OK) { + if (hresult != DSERR_BUFFERLOST) { + Con_Printf + ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); + S_Shutdown (); + S_Startup (); + return NULL; + } + + if (++reps > 10000) { + Con_Printf + ("S_TransferStereo16: DS: couldn't restore buffer\n"); + S_Shutdown (); + S_Startup (); + return NULL; + } + } + } else { + IDirectSoundBuffer_Unlock (pDSBuf, pbuf1, dwSize, NULL, 0); + pbuf1 = NULL; + pbuf2 = NULL; + dwSize = 0; + dwSize2 = 0; + } + return (pbuf1); +} + +void +DSOUND_ClearBuffer (int clear) +{ + DWORD *pData; + +// FIXME: this should be called with 2nd pbuf2 = NULL, dwsize =0 + pData = DSOUND_LockBuffer (true); + memset (pData, clear, shm->samples * shm->samplebits / 8); + DSOUND_LockBuffer (false); +} + +void +DSOUND_Restore (void) +{ +// if the buffer was lost or stopped, restore it and/or restart it + DWORD dwStatus; + + if (!pDSBuf) + return; + + if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DD_OK) + Con_Printf ("Couldn't get sound buffer status\n"); + + if (dwStatus & DSBSTATUS_BUFFERLOST) + IDirectSoundBuffer_Restore (pDSBuf); + + if (!(dwStatus & DSBSTATUS_PLAYING)) + IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING); + + return; +} diff --git a/nq/source/Makefile.am b/nq/source/Makefile.am index be11e25a0..ed2c5a8f5 100644 --- a/nq/source/Makefile.am +++ b/nq/source/Makefile.am @@ -38,14 +38,13 @@ bin_PROGRAMS = @NQ_TARGETS@ EXTRA_PROGRAMS= nq-3dfx nq-fbdev nq-glx nq-mgl nq-sdl \ nq-sgl nq-svga nq-wgl nq-x11 nq-server -noinst_LIBRARIES= libqfsys.a libqfsnd.a libqfjs.a libqfnet.a +noinst_LIBRARIES= libqfsys.a libqfjs.a libqfnet.a #if ASM_ARCH math_ASM= cl_math.S soft_ASM= d_draw.S d_draw16.S d_parta.S d_polysa.S d_scana.S d_spr8.S \ d_varsa.S sw_raclipa.S sw_raliasa.S sw_rdrawa.S sw_redgea.S sw_rvarsa.S \ surf16.S surf8.S -sound_ASM= snd_mixa.S common_ASM= sys_ia32.S worlda.S $(math_ASM) #endif @@ -64,34 +63,6 @@ libqfsys_a_SOURCES= sys_unix.c endif EXTRA_libqfsys_a_SOURCES= dirent.c fnmatch.c sys_unix.c sys_win.c -# -# ... Digital audio -# -if SNDTYPE_ALSA_0_5 -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c $(sound_ASM) -endif -if SNDTYPE_ALSA_0_9 -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_9.c $(sound_ASM) -endif -if SNDTYPE_MME -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_mme.c $(sound_ASM) -endif -if SNDTYPE_OSS -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_oss.c $(sound_ASM) -endif -if SNDTYPE_SUN -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sun.c $(sound_ASM) -endif -if SNDTYPE_WIN32 -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_win.c $(sound_ASM) -endif -if SNDTYPE_NULL -libqfsnd_a_SOURCES= snd_null.c $(sound_ASM) -endif -EXTRA_libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c \ - snd_alsa_0_9.c snd_oss.c snd_sun.c snd_win.c snd_null.c \ - $(sound_ASM) - # # ... Joystick # @@ -116,8 +87,9 @@ EXTRA_libqfnet_a_SOURCES=net_dos.c net_bw.c net_ipx.c net_mp.c net_ser.c \ client_LIBS= $(top_builddir)/libs/gamecode/libQFgamecode.la \ $(top_builddir)/libs/util/libQFutil.la \ $(top_builddir)/libs/audio/cd/libQFcd.la \ - -L. -lqfsys -lqfsnd -lqfjs -lqfnet $(SOUND_LIBS) $(NET_LIBS) -client_LIB_DEPS= libqfsys.a libqfsnd.a libqfjs.a libqfnet.a + $(top_builddir)/libs/audio/targets/libQFsound.la \ + -L. -lqfsys -lqfjs -lqfnet $(SOUND_LIBS) $(NET_LIBS) +client_LIB_DEPS= libqfsys.a libqfjs.a libqfnet.a client_SOURCES= cl_cam.c cl_cmd.c cl_demo.c cl_input.c cl_main.c cl_parse.c \ cl_tent.c console.c keys.c sbar.c r_part.c r_view.c \ diff --git a/nq/source/cl_parse.c b/nq/source/cl_parse.c index 81340099b..3f512268f 100644 --- a/nq/source/cl_parse.c +++ b/nq/source/cl_parse.c @@ -223,6 +223,8 @@ CL_KeepaliveMessage (void) SZ_Clear (&cls.message); } +struct model_s *snd_worldmodel; + /* ================== CL_ParseServerInfo @@ -326,6 +328,7 @@ CL_ParseServerInfo (void) // local state cl_entities[0].model = cl.worldmodel = cl.model_precache[1]; + snd_worldmodel = cl.worldmodel; R_NewMap (); @@ -703,6 +706,7 @@ CL_ParseStaticSound (void) #define SHOWNET(x) if(cl_shownet->int_val==2)Con_Printf ("%3i:%s\n", net_message->readcount-1, x); +int snd_viewentity; /* ===================== CL_ParseServerMessage @@ -806,6 +810,7 @@ CL_ParseServerMessage (void) case svc_setview: cl.viewentity = MSG_ReadShort (net_message); + snd_viewentity = cl.viewentity; // FIXME: evil hack break; case svc_lightstyle: diff --git a/qw/include/bspfile.h b/qw/include/bspfile.h new file mode 100644 index 000000000..0d6d3a9c5 --- /dev/null +++ b/qw/include/bspfile.h @@ -0,0 +1,262 @@ +/* + bspfile.h + + BSP (Binary Space Partitioning) file definitions + + Copyright (C) 1996-1997 Id Software, Inc. + + 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$ +*/ +#ifndef __bspfile_h_ +#define __bspfile_h_ + +#include "QF/qtypes.h" +#include "QF/sound.h" + +// upper design bounds + +#define MAX_MAP_HULLS 4 + +#define MAX_MAP_MODELS 256 +#define MAX_MAP_BRUSHES 4096 +#define MAX_MAP_ENTITIES 1024 +#define MAX_MAP_ENTSTRING 65536 + +#define MAX_MAP_PLANES 8192 +#define MAX_MAP_NODES 32767 // because negative shorts are contents +#define MAX_MAP_CLIPNODES 32767 // +#define MAX_MAP_LEAFS 32767 // +#define MAX_MAP_VERTS 65535 +#define MAX_MAP_FACES 65535 +#define MAX_MAP_MARKSURFACES 65535 +#define MAX_MAP_TEXINFO 4096 +#define MAX_MAP_EDGES 256000 +#define MAX_MAP_SURFEDGES 512000 +#define MAX_MAP_MIPTEX 0x200000 +#define MAX_MAP_LIGHTING 0x100000 +#define MAX_MAP_VISIBILITY 0x100000 + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + + +//============================================================================= + + +#define BSPVERSION 29 + +typedef struct { + int fileofs; + int filelen; +} lump_t; + +#define LUMP_ENTITIES 0 +#define LUMP_PLANES 1 +#define LUMP_TEXTURES 2 +#define LUMP_VERTEXES 3 +#define LUMP_VISIBILITY 4 +#define LUMP_NODES 5 +#define LUMP_TEXINFO 6 +#define LUMP_FACES 7 +#define LUMP_LIGHTING 8 +#define LUMP_CLIPNODES 9 +#define LUMP_LEAFS 10 +#define LUMP_MARKSURFACES 11 +#define LUMP_EDGES 12 +#define LUMP_SURFEDGES 13 +#define LUMP_MODELS 14 + +#define HEADER_LUMPS 15 + +typedef struct { + float mins[3], maxs[3]; + float origin[3]; + int headnode[MAX_MAP_HULLS]; + int visleafs; // not including the solid leaf 0 + int firstface, numfaces; +} dmodel_t; + +typedef struct { + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +typedef struct { + int nummiptex; + int dataofs[4]; // [nummiptex] +} dmiptexlump_t; + +#define MIPLEVELS 4 +typedef struct miptex_s { + char name[16]; + unsigned int width, height; + unsigned int offsets[MIPLEVELS]; // four mip maps stored +} miptex_t; + + +typedef struct { + float point[3]; +} dvertex_t; + + +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 + +// 3-5 are non-axial planes snapped to the nearest +#define PLANE_ANYX 3 +#define PLANE_ANYY 4 +#define PLANE_ANYZ 5 + +typedef struct { + float normal[3]; + float dist; + int type; // PLANE_X - PLANE_ANYZ ?remove? trivial to regenerate +} dplane_t; + +#define CONTENTS_EMPTY -1 +#define CONTENTS_SOLID -2 +#define CONTENTS_WATER -3 +#define CONTENTS_SLIME -4 +#define CONTENTS_LAVA -5 +#define CONTENTS_SKY -6 + +// !!! if this is changed, it must be changed in asm_i386.h too !!! +typedef struct { + int planenum; + short children[2]; // negative numbers are -(leafs+1), not nodes + short mins[3]; // for sphere culling + short maxs[3]; + unsigned short firstface; + unsigned short numfaces; // counting both sides +} dnode_t; + +typedef struct { + int planenum; + short children[2]; // negative numbers are contents +} dclipnode_t; + + +typedef struct texinfo_s { + float vecs[2][4]; // [s/t][xyz offset] + int miptex; + int flags; +} texinfo_t; +#define TEX_SPECIAL 1 // sky or slime, no lightmap or 256 subdivision + +// note that edge 0 is never used, because negative edge nums are used for +// counterclockwise use of the edge in a face +typedef struct { + unsigned short v[2]; // vertex numbers +} dedge_t; + +#define MAXLIGHTMAPS 4 +typedef struct { + short planenum; + short side; + + int firstedge; // we must support > 64k edges + short numedges; + short texinfo; + +// lighting info + byte styles[MAXLIGHTMAPS]; + int lightofs; // start of [numstyles*surfsize] samples +} dface_t; + + + +// leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas +// all other leafs need visibility info +typedef struct { + int contents; + int visofs; // -1 = no visibility info + + short mins[3]; // for frustum culling + short maxs[3]; + + unsigned short firstmarksurface; + unsigned short nummarksurfaces; + + byte ambient_level[NUM_AMBIENTS]; +} dleaf_t; + +//============================================================================ + +#ifndef QUAKE_GAME + +// the utilities get to be lazy and just use large static arrays + +extern int nummodels; +extern dmodel_t dmodels[MAX_MAP_MODELS]; + +extern int visdatasize; +extern byte dvisdata[MAX_MAP_VISIBILITY]; + +extern int lightdatasize; +extern byte dlightdata[MAX_MAP_LIGHTING]; + +extern int texdatasize; +extern byte dtexdata[MAX_MAP_MIPTEX]; // (dmiptexlump_t) + +extern int entdatasize; +extern char dentdata[MAX_MAP_ENTSTRING]; + +extern int numleafs; +extern dleaf_t dleafs[MAX_MAP_LEAFS]; + +extern int numplanes; +extern dplane_t dplanes[MAX_MAP_PLANES]; + +extern int numvertexes; +extern dvertex_t dvertexes[MAX_MAP_VERTS]; + +extern int numnodes; +extern dnode_t dnodes[MAX_MAP_NODES]; + +extern int numtexinfo; +extern texinfo_t texinfo[MAX_MAP_TEXINFO]; + +extern int numfaces; +extern dface_t dfaces[MAX_MAP_FACES]; + +extern int numclipnodes; +extern dclipnode_t dclipnodes[MAX_MAP_CLIPNODES]; + +extern int numedges; +extern dedge_t dedges[MAX_MAP_EDGES]; + +extern int nummarksurfaces; +extern unsigned short dmarksurfaces[MAX_MAP_MARKSURFACES]; + +extern int numsurfedges; +extern int dsurfedges[MAX_MAP_SURFEDGES]; + + +void LoadBSPFile (char *filename); +void WriteBSPFile (char *filename); +void PrintBSPFileSizes (void); + +#endif +#endif // __bspfile_h_ diff --git a/qw/source/Makefile.am b/qw/source/Makefile.am index 7345e2d4f..c157fc534 100644 --- a/qw/source/Makefile.am +++ b/qw/source/Makefile.am @@ -40,7 +40,7 @@ EXTRA_PROGRAMS= qw-client-3dfx qw-client-fbdev \ qw-client-sgl qw-client-svga qw-client-wgl \ qw-client-x11 qw-server -noinst_LIBRARIES= libqfjs.a libqfnet.a libqfsnd.a libqfsys_cl.a libqfsys_sv.a +noinst_LIBRARIES= libqfjs.a libqfnet.a libqfsys_cl.a libqfsys_sv.a if PACKETLOG packetlogger = net_packetlog.c @@ -103,40 +103,6 @@ libqfsys_cl_a_SOURCES= cl_sys_unix.c endif EXTRA_libqfsys_cl_a_SOURCES= cl_sys_sdl.c cl_sys_unix.c cl_sys_win.c sys_win.c fnmatch.c dirent.c -# -# ... Digital audio -# -if SNDTYPE_ALSA_0_5 -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c -endif -if SNDTYPE_ALSA_0_9 -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_9.c -endif -if SNDTYPE_MME -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_mme.c -endif -if SNDTYPE_OSS -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_oss.c -endif -if SNDTYPE_SDL -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sdl.c -endif -if SNDTYPE_SGI -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sgi.c -endif -if SNDTYPE_SUN -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_sun.c -endif -if SNDTYPE_WIN32 -libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_win.c -endif -if SNDTYPE_NULL -libqfsnd_a_SOURCES= snd_null.c -endif -EXTRA_libqfsnd_a_SOURCES= snd_dma.c snd_mem.c snd_mix.c snd_alsa_0_5.c \ - snd_alsa_0_9.c snd_oss.c snd_sdl.c snd_sgi.c \ - snd_sun.c snd_win.c snd_null.c - # # ... Joystick # @@ -155,12 +121,13 @@ EXTRA_libqfjs_a_SOURCES= joy_linux.c joy_win.c joy_null.c CLIENT_LIBS= $(top_builddir)/libs/util/libQFutil.la \ $(top_builddir)/libs/gamecode/libQFgamecode.la \ $(top_builddir)/libs/audio/cd/libQFcd.la \ - -L. -lqfnet -lqfsys_cl -lqfsnd -lqfjs \ + $(top_builddir)/libs/audio/targets/libQFsound.la \ + -L. -lqfnet -lqfsys_cl -lqfjs \ $(SOUND_LIBS) $(NET_LIBS) $(JOY_LIBS) -CLIENT_LIB_DEPS= libqfnet.a libqfsys_cl.a libqfsnd.a libqfjs.a +CLIENT_LIB_DEPS= libqfnet.a libqfsys_cl.a libqfjs.a if ASM_ARCH -client_ASM= snd_mixa.S cl_math.S +client_ASM= cl_math.S endif client_SOURCES= cl_cam.c cl_cmd.c cl_cvar.c cl_demo.c cl_ents.c cl_input.c \ diff --git a/qw/source/cl_parse.c b/qw/source/cl_parse.c index f3134de4b..242ef25f7 100644 --- a/qw/source/cl_parse.c +++ b/qw/source/cl_parse.c @@ -249,6 +249,7 @@ CL_CheckOrDownloadFile (char *filename) return false; } +struct model_s *snd_worldmodel; /* Model_NextDownload */ @@ -301,6 +302,9 @@ Model_NextDownload (void) // all done cl.worldmodel = cl.model_precache[1]; + // FIXME: evil hack for sound + snd_worldmodel = cl.worldmodel; + R_NewMap (); Team_NewMap (); Hunk_Check (); // make sure nothing is hurt @@ -595,6 +599,10 @@ void Draw_ClearCache (void); void CL_ClearBaselines (void); // LordHavoc: BIG BUG-FIX! +// FIXME: Evil hack that doesn't deserve to see the light of day. +// (pending merge of nq and qw client_stat_t's) +int snd_viewentity; + /* CL_ParseServerData */ @@ -658,8 +666,9 @@ CL_ParseServerData (void) cl.playernum &= ~128; } - // evil hack so NQ and QW can share sound code + // FIXME: evil hack so NQ and QW can share sound code cl.viewentity = cl.playernum + 1; + snd_viewentity = cl.playernum + 1; // get the full level name str = MSG_ReadString (net_message);