Iskhodnyy_kod_Doom
.pdf#include "i_system.h" #include "i_sound.h" #include "m_argv.h"
#include "m_misc.h" #include "w_wad.h"
#include "doomdef.h"
//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
//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 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. |
181
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 ) |
|
{ |
|
|
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);
}
}
//
// This function loads the sound data from the WAD lump,
182
// 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);
//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("dspistol");
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 );
//ddt: (unsigned char *) realloc(sfx, paddedsize+8);
//This should interfere with zone memory handling,
//which does not kick in in the soundserver.
//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 );
//Preserve padded length. *len = paddedsize;
183
// 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
int int int
{
static unsigned short |
handlenums = 0; |
|
int |
i; |
|
int |
rc = -1; |
|
int |
oldest = gametic; |
|
int |
oldestnum = 0; |
|
int |
slot; |
|
int |
rightvol; |
|
int |
leftvol; |
|
//Chainsaw troubles.
//Play these sound effects only one at a time. if ( sfxid == sfx_sawup
||sfxid == sfx_sawidl
||sfxid == sfx_sawful
||sfxid == sfx_sawhit
||sfxid == sfx_stnmov
|| |
sfxid == sfx_pistol |
) |
{ |
|
|
// Loop all channels, check. |
|
|
for |
(i=0 ; i<NUM_CHANNELS ; i++) |
|
{
// Active, and using the same SFX? if ( (channels[i])
&& (channelids[i] == sfxid) )
{
// Reset. channels[i] = 0;
// We are sure that iff,
// there will only be one. break;
}
}
}
// Loop all channels to find oldest SFX.
for (i=0; (i<NUM_CHANNELS) && (channels[i]); i++)
{
if (channelstart[i] < oldest)
{
184
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].data; // 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;
185
// 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;
}
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.
186
// 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); return W_GetNumForName(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
int int int int
{
// 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.
187
//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; |
//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;
188
//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.
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;
}
}
//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 (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;
//Increment current pointers in mixbuffer. leftout += step;
rightout += step;
}
#ifdef SNDINTR
189
// 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
}
//
//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)
{
// Write it to DSP device.
write(audio_fd, mixbuffer, SAMPLECOUNT*BUFMUL);
}
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)
{
#ifdef SNDSERV if (sndserver)
{
// Send a "quit" command. fprintf(sndserver, "q\n"); fflush(sndserver);
}
#else
190