text: // Emacs style mode select	 -*- C++ -*- 
  //-----------------------------------------------------------------------------
  //
  // $Id:$
  //
  // Copyright (C) 1993-1996 by 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.
  //
  // $Log:$
  //
  // DESCRIPTION:
  //	System interface for sound.
  //
  //-----------------------------------------------------------------------------
  
  static const char
  rcsid[] = "$Id: i_unix.c,v 1.5 1997/02/03 22:45:10 b1 Exp $";
  
  #include <stdio.h>
  #include <stdlib.h>
  #include <stdarg.h>
  
  #include <math.h>
  
  // #include <sys/time.h>
  // #include <sys/types.h>
  
  // #ifndef LINUX
  // #include <sys/filio.h>
  // #endif
  
  // #include <fcntl.h>
  //#include <unistd.h>
  //#include <sys/ioctl.h>
  
  // Linux voxware output.
  // #include <linux/soundcard.h>
  
  // Timer stuff. Experimental.
  #include <time.h>
  #include <signal.h>
  
  //#include "z_zone.h"
  
  //#include "i_system.h"
  //#include "i_sound.h"
  //#include "m_argv.h"
  //#include "m_misc.h"
  //#include "w_wad.h"
  
  #include "h2def.h"
  
  extern sfxinfo_t S_sfx[];
  
  int snd_Channels;
  
  int snd_DesiredMusicDevice, snd_DesiredSfxDevice;
  
  int snd_MusicDevice,    // current music card # (index to dmxCodes)
  	snd_SfxDevice,      // current sfx card # (index to dmxCodes)
  	snd_MaxVolume,      // maximum volume for sound
  	snd_MusicVolume;    // maximum volume for music
  
  int     snd_SBport, snd_SBirq, snd_SBdma;       // sound blaster variables
  int     snd_Mport;                              // midi variables
  
  #if 0
  
  // UNIX hack, to be removed.
  #ifdef SNDSERV
  // Separate sound server process.
  FILE*	sndserver=0;
  char*	sndserver_filename = "./sndserver ";
  #elif SNDINTR
  
  // Update all 30 millisecs, approx. 30fps synchronized.
  // Linux resolution is allegedly 10 millisecs,
  //	scale is microseconds.
  #define SOUND_INTERVAL		 500
  
  // Get the interrupt. Set duration in millisecs.
  int I_SoundSetTimer( int duration_of_tick );
  void I_SoundDelTimer( void );
  #else
  // None?
  #endif
  
  #endif
  
  
  int SoundDev_Init();
  int SoundDev_DeInit();
  int SoundDev_Submit();
  
  int SoundDev_WriteStereoSamples(short *buffer, int cnt);
  int SoundDev_WriteStereoSamples2(
  	short *buffer, int cnt, int ovcnt);
  int SoundDev_WriteSamples(short *buffer, int cnt);
  
  int FRGL_TimeMS();
  
  // A quick hack to establish a protocol between
  // synchronous mix buffer updates and asynchronous
  // audio writes. Probably redundant with gametic.
  static int flag = 0;
  
  // The number of internal mixing channels,
  //	the samples calculated for each mixing step,
  //	the size of the 16bit, 2 hardware channel (stereo)
  //	mixing buffer, and the samplerate of the raw data.
  
  
  // Needed for calling the actual sound output.
  #define SAMPLECOUNT		512
  #define NUM_CHANNELS		8
  // It is 2 for 16bit, and 2 for two channels.
  #define BUFMUL									4
  #define MIXBUFFERSIZE		(SAMPLECOUNT*BUFMUL)
  
  #define SAMPLERATE		11025	// Hz
  // #define SAMPLERATE		16000	// Hz
  #define SAMPLESIZE		2	 	// 16bit
  
  // The actual lengths of all sound effects.
  int 		lengths[NUMSFX];
  
  // The actual output device.
  int	audio_fd;
  
  // The global mixing buffer.
  // Basically, samples from all active internal channels
  //	are modifed and added, and stored in the buffer
  //	that is submitted to the audio device.
  signed short	mixbuffer[MIXBUFFERSIZE];
  
  
  // The channel step amount...
  unsigned int	channelstep[NUM_CHANNELS];
  // ... and a 0.16 bit remainder of last step.
  unsigned int	channelstepremainder[NUM_CHANNELS];
  
  
  // The channel data pointers, start and end.
  unsigned char*		channels[NUM_CHANNELS];
  unsigned char*		channelsend[NUM_CHANNELS];
  
  
  // Time/gametic that the channel started playing,
  //	used to determine oldest, which automatically
  //	has lowest priority.
  // In case number of active sounds exceeds
  //	available channels.
  int		channelstart[NUM_CHANNELS];
  
  // The sound in channel handles,
  //	determined on registration,
  //	might be used to unregister/stop/modify,
  //	currently unused.
  int 		channelhandles[NUM_CHANNELS];
  
  // SFX id of the playing sound effect.
  // Used to catch duplicates (like chainsaw).
  int		channelids[NUM_CHANNELS];			
  
  // Pitch to stepping lookup, unused.
  int		steptable[256];
  
  // Volume lookups.
  int		vol_lookup[128*256];
  
  // Hardware left and right channel volume lookup.
  int*		channelleftvol_lookup[NUM_CHANNELS];
  int*		channelrightvol_lookup[NUM_CHANNELS];
  
  
  
  
  //
  // Safe ioctl, convenience.
  //
  void
  myioctl
  ( int	fd,
  	int	command,
  	int*	arg )
  {	 
  #if 0
  		int		rc;
  		extern int	errno;
  		
  		rc = ioctl(fd, command, arg);	
  		if (rc < 0)
  		{
  	fprintf(stderr, "ioctl(dsp,%d,arg) failed\n", command);
  	fprintf(stderr, "errno=%d\n", errno);
  	exit(-1);
  		}
  #endif
  }
  
  
  
  
  
  //
  // This function loads the sound data from the WAD lump,
  //	for single sound.
  //
  void*
  getsfx
  ( char*				 sfxname,
  	int*					len )
  {
  	unsigned char*		sfx;
  	unsigned char*		paddedsfx;
  	int					i;
  	int					size;
  	int					paddedsize;
  	char				name[20];
  	int					sfxlump;
  
  	
  	// Get the sound data from the WAD, allocate lump
  	//	in zone memory.
  //		sprintf(name, "ds%s", sfxname);
  	strcpy(name, sfxname);
  
  	// Now, there is a severe problem with the
  	//	sound handling, in it is not (yet/anymore)
  	//	gamemode aware. That means, sounds from
  	//	DOOM II will be requested even with DOOM
  	//	shareware.
  	// The sound list is wired into sounds.c,
  	//	which sets the external variable.
  	// I do not do runtime patches to that
  	//	variable. Instead, we will use a
  	//	default sound for replacement.
  	if ( W_CheckNumForName(name) == -1 )
  	{
  		sfxlump = W_GetNumForName("TICTOC");
  	}
  	else
  	{
  		sfxlump = W_GetNumForName(name);
  	}
  	
  	size = W_LumpLength( sfxlump );
  
  	// Debug.
  	// fprintf( stderr, "." );
  	//fprintf( stderr, " -loading	%s (lump %d, %d bytes)\n",
  	//			 sfxname, sfxlump, size );
  	//fflush( stderr );
  	
  	sfx = (unsigned char*)W_CacheLumpNum( sfxlump, PU_STATIC );
  
  	// Pads the sound effect out to the mixing buffer size.
  	// The original realloc would interfere with zone memory.
  	paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT;
  
  	// Allocate from zone memory.
  //	paddedsfx = (unsigned char*)Z_Malloc( paddedsize+8, PU_STATIC, 0 );
  	paddedsfx = (unsigned char*)Z_Malloc( paddedsize+16, PU_STATIC, 0 );
  	// ddt: (unsigned char *) realloc(sfx, paddedsize+8);
  	// This should interfere with zone memory handling,
  	//	which does not kick in in the soundserver.
  
  	if(paddedsize < size)
  		size = paddedsize;
  
  	// Now copy and pad.
  	memcpy(	paddedsfx, sfx, size );
  	for (i=size ; i<paddedsize+8 ; i++)
  		paddedsfx[i] = 128;
  
  	// Remove the cached lump.
  	Z_Free( sfx );
  	
  	Z_CheckIntact (paddedsfx);
  	
  	// Preserve padded length.
  	*len = paddedsize;
  
  	// Return allocated padded data.
  	return (void *) (paddedsfx + 8);
  }
  
  
  
  
  
  //
  // This function adds a sound to the
  //	list of currently active sounds,
  //	which is maintained as a given number
  //	(eight, usually) of internal channels.
  // Returns a handle.
  //
  int
  addsfx
  ( int		sfxid,
  	int		volume,
  	int		step,
  	int		seperation )
  {
  	static unsigned short	handlenums = 0;
  
  	int		i;
  	int		rc = -1;
  	
  	int		oldest = gametic;
  	int		oldestnum = 0;
  	int		slot;
  
  	int		rightvol;
  	int		leftvol;
  
  	// Loop all channels to find oldest SFX.
  	for (i=0; (i<NUM_CHANNELS) && (channels[i]); i++)
  	{
  		if (channelstart[i] < oldest)
  		{
  				oldestnum = i;
  				oldest = channelstart[i];
  		}
  	}
  
  	// Tales from the cryptic.
  	// If we found a channel, fine.
  	// If not, we simply overwrite the first one, 0.
  	// Probably only happens at startup.
  	if (i == NUM_CHANNELS)
  		slot = oldestnum;
  	else
  		slot = i;
  
  	// Okay, in the less recent channel,
  	//	we will handle the new SFX.
  	// Set pointer to raw data.
  	channels[slot] = (unsigned char *) S_sfx[sfxid].snd_ptr;
  	// Set pointer to end of raw data.
  	channelsend[slot] = channels[slot] + lengths[sfxid];
  
  	// Reset current handle number, limited to 0..100.
  	if (!handlenums)
  		handlenums = 100;
  
  	// Assign current handle number.
  	// Preserved so sounds could be stopped (unused).
  	channelhandles[slot] = rc = handlenums++;
  
  	// Set stepping???
  	// Kinda getting the impression this is never used.
  	channelstep[slot] = step;
  	// ???
  	channelstepremainder[slot] = 0;
  	// Should be gametic, I presume.
  	channelstart[slot] = gametic;
  
  	// Separation, that is, orientation/stereo.
  	//	range is: 1 - 256
  	seperation += 1;
  
  	// Per left/right channel.
  	//	x^2 seperation,
  	//	adjust volume properly.
  	leftvol =
  		volume - ((volume*seperation*seperation) >> 16); ///(256*256);
  	seperation = seperation - 257;
  	rightvol =
  		volume - ((volume*seperation*seperation) >> 16);	
  
  	// Sanity check, clamp volume.
  	if (rightvol < 0 || rightvol > 127)
  		I_Error("rightvol out of bounds");
  	
  	if (leftvol < 0 || leftvol > 127)
  		I_Error("leftvol out of bounds");
  	
  	// Get the proper lookup table piece
  	//	for this volume level???
  	channelleftvol_lookup[slot] = &vol_lookup[leftvol*256];
  	channelrightvol_lookup[slot] = &vol_lookup[rightvol*256];
  
  	// Preserve sound SFX id,
  	//	e.g. for avoiding duplicates of chainsaw.
  	channelids[slot] = sfxid;
  
  	// You tell me.
  	return rc;
  }
  
  
  
  
  
  //
  // SFX API
  // Note: this was called by S_Init.
  // However, whatever they did in the
  // old DPMS based DOS version, this
  // were simply dummies in the Linux
  // version.
  // See soundserver initdata().
  //
  void I_SetChannels()
  {
  	// Init internal lookups (raw data, mixing buffer, channels).
  	// This function sets up internal lookups used during
  	//	the mixing process. 
  	int		i;
  	int		j;
  		
  	int*	steptablemid = steptable + 128;
  	
  	// Okay, reset internal mixing channels to zero.
  	/*for (i=0; i<NUM_CHANNELS; i++)
  	{
  		channels[i] = 0;
  	}*/
  
  	// This table provides step widths for pitch parameters.
  	// I fail to see that this is currently used.
  	for (i=-128 ; i<128 ; i++)
  		steptablemid[i] = (int)(pow(2.0, (i/64.0))*65536.0);
  	
  	
  	// Generates volume lookup tables
  	//	which also turn the unsigned samples
  	//	into signed samples.
  	for (i=0 ; i<128 ; i++)
  		for (j=0 ; j<256 ; j++)
  			vol_lookup[i*256+j] = (i*(j-128)*256)/127;
  }	
  
  int snd_SfxVolume;
  
   
  void I_SetSfxVolume(int volume)
  {
  	// Identical to DOS.
  	// Basically, this should propagate
  	//	the menu/config file setting
  	//	to the state variable used in
  	//	the mixing.
  	snd_SfxVolume = volume;
  }
  
  // MUSIC API - dummy. Some code from DOS version.
  void I_SetMusicVolume(int volume)
  {
  	// Internal state variable.
  	snd_MusicVolume = volume;
  	// Now set volume on output device.
  	// Whatever( snd_MusciVolume );
  }
  
  
  //
  // Retrieve the raw data lump index
  //	for a given SFX name.
  //
  int I_GetSfxLumpNum(sfxinfo_t* sfx)
  {
  		char namebuf[9];
  //		sprintf(namebuf, "ds%s", sfx->name);
  		memcpy(namebuf, sfx->lumpname, 8);
  		namebuf[8]=0;
  //		return W_GetNumForName(namebuf);
  		return W_CheckNumForName(namebuf);
  }
  
  //
  // Starting a sound means adding it
  //	to the current list of active sounds
  //	in the internal channels.
  // As the SFX info struct contains
  //	e.g. a pointer to the raw data,
  //	it is ignored.
  // As our sound handling does not handle
  //	priority, it is ignored.
  // Pitching (that is, increased speed of playback)
  //	is set, but currently not used by mixing.
  //
  int
  I_StartSound
  ( int		id,
  	int		vol,
  	int		sep,
  	int		pitch,
  	int		priority )
  {
  
  	// UNUSED
  	priority = 0;
  	
  #ifdef SNDSERV 
  		if (sndserver)
  		{
  	fprintf(sndserver, "p%2.2x%2.2x%2.2x%2.2x\n", id, pitch, vol, sep);
  	fflush(sndserver);
  		}
  		// warning: control reaches end of non-void function.
  		return id;
  #else
  		// Debug.
  		//fprintf( stderr, "starting sound %d", id );
  		
  		// Returns a handle (not used).
  		id = addsfx( id, vol, steptable[pitch], sep );
  
  		// fprintf( stderr, "/handle is %d\n", id );
  		
  		return id;
  #endif
  }
  
  
  
  void I_StopSound (int handle)
  {
  	// You need the handle returned by StartSound.
  	// Would be looping all channels,
  	//	tracking down the handle,
  	//	an setting the channel to zero.
  	
  	// UNUSED.
  	handle = 0;
  }
  
  
  int I_SoundIsPlaying(int handle)
  {
  		// Ouch.
  		return gametic < handle;
  }
  
  
  
  
  //
  // This function loops all active (internal) sound
  //	channels, retrieves a given number of samples
  //	from the raw sound data, modifies it according
  //	to the current (internal) channel parameters,
  //	mixes the per channel samples into the global
  //	mixbuffer, clamping it to the allowed range,
  //	and sets up everything for transferring the
  //	contents of the mixbuffer to the (two)
  //	hardware channels (left and right, that is).
  //
  // This function currently supports only 16bit.
  //
  void I_UpdateSound( void )
  {
  #ifdef SNDINTR
  	// Debug. Count buffer misses with interrupt.
  	static int misses = 0;
  #endif
  
  	// Mix current sound data.
  	// Data, from raw sound, for right and left.
  	register unsigned int	sample;
  	register int		dl;
  	register int		dr;
  	
  	// Pointers in global mixbuffer, left, right, end.
  	signed short*		leftout;
  	signed short*		rightout;
  	signed short*		leftend;
  	// Step in mixbuffer, left and right, thus two.
  	int				step;
  
  	// Mixing channel index.
  	int				chan;
  	
  	unsigned int chstp;
  	byte 	**chanptr;
  	unsigned int *chanstp;
  	byte	*chancur;
  	int		**clvl;
  	int		**crvl;
  	
  #if 1
  	chanptr = channels;
  	clvl	= channelleftvol_lookup;
  	crvl	= channelrightvol_lookup;
  	chanstp	= channelstepremainder;
  #endif
  		
  	// Left and right channel
  	//	are in global mixbuffer, alternating.
  	leftout = mixbuffer;
  	rightout = mixbuffer+1;
  	step = 2;
  
  	// Determine end, for left channel only
  	//	(right channel is implicit).
  	leftend = mixbuffer + SAMPLECOUNT*step;
  
  	// Mix sounds into the mixing buffer.
  	// Loop over step*SAMPLECOUNT,
  	//	that is 512 values for two channels.
  	while (leftout != leftend)
  	{
  		// Reset left/right value. 
  		dl = 0;
  		dr = 0;
  
  		// Love thy L2 chache - made this a loop.
  		// Now more channels could be set at compile time
  		//	as well. Thus loop those	channels.
  #if 0
  		for ( chan = 0; chan < NUM_CHANNELS; chan++ )
  		{
  			// Check channel, if active.
  			if (channels[ chan ])
  			{
  				// Get the raw data from the channel. 
  				sample = *channels[ chan ];
  				// Add left and right part
  				//	for this channel (sound)
  				//	to the current data.
  				// Adjust volume accordingly.
  				dl += channelleftvol_lookup[ chan ][sample];
  				dr += channelrightvol_lookup[ chan ][sample];
  				// Increment index ???
  				channelstepremainder[ chan ] += channelstep[ chan ];
  				// MSB is next sample???
  				channels[ chan ] += channelstepremainder[ chan ] >> 16;
  				// Limit to LSB???
  				channelstepremainder[ chan ] &= 65536-1;
  
  				// Check whether we are done.
  				if (channels[ chan ] >= channelsend[ chan ])
  					channels[ chan ] = 0;
  			}
  		}
  #endif
  
  #if 1
  		for ( chan = 0; chan < NUM_CHANNELS; chan++ )
  		{
  			chancur = chanptr[ chan ];
  			if (!chancur)
  				continue;
  			chstp = chanstp[ chan ];
  
  			sample = *chancur;
  			dl += clvl[ chan ][sample];
  			dr += crvl[ chan ][sample];
  			chstp += channelstep[ chan ];
  			chancur += chstp >> 16;
  			chstp &= 65535;
  			if (chancur >= channelsend[ chan ])
  				chancur = NULL;
  
  			chanptr[ chan ] = chancur;
  			chanstp[ chan ] = chstp;
  		}
  #endif
  	
  		// Clamp to range. Left hardware channel.
  		// Has been char instead of short.
  		// if (dl > 127) *leftout = 127;
  		// else if (dl < -128) *leftout = -128;
  		// else *leftout = dl;
  #if 0
  		if (dl > 0x7fff)
  			*leftout = 0x7fff;
  		else if (dl < -0x8000)
  			*leftout = -0x8000;
  		else
  			*leftout = dl;
  
  		// Same for right hardware channel.
  		if (dr > 0x7fff)
  			*rightout = 0x7fff;
  		else if (dr < -0x8000)
  			*rightout = -0x8000;
  		else
  			*rightout = dr;
  #endif
  
  		if(dl<(-32767)) dl=-32767;
  		if(dl>( 32767)) dl= 32767;
  		if(dr<(-32767)) dr=-32767;
  		if(dr>( 32767)) dr= 32767;
  		*leftout = dl;
  		*rightout = dr;
  
  		// Increment current pointers in mixbuffer.
  		leftout += step;
  		rightout += step;
  	}
  
  #ifdef SNDINTR
  	// Debug check.
  	if ( flag )
  	{
  		misses += flag;
  		flag = 0;
  	}
  	
  	if ( misses > 10 )
  	{
  		fprintf( stderr, "I_SoundUpdate: missed 10 buffer writes\n");
  		misses = 0;
  	}
  	
  	// Increment flag for update.
  	flag++;
  #endif
  }
  
  
  // void I_MusicGetAdvanceSamples(short *buf, int sn, int snmax);
  void I_SubmitSound2(int extra);
  
  // 
  // This would be used to write out the mixbuffer
  //	during each game loop update.
  // Updates sound buffer and audio device at runtime. 
  // It is called during Timer interrupt with SNDINTR.
  // Mixing now done synchronous, and
  //	only output be done asynchronous?
  //
  void
  I_SubmitSound(void)
  {
  	I_SubmitSound2(0);
  }
  
  void
  I_SubmitSound2(int extra)
  {
  	static short mixbuf2[SAMPLECOUNT*2*2];
  	static short mixbuf_mus[SAMPLECOUNT*2*2];
  	static int curms, lastms;
  	
  	int i, j;
  	int n, ns, dt, musvol;
  	// Write it to DSP device.
  //	write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
  
  //	n=SAMPLECOUNT*1.451247;
  	n=(SAMPLECOUNT*1486)>>10;
  
  	curms=FRGL_TimeMS();
  	dt=curms-lastms;
  	lastms=curms;
  	ns=dt*16;
  	if(ns<0)ns=0;
  	if(ns>n)ns=n;
  
  // #ifndef __BGBCC
  //	I_MusicGetAdvanceSamples(mixbuf_mus, ns, SAMPLECOUNT*1.5);
  // #endif
  
  	musvol=snd_MusicVolume;
  
  //	n=SAMPLECOUNT*1.451247;
  	n=(SAMPLECOUNT*1486)>>10;
  	for(i=0; i<n; i++)
  	{
  //		j=i/1.451247;
  		j=(i*2822)>>12;
  		mixbuf2[i*2+0]=mixbuffer[j*2+0];
  		mixbuf2[i*2+1]=mixbuffer[j*2+1];
  
  // #ifndef __BGBCC
  #if 0
  //		mixbuf2[i*2+0]+=mixbuf_mus[i*2+0]/4;
  //		mixbuf2[i*2+1]+=mixbuf_mus[i*2+1]/4;
  //		mixbuf2[i*2+0]+=(mixbuf_mus[i*2+0]*musvol)/16;
  //		mixbuf2[i*2+1]+=(mixbuf_mus[i*2+1]*musvol)/16;
  		mixbuf2[i*2+0]+=(mixbuf_mus[i*2+0]*musvol)>>4;
  		mixbuf2[i*2+1]+=(mixbuf_mus[i*2+1]*musvol)>>4;
  #endif
  	}
  
  	if(ns>0)
  	{
  //		SoundDev_WriteStereoSamples(mixbuffer, SAMPLECOUNT);
  		if(extra)
  		{
  			SoundDev_WriteStereoSamples(mixbuf2, n);
  		}else
  		{
  			SoundDev_WriteStereoSamples(mixbuf2, ns);
  		}
  
  		SoundDev_Submit();
  	}
  }
  
  
  
  void
  I_UpdateSoundParams
  ( int	handle,
  	int	vol,
  	int	sep,
  	int	pitch)
  {
  	// I fail too see that this is used.
  	// Would be using the handle to identify
  	//	on which channel the sound might be active,
  	//	and resetting the channel parameters.
  
  	// UNUSED.
  	handle = vol = sep = pitch = 0;
  }
  
  
  
  
  void I_ShutdownSound(void)
  {	
  #if 0
  	
  #ifdef SNDSERV
  	if (sndserver)
  	{
  		// Send a "quit" command.
  		fprintf(sndserver, "q\n");
  		fflush(sndserver);
  	}
  #else
  	// Wait till all pending sounds are finished.
  	int done = 0;
  	int i;
  	
  
  	// FIXME (below).
  	fprintf( stderr, "I_ShutdownSound: NOT finishing pending sounds\n");
  	fflush( stderr );
  	
  	while ( !done )
  	{
  		for( i=0 ; i<8 && !channels[i] ; i++);
  		
  		// FIXME. No proper channel output.
  		//if (i==8)
  		done=1;
  	}
  #ifdef SNDINTR
  	I_SoundDelTimer();
  #endif
  	
  	// Cleaning up -releasing the DSP device.
  	close ( audio_fd );
  #endif
  
  #endif
  
  	// Done.
  	return;
  }
  
  
  
  static int i_initsound_init = 0;
  
  
  void
  I_InitSound()
  { 
  #if 1
  
  // #ifdef SNDSERV
  #if 0
  	char buffer[256];
  	
  	if (getenv("DOOMWADDIR"))
  		sprintf(buffer, "%s/%s",
  			getenv("DOOMWADDIR"),
  			sndserver_filename);
  	else
  		sprintf(buffer, "%s", sndserver_filename);
  	
  	// start sound process
  	if ( !access(buffer, X_OK) )
  	{
  		strcat(buffer, " -quiet");
  		sndserver = popen(buffer, "w");
  	}
  	else
  		fprintf(stderr, "Could not start sound server [%s]\n", buffer);
  #else
  	
  	char tb[256], tb1[256], tb2[256];
  	char *sndinfo;
  	char *cs, *ct, *cse;
  	int lump, size;
  	int i, j, k;
  	
  	if(i_initsound_init)
  		return;
  	i_initsound_init=1;
  	
  #ifdef SNDINTR
  //	fprintf( stderr, "I_SoundSetTimer: %d microsecs\n", SOUND_INTERVAL );
  //	I_SoundSetTimer( SOUND_INTERVAL );
  #endif
  		
  	// Secure and configure sound device first.
  	fprintf( stderr, "I_InitSound: ");
  	
  //	audio_fd = open("/dev/dsp", O_WRONLY);
  //	if (audio_fd<0)
  //		fprintf(stderr, "Could not open /dev/dsp\n");
  	
  										 
  //	i = 11 | (2<<16);																					 
  //	myioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &i);
  //	myioctl(audio_fd, SNDCTL_DSP_RESET, 0);
  	
  	i=SAMPLERATE;
  	
  	SoundDev_Init();
  	
  //	myioctl(audio_fd, SNDCTL_DSP_SPEED, &i);
  	
  //	i=1;
  //	myioctl(audio_fd, SNDCTL_DSP_STEREO, &i);
  	
  //	myioctl(audio_fd, SNDCTL_DSP_GETFMTS, &i);
  	
  //	if (i&=AFMT_S16_LE)		
  //		myioctl(audio_fd, SNDCTL_DSP_SETFMT, &i);
  //	else
  //		fprintf(stderr, "Could not play signed 16 data\n");
  
  	fprintf(stderr, " configured audio device\n" );
  
  	lump = W_GetNumForName("SNDINFO");
  	if(lump>=0)
  	{
  		size = W_LumpLength(lump);
  		sndinfo = W_CacheLumpNum(lump, PU_CACHE);
  
  		cs=sndinfo; cse=cs+size;
  		while(cs<cse)
  		{
  			ct=tb;
  			while(cs<cse)
  			{
  				if((*cs=='\r') || (*cs=='\n'))
  					break;
  				*ct++=*cs++;
  			}
  			*ct=0;
  			if(*cs=='\r')cs++;
  			if(*cs=='\n')cs++;
  			
  			if(tb[0]==';')
  				continue;
  			if(tb[0]=='$')
  				continue;
  				
  			tb1[0]=0;
  			tb2[0]=0;
  			sscanf(tb, "%s %s", tb1, tb2);
  			if(!tb1[0] || !tb2[0])
  				continue;
  			
  			for(i=0; i<NUMSFX; i++)
  			{
  				if(!strcmp(S_sfx[i].tagName, tb1))
  				{
  					printf("Snd %s=%s\n", tb1, tb2);
  					strcpy(S_sfx[i].lumpname, tb2);
  					break;
  				}
  			}
  			
  		}
  	}
  	
  #if 1
  	// Initialize external data (all sounds) at start, keep static.
  	fprintf( stderr, "I_InitSound: ");
  	
  	for (i=1 ; i<NUMSFX ; i++)
  	{
  		if(!(S_sfx[i].lumpname[0]))
  			continue;
  	
  		S_sfx[i].snd_ptr = getsfx( S_sfx[i].lumpname, &lengths[i] );
  
  #if 0
  		// Alias? Example is the chaingun sound linked to pistol.
  		if (!S_sfx[i].link)
  		{
  			// Load data from WAD file.
  			S_sfx[i].data = getsfx( S_sfx[i].name, &lengths[i] );
  		}	
  		else
  		{
  			// Previously loaded already?
  			S_sfx[i].data = S_sfx[i].link->data;
  			lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)];
  		}
  #endif
  	}
  
  	fprintf( stderr, " pre-cached all sound data\n");
  #endif
  
  	// Now initialize mixbuffer with zero.
  	for ( i = 0; i< MIXBUFFERSIZE; i++ )
  		mixbuffer[i] = 0;
  	
  	// Finished initialization.
  	fprintf(stderr, "I_InitSound: sound module ready\n");
  		
  #endif
  
  #endif
  }
  
  // void S_Init()
  // { 
  //	I_InitSound();
  // }
  
  #if 0
  byte *i_mus_songs[32];
  int i_mus_song_mask;
  
  //
  // MUSIC API.
  // Still no music done.
  // Remains. Dummies.
  //
  
  void I_InitMusic(void)	
  {
  }
  
  void I_ShutdownMusic(void)
  {
  }
  
  static int	looping=0;
  static int	musicdies=-1;
  
  void I_PlaySong(int handle, int looping)
  {
  	// UNUSED.
  //	handle = looping = 0;
  //	musicdies = gametic + TICRATE*30;
  }
  
  void I_PauseSong (int handle)
  {
  	// UNUSED.
  	handle = 0;
  }
  
  void I_ResumeSong (int handle)
  {
  	// UNUSED.
  	handle = 0;
  }
  
  void I_StopSong(int handle)
  {
  	// UNUSED.
  	handle = 0;
  	
  	looping = 0;
  	musicdies = 0;
  }
  
  void I_UnRegisterSong(int handle)
  {
  	i_mus_song_mask&=~(1<<handle);
  }
  
  int I_RegisterSong(void* data)
  {
  	int i;
  	
  	for(i=1; i<32; i++)
  		if(!(i_mus_song_mask&(1<<i)))
  			break;
  	
  	i_mus_songs[i]=data;
  	i_mus_song_mask|=(1<<i);
  	return(i);
  
  //	data = NULL;
  //	return 1;
  }
  
  // Is the song playing?
  int I_QrySongPlaying(int handle)
  {
  	// UNUSED.
  	handle = 0;
  	return looping || musicdies > gametic;
  }
  #endif
  
  
  //
  // Experimental stuff.
  // A Linux timer interrupt, for asynchronous
  //	sound output.
  // I ripped this out of the Timer class in
  //	our Difference Engine, including a few
  //	SUN remains...
  //	
  #ifdef sun
  		typedef		 sigset_t				tSigSet;
  #else		
  		typedef		 int						 tSigSet;
  #endif
  
  
  // We might use SIGVTALRM and ITIMER_VIRTUAL, if the process
  //	time independend timer happens to get lost due to heavy load.
  // SIGALRM and ITIMER_REAL doesn't really work well.
  // There are issues with profiling as well.
  // static int /*__itimer_which*/	itimer = ITIMER_REAL;
  
  // static int sig = SIGALRM;
  
  // Interrupt handler.
  void I_HandleSoundTimer( int ignore )
  {
  #if 0
  	// Debug.
  	//fprintf( stderr, "%c", '+' ); fflush( stderr );
  	
  	// Feed sound device if necesary.
  	if ( flag )
  	{
  		// See I_SubmitSound().
  		// Write it to DSP device.
  		write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
  
  		// Reset flag counter.
  		flag = 0;
  	}
  	else
  		return;
  	
  	// UNUSED, but required.
  	ignore = 0;
  #endif
  
  	return;
  }
  
  // Get the interrupt. Set duration in millisecs.
  int I_SoundSetTimer( int duration_of_tick )
  {
  #if 0
  	// Needed for gametick clockwork.
  	struct itimerval		value;
  	struct itimerval		ovalue;
  	struct sigaction		act;
  	struct sigaction		oact;
  
  	int res;
  	
  	// This sets to SA_ONESHOT and SA_NOMASK, thus we can not use it.
  	//		 signal( _sig, handle_SIG_TICK );
  	
  	// Now we have to change this attribute for repeated calls.
  	act.sa_handler = I_HandleSoundTimer;
  #ifndef sun		
  	//ac	t.sa_mask = _sig;
  #endif
  	act.sa_flags = SA_RESTART;
  	
  	sigaction( sig, &act, &oact );
  
  	value.it_interval.tv_sec		= 0;
  	value.it_interval.tv_usec	 = duration_of_tick;
  	value.it_value.tv_sec			 = 0;
  	value.it_value.tv_usec			= duration_of_tick;
  
  	// Error is -1.
  	res = setitimer( itimer, &value, &ovalue );
  
  	// Debug.
  	if ( res == -1 )
  		fprintf( stderr, "I_SoundSetTimer: interrupt n.a.\n");
  	
  	return res;
  #endif
  
  	return(-1);
  
  }
  
  
  // Remove the interrupt. Set duration to zero.
  void I_SoundDelTimer()
  {
  	// Debug.
  	if ( I_SoundSetTimer( 0 ) == -1)
  		fprintf( stderr, "I_SoundDelTimer: failed to remove interrupt. Doh!\n");
  }
  
  
  
  void I_PauseSong(int handle)
  {
  //  MUS_PauseSong(handle);
  }
  
  void I_ResumeSong(int handle)
  {
  //  MUS_ResumeSong(handle);
  }
  