📄 i_sound.c
字号:
// 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.
// 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 id, int vol, int sep, int pitch, int priority, void *origin )
{
// UNUSED
priority = 0;
// Debug.
// sprintf(MsgText, "starting sound %d", id );
// WriteDebug(MsgText);
// Returns a handle (not used).
id = addsfx( id, vol, steptable[pitch], sep, origin );
// sprintf(MsgText, "/handle is %d\n", id );
// WriteDebug(MsgText);
return id;
}
void I_StopSound (int handle)
{
HRESULT hresult;
// 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;
if (handle > NUM_DSBUFFERS)
{
WriteDebug("Invalid sound channel...\n");
return;
}
if (lpDSBuffer[handle] == 0)
{
WriteDebug("Trying to stop sound without DirectSound working...\n");
return;
}
hresult = lpDSBuffer[handle]->lpVtbl->Stop(lpDSBuffer[handle]);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.Stop");
/*
else
if (DSBControl[handle].dsb_type == dsb_temp)
{
lpDSBuffer[handle]->lpVtbl->Release(lpDSBuffer[handle]);
DSBControl[handle].origin = NULL;
DSBControl[handle].sfxid = -1;
}
*/
}
int I_SoundIsPlaying(int handle)
{
DWORD dwStatus;
HRESULT hresult;
if (lpDSBuffer[handle] == 0)
{
//WriteDebug("Trying to get status of a sound without DirectSound working...\n");
return FALSE;
}
hresult = lpDSBuffer[handle]->lpVtbl->GetStatus(lpDSBuffer[handle], &dwStatus);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.GetStatus");
return (dwStatus == DSBSTATUS_PLAYING);
// 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;
// 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
// 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.
int iVolume, iPan;
HRESULT hresult;
if (lpDSBuffer[handle] == 0)
return;
iVolume = 0-(128*(15-vol));
if (iVolume < -10000)
iVolume == -10000;
iPan = (sep-128)*20;
if (iPan < -10000)
iPan = -10000;
if (iPan > 10000)
iPan = 10000;
if (swap_stereo == TRUE)
iPan *= -1;
hresult = lpDSBuffer[handle]->lpVtbl->SetVolume(lpDSBuffer[handle], iVolume );
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.SetVolume");
hresult = lpDSBuffer[handle]->lpVtbl->SetPan(lpDSBuffer[handle], iPan);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.SetPan");
// 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
// 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
// Done.
return;
}
void
I_InitSound()
{
int i;
for (i = 1; i < NUMSFX; i++)
{
// 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)];
}
CreateSoundBuffer(i, lengths[i], S_sfx[i].data);
DSBControl[i].origin = NULL;
DSBControl[i].sfxid = i;
DSBControl[i].dsb_type = dsb_perm;
}
for (; i < NUM_DSBUFFERS; i++)
{
DSBControl[i].origin = NULL;
DSBControl[i].sfxid = -1;
DSBControl[i].dsb_type = dsb_temp;
}
}
//
// 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)
{
// UNUSED.
handle = 0;
}
int I_RegisterSong(void* data)
{
// UNUSED.
data = NULL;
return 1;
}
// Is the song playing?
int I_QrySongPlaying(int handle)
{
// UNUSED.
handle = 0;
return looping || musicdies > gametic;
}
//
// 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.
// FIXME
#define ITIMER_REAL 0
static int /*__itimer_which*/ itimer = ITIMER_REAL;
// FIXME
#define SIGALRM 0
static int sig = SIGALRM;
// Interrupt handler.
void I_HandleSoundTimer( int ignore )
{
// 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;
return;
}
// Get the interrupt. Set duration in millisecs.
int I_SoundSetTimer( int duration_of_tick )
{
/*
// 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;
*/
return 0;
}
// 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");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -