📄 i_sound.c
字号:
// Emacs style mode select -*- C++ -*- //-----------------------------------------------------------------------------//// $Id: i_sound.c,v 1.6 2000/08/11 19:11:07 metzgermeister Exp $//// Copyright (C) 1993-1996 by id Software, Inc.// Portions Copyright (C) 1998-2000 by DooM Legacy Team.//// 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: i_sound.c,v $// Revision 1.6 2000/08/11 19:11:07 metzgermeister// *** empty log message ***//// Revision 1.5 2000/04/30 19:47:38 metzgermeister// iwad support//// Revision 1.4 2000/03/28 16:18:42 linuxcub// Added a command to the Linux sound-server which sets a master volume.// Someone needs to check that this isn't too much of a performance drop// on slow machines. (Works for me).//// Added code to the main parts of doomlegacy which uses this command to// implement volume control for sound effects.//// Added code so the (really cool) cd music works for me. The volume didn't// work for me (with a Teac 532E drive): It always started at max (31) no-// matter what the setting in the config-file was. The added code "jiggles"// the volume-control, and now it works for me :-)// If this code is unacceptable, perhaps another solution is to periodically// compare the cd_volume.value with an actual value _read_ from the drive.// Ie. not trusting that calling the ioctl with the correct value actually// sets the hardware-volume to the requested value. Right now, the ioctl// is assumed to work perfectly, and the value in cd_volume.value is// compared periodically with cdvolume.//// Updated the spec file, so an updated RPM can easily be built, with// a minimum of editing. Where can I upload my pre-built (S)RPMS to ?//// Erling Jacobsen, linuxcub@email.dk//// Revision 1.3 2000/03/12 23:21:10 linuxcub// Added consvars which hold the filenames and arguments which will be used// when running the soundserver and musicserver (under Linux). I hope I// didn't break anything ... Erling Jacobsen, linuxcub@email.dk//// Revision 1.2 2000/02/27 00:42:11 hurdler// fix CR+LF problem//// Revision 1.1.1.1 2000/02/22 20:32:33 hurdler// Initial import into CVS (v1.29 pr3)////// DESCRIPTION:// System interface for sound.////-----------------------------------------------------------------------------#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <math.h>#include <sys/time.h>#include <sys/types.h>#if !defined(LINUX) && !defined(SCOOS5) && !defined(_AIX)#include <sys/filio.h>#endif#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/msg.h>// Linux voxware output.#ifdef LINUX#include <linux/soundcard.h>#endif// SCO OS5 and Unixware OSS sound output#if defined(SCOOS5) || defined(SCOUW2) || defined(SCOUW7)#include <sys/soundcard.h>#endif// Timer stuff. Experimental.#include <time.h>#include <signal.h>// for IPC between xdoom and musserver#include <sys/ipc.h>#include "z_zone.h"#include "i_system.h"#include "i_sound.h"//#include "s_sound.h"int S_GetSfxLumpNum (sfxinfo_t* sfx); #include "m_argv.h"#include "m_misc.h"#include "w_wad.h"#include "doomdef.h"// added for 1.27 19990203 by Kin#include "doomstat.h"#include "searchp.h"//#include "cd_audio.h"// Number of sound channels used as set in the config fileextern consvar_t cv_numChannels;#ifdef SNDSERVextern consvar_t sndserver_cmd;extern consvar_t sndserver_arg;#endif#ifdef MUSSERVextern consvar_t musserver_cmd;extern consvar_t musserver_arg;#endif// UNIX hack, to be removed.#ifdef SNDSERVFILE* sndserver=0;int sndcnt = 0; // sfx serial no. 19990201 by Kin#elif SNDINTR// Update all 30 millisecs, approx. 30fps synchronized.// Linux resolution is allegedly 10 millisecs,// scale is microseconds.#define SOUND_INTERVAL 10000// Get the interrupt. Set duration in millisecs.int I_SoundSetTimer( int duration_of_tick );void I_SoundDelTimer( void );#else// None?#endif// UNIX hack too, unlikely to be removed.#ifdef MUSSERVFILE* musserver=0;int msg_id = -1;#endif// Flags for the -nosound and -nomusic optionsextern boolean nosound;extern boolean nomusic;// Flag to signal CD audio support to not play a title//int playing_title;// A quick hack to establish a protocol between// synchronous mix buffer updates and asynchronous// audio writes. Probably redundant with gametic.volatile 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 1024#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 and a flag for using 8bit samples.int audio_fd;int audio_8bit_flag;// 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,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];// master hardware sound volumeint hw_sndvolume=31;//// Safe ioctl, convenience.//voidmyioctl( 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); }}// dummy for now 19990220 by Kinvoid I_FreeSfx(sfxinfo_t* sfx) {}//// This function loads the sound data from the WAD lump,// for single sound.//void* I_GetSfx (sfxinfo_t* sfx){ byte* dssfx; int size,i; unsigned char* paddedsfx; int paddedsize; if (sfx->lumpnum<0) { sfx->lumpnum = S_GetSfxLumpNum (sfx); } size = W_LumpLength (sfx->lumpnum); dssfx = (byte*) W_CacheLumpNum (sfx->lumpnum, PU_STATIC); paddedsize = ((size-8 + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT; paddedsfx = (unsigned char*)Z_Malloc( paddedsize+8, PU_STATIC, 0 ); memcpy( paddedsfx, dssfx, size ); for (i=size ; i<paddedsize+8 ; i++) paddedsfx[i] = 128; // Remove the cached lump. Z_Free( dssfx ); *((int*)paddedsfx) = paddedsize; // convert raw data and header from Doom sfx to a SAMPLE for Allegro // write data to llsndserv 19990201 by Kin#ifdef SNDSERV if(sndserver) { fputc('v',sndserver); fputc((char)hw_sndvolume,sndserver); fputc('l',sndserver); fwrite(paddedsfx, sizeof(int), 1,sndserver); fwrite(paddedsfx+8, 1, paddedsize, sndserver); fflush(sndserver); } Z_Free(paddedsfx); paddedsfx = (unsigned char*)Z_Malloc(sizeof(int),PU_STATIC,0); *((int*)paddedsfx) = sndcnt; sndcnt++;#endif return (void*)(paddedsfx);}//// 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.//intaddsfx( 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; // 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<cv_numChannels.value ; 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<cv_numChannels.value) && (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 == cv_numChannels.value) 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+8; // Set pointer to end of raw data. channelsend[slot] = channels[slot] + *((int*)S_sfx[sfxid].data); // 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*hw_sndvolume/31)*256]; channelrightvol_lookup[slot] = &vol_lookup[(rightvol*hw_sndvolume/31)*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<cv_numChannels.value; 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++) { if (!audio_8bit_flag) vol_lookup[i*256+j] = (i*(j-128)*256)/127; else vol_lookup[i*256+j] = (i*(j-128)*256)/127*4; } }} 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. // dummy for Linux 19990117 by Kin// snd_SfxVolume = volume; hw_sndvolume = volume;#ifdef SNDSERV if(sndserver) { fputc('v',sndserver); fputc((char)hw_sndvolume,sndserver); fflush(sndserver); }#endif}// MUSIC APIvoid I_SetMusicVolume(int volume){ // Internal state variable.// snd_MusicVolume = volume; // Now set volume on output device. // Whatever( snd_MusciVolume ); if (nomusic) return;#ifdef MUSSERV if (msg_id != -1) { struct { long msg_type; char msg_text[12]; } msg_buffer; msg_buffer.msg_type = 6; memset(msg_buffer.msg_text, 0, 12); msg_buffer.msg_text[0] = 'v'; msg_buffer.msg_text[1] = volume; msgsnd(msg_id, (struct msgbuf *) &msg_buffer, 12, IPC_NOWAIT); }#endif}//// 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.//intI_StartSound( int id, int vol, int sep, int pitch, int priority ){ // UNUSED priority = 0; vol = vol >> 4; // xdoom only accept 0-15 19990124 by Kin if (nosound) return 0;#ifdef SNDSERV if (sndserver) { unsigned char scmd[4]; scmd[0] = (unsigned char)*((int*)S_sfx[id].data); scmd[1] = (unsigned char)pitch; scmd[2] = (unsigned char)vol; scmd[3] = (unsigned char)sep; fputc('p',sndserver); fwrite(scmd,1,4,sndserver); 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. #ifndef SNDSERV int i; for (i=0; i<cv_numChannels.value; i++) { if (channelhandles[i] == handle) { channels[i] = 0; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -