// 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 #include #include #include #include #include //#ifndef LINUX //#include //#endif #include #include //#include //#include // Linux voxware output. //#include // Timer stuff. Experimental. #include #include #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" typedef unsigned int DWORD; //////////////////////////////////////////////////////////////////////////// // WinDoom - DirectSound //////////////////////////////////////////////////////////////////////////// #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); #define NUM_DSBUFFERS 256 typedef enum { dsb_perm, dsb_temp } dsb_type; 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 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. // // // 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; iname); 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; } // // 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_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; I_CreateSound(); 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)]; } //having to crack here cuz crashing out at sound file 86 chgun // incidentally the only 'linked' sound in the whole pile // 10.10.98 dlw tested good. if(i==86) CreateSoundBuffer(i, lengths[1], S_sfx[1].data); else 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) { } static int looping=0; static int musicdies=-1; // 10.12.98 dlw - cleanup the music files void I_ShutdownMusic(void) { int trasherror; //self explained trasherror=unlink("doomsong.mus"); trasherror=unlink("doomsong.mid"); printf("I_ShutdownMusic...\n"); } 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; } void I_SoundDelTimer() { // Debug. //if ( I_SoundSetTimer( 0 ) == -1) // fprintf( stderr, "I_SoundDelTimer: failed to remove interrupt. Doh!\n"); }