?? i_sound.c
字號:
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id:$
//
// Copyright (C) 1993-1996 by id Software, Inc.
//
// This source is available for distribution and/or modification
// only under the terms of the DOOM Source Code License as
// published by id Software. All rights reserved.
//
// The source is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code 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 <time.h>
#include <sys/types.h>
//#ifndef LINUX
//#include <sys/filio.h>
//#endif
#include <fcntl.h>
#include <io.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 "doomdef.h"
////////////////////////////////////////////////////////////////////////////
// WinDoom - DirectSound
////////////////////////////////////////////////////////////////////////////
#include <dsound.h>
char MsgText[256];
void WriteDebug(char *);
#define NUM_SOUND_FX 128
#define SB_SIZE 20480
void CreateSoundBuffer(int Channel, int length, unsigned char *data);
void I_PlaySoundEffect(int sfxid, int Channel, int volume, int pan);
void DS_Error( HRESULT hresult, char *msg );
#define NUM_DSBUFFERS 256
typedef enum { dsb_perm, dsb_temp } dsb_type;
extern LPDIRECTSOUND lpDS;
extern LPDIRECTSOUNDBUFFER lpDSBuffer[NUM_DSBUFFERS];
typedef struct
{
void *origin;
int dsb_type;
int sfxid;
}DSBControl_t;
DSBControl_t DSBControl[NUM_DSBUFFERS];
extern int swap_stereo;
////////////////////////////////////////////////////////////////////////////
// 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 16
// 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.
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_DSBUFFERS];
// 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 )
{
// FIXME
/*
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,
// 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;
sprintf(name, "ds%s", sfxname);
// Get the sound data from the WAD, allocate lump
// in zone memory.
// 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 );
// sprintf(MsgText, "Getting sound effect : %s - %d\n", name, size);
// WriteDebug(MsgText);
// 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;
// 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;
int iVolume, iPan;
// 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)
{
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;
iVolume = 0-(128*(15-volume));
if (iVolume < -10000)
iVolume == -10000;
iPan = (seperation-128)*20;
if (iPan < -10000)
iPan = -10000;
if (iPan > 10000)
iPan = 10000;
if (swap_stereo == TRUE)
iPan *= -1;
// 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;
I_PlaySoundEffect(sfxid, iVolume, iPan);
// You tell me.
//return rc;
return sfxid;
}
*/
//
// 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, void *origin )
{
static unsigned short handlenums = 0;
int i;
int rc = -1;
int oldest = gametic;
int oldestnum = 0;
int slot;
int rightvol;
int leftvol;
int iVolume, iPan;
DWORD dwDSBStatus;
int dsbchannel;
// 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 )
dsbchannel = sfxid;
else
{
dsbchannel = sfxid;
if (lpDSBuffer[0] != 0)
lpDSBuffer[sfxid]->lpVtbl->GetStatus(lpDSBuffer[sfxid], &dwDSBStatus);
else
dwDSBStatus = DS_OK;
if (dwDSBStatus == DSBSTATUS_PLAYING)
{
for (i = NUMSFX; i < NUM_DSBUFFERS; i++)
{
if ((DSBControl[i].origin == origin) && (DSBControl[i].sfxid == sfxid))
{
dsbchannel = i;
break;
}
if (DSBControl[i].sfxid == -1)
{
dsbchannel = i;
DSBControl[i].origin = origin;
break;
}
if (DSBControl[i].sfxid >= 0)
{
lpDSBuffer[i]->lpVtbl->GetStatus(lpDSBuffer[i], &dwDSBStatus);
if (dwDSBStatus != DSBSTATUS_PLAYING)
{
dsbchannel = i;
DSBControl[i].origin = origin;
break;
}
}
if (DSBControl[i].origin == origin)
{
dsbchannel = i;
break;
}
}
}
}
iVolume = 0-(128*(15-volume));
if (iVolume < -10000)
iVolume == -10000;
iPan = (seperation-128)*20;
if (iPan < -10000)
iPan = -10000;
if (iPan > 10000)
iPan = 10000;
if (swap_stereo == TRUE)
iPan *= -1;
// Preserve sound SFX id,
// e.g. for avoiding duplicates of chainsaw.
channelids[dsbchannel] = sfxid;
I_PlaySoundEffect(sfxid, dsbchannel, iVolume, iPan);
// You tell me.
//return rc;
return dsbchannel;
}
// This function call starts a sound playing in a DirectSound buffer...
void I_PlaySoundEffect(int sfxid, int Channel, int iVolume, int iPan)
{
HRESULT hresult;
DWORD dwDSBStatus;
if (Channel > NUM_SOUND_FX)
{
//WriteDebug("Invalid sound channel...\n");
return;
}
if (lpDSBuffer[Channel] == 0)
{
//WriteDebug("Trying to play sound without DirectSound working...\n");
return;
}
if ((DSBControl[Channel].dsb_type == dsb_temp) && (DSBControl[Channel].sfxid != sfxid))
{
if (DSBControl[Channel].sfxid > 0)
{
/*
lpDSBuffer[Channel]->lpVtbl->GetStatus(lpDSBuffer[Channel], &dwDSBStatus);
if (dwDSBStatus == DSBSTATUS_PLAYING)
{
hresult = lpDSBuffer[Channel]->lpVtbl->Stop(lpDSBuffer[Channel]);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.Stop");
}
*/
lpDSBuffer[Channel]->lpVtbl->Release(lpDSBuffer[Channel]);
DSBControl[Channel].sfxid = -1;
}
if (DSBControl[Channel].sfxid < 0)
{
// clone temp buffer here...
lpDS->lpVtbl->DuplicateSoundBuffer(lpDS, lpDSBuffer[sfxid], &lpDSBuffer[Channel]);
DSBControl[Channel].sfxid = sfxid;
}
}
/*
else
{
lpDSBuffer[Channel]->lpVtbl->GetStatus(lpDSBuffer[Channel], &dwDSBStatus);
if (dwDSBStatus == DSBSTATUS_PLAYING)
{
hresult = lpDSBuffer[Channel]->lpVtbl->Stop(lpDSBuffer[Channel]);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.Stop");
}
}
*/
hresult = lpDSBuffer[Channel]->lpVtbl->SetCurrentPosition(lpDSBuffer[Channel], 0);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.SetCurrentPosition");
hresult = lpDSBuffer[Channel]->lpVtbl->SetVolume(lpDSBuffer[Channel], iVolume );
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.SetVolume");
hresult = lpDSBuffer[Channel]->lpVtbl->SetPan(lpDSBuffer[Channel], iPan);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.SetPan");
hresult = lpDSBuffer[Channel]->lpVtbl->Play(lpDSBuffer[Channel], 0, 0, 0);
if (hresult != DS_OK)
DS_Error(hresult, "lpDSBuffer.Play");
}
//
// SFX API
// Note: this was called by S_Init.
// However, whatever they did in the
// old DPMS based DOS version, this
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -