// 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 // 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" // 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_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]; // // 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; // Get the sound data from the WAD, allocate lump // in zone memory. sprintf(name, "ds%s", sfxname); // 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 ); // 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> 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; // 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; 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) // int I_StartSound(int id, int vol, int sep, int pitch, int priority ) { // Returns a handle (not used). id = addsfx( id, vol, steptable[pitch], sep ); return id; } 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. // UNUSED. handle = 0; } int I_SoundIsPlaying(int handle) { // 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 ) { // 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]; channelstepremainder[ chan ] += channelstep[ chan ]; channels[ chan ] += channelstepremainder[ chan ] >> 16; 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; } I_SubmitSound(mixbuffer); } // // 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. // UNUSED. handle = vol = sep = pitch = 0; } void I_ShutdownSound(void) { // Wait till all pending sounds are finished. int done = 0; int i; // FIXME (below). printf( stderr, "I_ShutdownSound: NOT finishing pending sounds\n"); while ( !done ) { for( i=0 ; i<8 && !channels[i] ; i++); // FIXME. No proper channel output. //if (i==8) done=1; } return; } void I_InitSound() { int i; printf( stderr, "I_InitSound: "); for (i=1 ; idata; lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)]; } } printf( stderr, " pre-cached all sound data\n"); // Now initialize mixbuffer with zero. for ( i = 0; i< MIXBUFFERSIZE; i++ ) mixbuffer[i] = 0; // Finished initialization. printf(stderr, "I_InitSound: sound module ready\n"); } // // 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; } // Interrupt handler. void I_HandleSoundTimer( int ignore ) { // UNUSED, but required. ignore = 0; return; }