// 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: none // //----------------------------------------------------------------------------- static const char rcsid[] = "$Id: s_sound.c,v 1.6 1997/02/03 22:45:12 b1 Exp $"; #include #include #include "i_system.h" #include "i_sound.h" #include "sounds.h" #include "s_sound.h" #include "z_zone.h" #include "m_random.h" #include "w_wad.h" #include "doomdef.h" #include "p_local.h" //#include "m_music.h" #include "doomstat.h" //#include "qmus2mid.h" #include "kolibri.h" #include "sound.h" void WriteDebug(char *); // Purpose? const char snd_prefixen[] = { 'P', 'P', 'A', 'S', 'S', 'S', 'M', 'M', 'M', 'S', 'S', 'S' }; #define S_MAX_VOLUME 127 // when to clip out sounds // Does not fit the large outdoor areas. #define S_CLIPPING_DIST (1200*0x10000) // Distance tp origin when sounds should be maxed out. // This should relate to movement clipping resolution // (see BLOCKMAP handling). // Originally: (200*0x10000). #define S_CLOSE_DIST (160*0x10000) #define S_ATTENUATOR ((S_CLIPPING_DIST-S_CLOSE_DIST)>>FRACBITS) // Adjustable by menu. #define NORM_VOLUME snd_MaxVolume #define NORM_PITCH 128 #define NORM_PRIORITY 64 #define NORM_SEP 128 #define S_PITCH_PERTURB 1 #define S_STEREO_SWING (96*0x10000) // percent attenuation from front to back #define S_IFRACVOL 30 #define NA 0 //#define S_NUMCHANNELS 2 #define NUM_CHANNELS 16 // Current music/sfx card - index useless // w/o a reference LUT in a sound module. extern int snd_MusicDevice; extern int snd_SfxDevice; // Config file? Same disclaimer as above. extern int snd_DesiredMusicDevice; extern int snd_DesiredSfxDevice; typedef struct { // sound information (if null, channel avail.) sfxinfo_t* sfxinfo; // origin of sound void* origin; // handle of the sound being played int handle; } channel_t; // the set of channels available static channel_t* channels; // These are not used, but should be (menu). // Maximum volume of a sound effect. // Internal default is max out of 0-15. int snd_SfxVolume = 15; // Maximum volume of music. Useless so far. int snd_MusicVolume = 15; // whether songs are mus_paused static boolean mus_paused; // music currently being played static musicinfo_t* mus_playing=0; // following is set // by the defaults code in M_misc: // number of channels available int numChannels; static int nextcleanup; // // Internals. // int S_getChannel ( void* origin, sfxinfo_t* sfxinfo, int sfxid ); int S_AdjustSoundParams ( mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch ); void S_StopChannel(int cnum); // // Initializes sound stuff, including volume // Sets channels, SFX and music volume, // allocates channel buffer, sets S_sfx lookup. // SNDBUF hMixBuff; volatile int sound_state; void sound_proc(void); void I_UpdateSound( void ); void S_Init ( int sfxVolume, int musicVolume ) { int i; char *thread_stack; numChannels = NUM_CHANNELS; // Whatever these did with DMX, these are rather dummies now. I_SetChannels(); S_SetSfxVolume(sfxVolume); // No music with Linux - another dummy. S_SetMusicVolume(musicVolume); // Allocating the internal channels for mixing // (the maximum numer of sounds rendered // simultaneously) within zone memory. channels = (channel_t *) Z_Malloc(numChannels*sizeof(channel_t), PU_STATIC, 0); // Free all channels for use for (i=0 ; i(ver&0xFFFF)) { printf("Sound version mismatch\n\r"); printf("Current version: %d, required version %d\n\r", ver&0xFFFF, SOUND_VERSION); _asm { mov eax, -1 int 0x40 }; }; if(SOUND_VERSION<(ver >> 16)) { printf("Sound version obsolete\n\r"); printf("Compatible version: %d, required version %d\n\r", ver, SOUND_VERSION); _asm { mov eax, -1 int 0x40 }; }; if (err = CreateBuffer(PCM_2_16_11|PCM_RING,0, &hMixBuff)) { printf("Error %x sound not available\n\r", err); printf("handle = %x\n\r", hMixBuff); _asm { mov eax, -1 int 0x40 }; } if(err = GetBufferSize(hMixBuff, &mix_size)) { printf("Error %x get buffer size\n\r", err); printf("size = %x\n\r", mix_size); _asm { mov eax, -1 int 0x40 }; }; mix_size= mix_size/2; printf("mixer size %d\n\r", mix_size); if(err=PlayBuffer(hMixBuff, 0)) { printf("Error %x play buffer\n\r", err); _asm { mov eax, -1 int 0x40 }; } mixbuffer = malloc(mix_size); while(sound_state) { GetNotify(&evnt); if(evnt.code != 0xFF000001) { printf("invalid code %d\n\r", evnt.code); continue; } if(evnt.stream != hMixBuff) { printf("invalid stream %d hMixBuff= %d\n\r", evnt.stream, hMixBuff); continue; }; mix_offset= evnt.offset; I_UpdateSound(); }; //flush sound buffers for(i=0; i<32; i++) { GetNotify(&evnt); if(evnt.code != 0xFF000001) { printf("invalid code %d\n\r", evnt.code); continue; } if(evnt.stream != hMixBuff) { printf("invalid stream %d hMixBuff= %d\n\r", evnt.stream, hMixBuff); continue; }; mix_offset= evnt.offset; I_UpdateSound(); }; _asm { mov eax, -1 int 0x40 }; }; // // Per level startup code. // Kills playing sounds at start of level, // determines music if any, changes music. // void S_Start(void) { int cnum; int mnum; // kill all playing sounds at start of level // (trust me - a good idea) for (cnum=0 ; cnum mus_e3m9) // mnum -= mus_e3m9; S_ChangeMusic(mnum, true); nextcleanup = 15; } void S_StartSoundAtVolume( void *origin_p, int sfx_id, int volume ) { int rc; int sep; int pitch; int priority; sfxinfo_t *sfx; int cnum; // int chnum; <-- 10.9.98 compiler warning mobj_t* origin = (mobj_t *)origin_p; //WriteDebug("S_StartSoundAtVolume...\n"); // Debug. /*fprintf( stderr,"S_StartSoundAtVolume: playing sound %d (%s)\n",sfx_id, S_sfx[sfx_id].name );*/ // check for bogus sound # if (sfx_id < 1 || sfx_id > NUMSFX) I_Error("Bad sfx #: %d", sfx_id); sfx = &S_sfx[sfx_id]; // Initialize sound parameters if (sfx->link) { pitch = sfx->pitch; priority = sfx->priority; volume += sfx->volume; if (volume < 1) { //WriteDebug("Volume off...\n"); return; } if (volume > snd_SfxVolume) volume = snd_SfxVolume; } else { pitch = NORM_PITCH; priority = NORM_PRIORITY; } // Check to see if it is audible, // and if not, modify the params if (origin && origin != players[consoleplayer].mo) { rc = S_AdjustSoundParams(players[consoleplayer].mo, origin, &volume, &sep, &pitch); if ( origin->x == players[consoleplayer].mo->x && origin->y == players[consoleplayer].mo->y) { sep = NORM_SEP; } if (!rc) { //WriteDebug("No rc from S_AdjustSoundParams...\n"); return; } } else { sep = NORM_SEP; } // hacks to vary the sfx pitches if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit) { pitch += 8 - (M_Random()&15); if (pitch < 0) pitch = 0; else if (pitch>255) pitch = 255; } else if (sfx_id != sfx_itemup && sfx_id != sfx_tink) { pitch += 16 - (M_Random()&31); if (pitch<0) pitch = 0; else if (pitch > 255) pitch = 255; } // kill old sound S_StopSound(origin); // try to find a channel cnum = S_getChannel(origin, sfx, sfx_id); if (cnum<0) { //WriteDebug("cnum < 0 -- no channel...\n"); return; } // // This is supposed to handle the loading/caching. // For some odd reason, the caching is done nearly // each time the sound is needed? // // get lumpnum if necessary if (sfx->lumpnum < 0) sfx->lumpnum = I_GetSfxLumpNum(sfx); // cache data if necessary if (!sfx->data) { sfx->data = (void *) W_CacheLumpNum(sfx->lumpnum, PU_MUSIC); } // increase the usefulness if (sfx->usefulness++ < 0) sfx->usefulness = 1; // Assigns the handle to one of the channels in the // mix/output buffer. //WriteDebug("I_StartSound...\n"); channels[cnum].handle = I_StartSound(sfx_id,volume,sep,pitch,priority); channels[cnum].handle = cnum; channels[cnum].sfxinfo = sfx; channels[cnum].origin = origin; } void S_StartSound( void *origin, int sfx_id ) { #ifdef SAWDEBUG // if (sfx_id == sfx_sawful) // sfx_id = sfx_itemup; #endif S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume); // UNUSED. We had problems, had we not? #ifdef SAWDEBUG { int i; int n; static mobj_t* last_saw_origins[10] = {1,1,1,1,1,1,1,1,1,1}; static int first_saw=0; static int next_saw=0; if (sfx_id == sfx_sawidl || sfx_id == sfx_sawful || sfx_id == sfx_sawhit) { for (i=first_saw;i!=next_saw;i=(i+1)%10) if (last_saw_origins[i] != origin) fprintf(stderr, "old origin 0x%lx != " "origin 0x%lx for sfx %d\n", last_saw_origins[i], origin, sfx_id); last_saw_origins[next_saw] = origin; next_saw = (next_saw + 1) % 10; if (next_saw == first_saw) first_saw = (first_saw + 1) % 10; for (n=i=0; i1) { for (i=0; ihandle); mus_paused = true; } } void S_ResumeSound(void) { if (mus_playing && mus_paused) { I_ResumeSong(mus_playing->handle); mus_paused = false; } } // // Updates music & sounds // void S_UpdateSounds(void* listener_p) { int audible; int cnum; int volume; int sep; int pitch; sfxinfo_t* sfx; channel_t* c; mobj_t* listener = (mobj_t*)listener_p; // Clean up unused data. // This is currently not done for 16bit (sounds cached static). // DOS 8bit remains. /*if (gametic > nextcleanup) { for (i=1 ; i -1) { if (--S_sfx[i].usefulness == -1) { Z_ChangeTag(S_sfx[i].data, PU_CACHE); S_sfx[i].data = 0; } } } nextcleanup = gametic + 15; }*/ for (cnum=0 ; cnumsfxinfo; if (c->sfxinfo) { if (I_SoundIsPlaying(c->handle)) { // initialize parameters volume = snd_SfxVolume; pitch = NORM_PITCH; sep = NORM_SEP; if (sfx->link) { pitch = sfx->pitch; volume += sfx->volume; if (volume < 1) { S_StopChannel(cnum); continue; } else if (volume > snd_SfxVolume) { volume = snd_SfxVolume; } } // check non-local sounds for distance clipping // or modify their params if (c->origin && listener_p != c->origin) { audible = S_AdjustSoundParams(listener, c->origin, &volume, &sep, &pitch); if (!audible) { S_StopChannel(cnum); } else I_UpdateSoundParams(c->handle, volume, sep, pitch); } } else { // if channel is allocated but sound has stopped, // free it S_StopChannel(cnum); } } } // kill music if it is a single-play && finished // if ( mus_playing // && !I_QrySongPlaying(mus_playing->handle) // && !mus_paused ) // S_StopMusic(); } void S_SetMusicVolume(int volume) { if (volume < 0 || volume > 127) { I_Error("Attempt to set music volume at %d", volume); } I_SetMusicVolume(127); I_SetMusicVolume(volume); snd_MusicVolume = volume; } void S_SetSfxVolume(int volume) { if (volume < 0 || volume > 127) I_Error("Attempt to set sfx volume at %d", volume); snd_SfxVolume = volume; } // // Starts some music with the music id found in sounds.h. // void S_StartMusic(int m_id) { // S_ChangeMusic(m_id, false); } // clean-up&code for orig midi 10.9.98-dlw void S_ChangeMusic(int musicnum, int looping) { // I_PlaySong(music->handle, looping); // mus_playing = music; } void S_StopMusic(void) { if (mus_playing) { if (mus_paused) I_ResumeSong(mus_playing->handle); I_StopSong(mus_playing->handle); I_UnRegisterSong(mus_playing->handle); Z_ChangeTag(mus_playing->data, PU_CACHE); mus_playing->data = 0; mus_playing = 0; } } void S_StopChannel(int cnum) { int i; channel_t* c = &channels[cnum]; if (c->sfxinfo) { // stop the sound playing if (I_SoundIsPlaying(c->handle)) { #ifdef SAWDEBUG if (c->sfxinfo == &S_sfx[sfx_sawful]) fprintf(stderr, "stopped\n"); #endif I_StopSound(c->handle); } // check to see // if other channels are playing the sound for (i=0 ; isfxinfo == channels[i].sfxinfo) { break; } } // degrade usefulness of sound data c->sfxinfo->usefulness--; c->sfxinfo = 0; } } // // Changes volume, stereo-separation, and pitch variables // from the norm of a sound effect to be played. // If the sound is not audible, returns a 0. // Otherwise, modifies parameters and returns 1. // int S_AdjustSoundParams (mobj_t* listener, mobj_t* source, int* vol, int* sep, int* pitch) { fixed_t approx_dist; fixed_t adx; fixed_t ady; angle_t angle; // calculate the distance to sound origin // and clip it if necessary adx = abs(listener->x - source->x); ady = abs(listener->y - source->y); // From _GG1_ p.428. Appox. eucledian distance fast. approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); if (gamemap != 8 && approx_dist > S_CLIPPING_DIST) { return 0; } // angle of source to listener angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y); if (angle > listener->angle) angle = angle - listener->angle; else angle = angle + (0xffffffff - listener->angle); angle >>= ANGLETOFINESHIFT; // stereo separation *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS); // volume calculation if (approx_dist < S_CLOSE_DIST) { *vol = snd_SfxVolume; } else if (gamemap == 8) { if (approx_dist > S_CLIPPING_DIST) approx_dist = S_CLIPPING_DIST; *vol = 15+ ((snd_SfxVolume-15) *((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } else { // distance effect *vol = (snd_SfxVolume * ((S_CLIPPING_DIST - approx_dist)>>FRACBITS)) / S_ATTENUATOR; } return (*vol > 0); } // // S_getChannel : // If none available, return -1. Otherwise channel #. // int S_getChannel( void *origin, sfxinfo_t *sfxinfo, int sfxid ) { // channel number to use int cnum; channel_t* c; // Find an open channel //for (cnum = 0; cnum < numChannels; cnum++) for (cnum = 0; cnum < NUM_CHANNELS; cnum++) { if (!channels[cnum].sfxinfo) break; else if (origin && channels[cnum].origin == origin) { S_StopChannel(cnum); break; } } // None available if (cnum == NUM_CHANNELS) { // Look for lower priority for (cnum = NUMSFX; cnum < NUM_CHANNELS; cnum++) if (channels[cnum].sfxinfo->priority >= sfxinfo->priority) break; if (cnum == numChannels) { // FUCK! No lower priority. Sorry, Charlie. return -1; } else { // Otherwise, kick out lower priority. S_StopChannel(cnum); } } c = &channels[cnum]; // channel is decided to be cnum. c->sfxinfo = sfxinfo; c->origin = origin; return cnum; }