/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ #include #include #include #include #include #include #include "fix.h" #include "object.h" #include "mono.h" #include "timer.h" #include "joy.h" #include "digi.h" #include "sounds.h" #include "args.h" #include "key.h" #include "newdemo.h" #include "game.h" #include "u_dpmi.h" #include "error.h" #include "wall.h" #include "cfile.h" #include "piggy.h" #include "text.h" #include "midiallg.h" #include "kconfig.h" #define _DIGI_MAX_VOLUME 128 // set lower to have difference with > F1_0 sounds #define MIN_VOLUME 10 // minimal volume to be played (in 0-F1_0(?) range) // patch files #define _MELODIC_PATCH "melodic.bnk" #define _DRUM_PATCH "drum.bnk" #define _DIGDRUM_PATCH "drum32.dig" static int Digi_initialized = 0; static int digi_atexit_called = 0; // Set to 1 if atexit(digi_close) was called int digi_driver_board = 0; int digi_driver_port = 0; int digi_driver_irq = 0; int digi_driver_dma = 0; //int digi_midi_type = 0; // Midi driver type //int digi_midi_port = 0; // Midi driver port static int digi_max_channels = 8; //static int digi_driver_rate = 11025; // rate to use driver at //static int digi_dma_buffersize = 4096; // size of the dma buffer to use (4k) int digi_timer_rate = 9943; // rate for the timer to go off to handle the driver system (120 Hz) int digi_lomem = 0; static int digi_volume = _DIGI_MAX_VOLUME; // Max volume //static int midi_volume = 128/2; // Max volume //static int midi_system_initialized = 0; //static int digi_system_initialized = 0; static int timer_system_initialized = 0; static int digi_sound_locks[MAX_SOUNDS]; //char digi_last_midi_song[16] = ""; //char digi_last_melodic_bank[16] = ""; //char digi_last_drum_bank[16] = ""; char *digi_driver_path = NULL; extern int midi_volume; //static void * lpInstruments = NULL; // pointer to the instrument file //static int InstrumentSize = 0; //static void * lpDrums = NULL; // pointer to the drum file //static int DrumSize = 0; // handle for the initialized MIDI song //MIDI *SongHandle = NULL; #define SOF_USED 1 // Set if this sample is used #define SOF_PLAYING 2 // Set if this sample is playing on a channel #define SOF_LINK_TO_OBJ 4 // Sound is linked to a moving object. If object dies, then finishes play and quits. #define SOF_LINK_TO_POS 8 // Sound is linked to segment, pos #define SOF_PLAY_FOREVER 16 // Play forever (or until level is stopped), otherwise plays once typedef int WORD; typedef struct sound_object { short signature; // A unique signature to this sound ubyte flags; // Used to tell if this slot is used and/or currently playing, and how long. fix max_volume; // Max volume that this sound is playing at fix max_distance; // The max distance that this sound can be heard at... int volume; // Volume that this sound is playing at int pan; // Pan value that this sound is playing at WORD handle; // What handle this sound is playing on. Valid only if SOF_PLAYING is set. short soundnum; // The sound number that is playing union { struct { short segnum; // Used if SOF_LINK_TO_POS field is used short sidenum; vms_vector position; }pos; struct { short objnum; // Used if SOF_LINK_TO_OBJ field is used short objsignature; }obj; }link; } sound_object; #define lp_segnum link.pos.segnum #define lp_sidenum link.pos.sidenum #define lp_position link.pos.position #define lo_objnum link.obj.objnum #define lo_objsignature link.obj.objsignature #define MAX_SOUND_OBJECTS 16 sound_object SoundObjects[MAX_SOUND_OBJECTS]; short next_signature=0; int digi_sounds_initialized=0; // a channel (voice) id is an int in Allegro typedef int CHANNEL; #define VALID_CHANNEL(ch) (ch >= 0) #define INVALID_CHANNEL -1 #define DIGI_SLOTS DIGI_VOICES // next_handle is a Descent internal number (hereafter reffered to as slot), // which indexes into SampleHandles, SoundNums and SoundVolumes. // only digi_max_channels entries are used. // SampleHandles points to sounddriver handles (voices) static int next_handle = 0; static CHANNEL SampleHandles[DIGI_SLOTS] = { INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL, INVALID_CHANNEL }; static int SoundNums[DIGI_SLOTS] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; static uint SoundVolumes[DIGI_SLOTS] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 }; void digi_reset_digi_sounds(); int verify_channel_not_used_by_soundobjects( int channel ); int digi_xlat_sound(int soundno) { if ( soundno < 0 ) return -1; if ( digi_lomem ) { soundno = AltSounds[soundno]; if ( soundno == 255 ) return -1; } return Sounds[soundno]; } void digi_close() { if (!Digi_initialized) return; remove_sound(); Digi_initialized = 0; if ( timer_system_initialized ) { // Remove timer... timer_set_function( NULL ); } timer_system_initialized = 0; } /* initialize sound system. 0=ok, 1=error */ int digi_init() { int i; if (!timer_system_initialized) { allg_snd_init(); timer_system_initialized = 1; } if (!Digi_initialized) { // amount of voices we need // 16 for normal sounds and 16 for SoundObjects (fan, boss) // for DIGMID we sacrify some sounds (32 is the max). // reserve_voices(allegro_using_digmid() ? 16 : 32, -1); if (install_sound(DIGI_AUTODETECT, MIDI_AUTODETECT, NULL)) return 1; set_volume(255, -1); digi_driver_board = digi_card; // only for 0, !=0 digi_midi_type = midi_card; // only for 0, !=0 } Digi_initialized = 1; if (!digi_atexit_called) { atexit( digi_close ); digi_atexit_called = 1; } digi_init_sounds(); digi_set_midi_volume( midi_volume ); for (i=0; i 0 ); if ( digi_sound_locks[soundnum] == 1 ) { digi_total_locks--; //mprintf(( 0, "Total sound locks = %d\n", digi_total_locks )); i = dpmi_unlock_region( GameSounds[soundnum].data, GameSounds[soundnum].len ); if ( !i ) Error( "Error unlocking sound %d\n", soundnum ); } digi_sound_locks[soundnum]--; } void digi_reset_digi_sounds() { int i; for (i = 0; i < DIGI_SLOTS; i++) { if (VALID_CHANNEL(SampleHandles[i])) { deallocate_voice(SampleHandles[i]); SampleHandles[i] = INVALID_CHANNEL; } if (SoundNums[i] != -1) { digi_unlock_sound_data(SoundNums[i]); SoundNums[i] = -1; } } for (i=0; i DIGI_VOICES) digi_max_channels = DIGI_VOICES; } int digi_get_max_channels() { return digi_max_channels; } int get_free_slot() { int ntries = 0; int ret_slot; TryNextChannel: // sound playing on slot ? if ( (VALID_CHANNEL(SampleHandles[next_handle])) && (voice_check(SampleHandles[next_handle])) ) { // don't use this slot if loud sound if ( (SoundVolumes[next_handle] > digi_volume) && (ntries= digi_max_channels ) next_handle = 0; ntries++; goto TryNextChannel; } //mprintf(( 0, "[SS:%d]", next_handle )); if (voice_check(next_handle)) mprintf((0,"Sound: deallocating used voice %d sound %d vol %d pos %d/%d\n", next_handle, SoundNums[next_handle], SoundVolumes[next_handle], voice_get_position(next_handle), GameSounds[SoundNums[next_handle]].len)); deallocate_voice(SampleHandles[next_handle]); SampleHandles[next_handle] = INVALID_CHANNEL; } if ( SoundNums[next_handle] != -1 ) { digi_unlock_sound_data(SoundNums[next_handle]); SoundNums[next_handle] = -1; } ret_slot = next_handle; next_handle++; if ( next_handle >= digi_max_channels ) next_handle = 0; return ret_slot; } CHANNEL digi_start_sound(int soundnum, int volume, int pan) { int i; int channel; int slot; volume = fixmul(volume, digi_volume); slot = get_free_slot(); digi_lock_sound_data(soundnum); channel = play_sample(&GameSounds[soundnum], volume, pan >> 8, 1000, 0); if (channel < 0) { mprintf(( 1, "NOT ENOUGH SOUND SLOTS!!!\n" )); digi_unlock_sound_data(soundnum); return -1; } release_voice(channel); //mprintf(( 0, "Starting sound on channel %d\n", channel )); #ifndef NDEBUG verify_channel_not_used_by_soundobjects(channel); #endif // find slots pointing to just allocated channel and kill sounds on it // (because they can't be playing right now) for (i=0; i 127 ) //-killed- midi_volume = 127; //-killed- else if ( mvolume < 0 ) //-killed- midi_volume = 0; //-killed- else //-killed- midi_volume = mvolume; //-killed- //-killed- if ( (digi_midi_type > 0) ) { //-killed- if ( (old_volume < 1) && ( midi_volume > 1 ) ) { //-killed- if (SongHandle == NULL ) //-killed- digi_play_midi_song( digi_last_midi_song, digi_last_melodic_bank, digi_last_drum_bank, 1 ); //-killed- } //-killed- set_volume(-1, midi_volume * 2 + (midi_volume & 1)); //-killed- } //-killed- } void digi_set_digi_volume( int dvolume ) { dvolume = fixmuldiv( dvolume, _DIGI_MAX_VOLUME, 0x7fff); if ( dvolume > _DIGI_MAX_VOLUME ) digi_volume = _DIGI_MAX_VOLUME; else if ( dvolume < 0 ) digi_volume = 0; else digi_volume = dvolume; if ( !Digi_initialized ) return; if ( digi_driver_board <= 0 ) return; digi_sync_sounds(); } // 0-0x7FFF void digi_set_volume( int dvolume, int mvolume ) { digi_set_midi_volume( mvolume ); digi_set_digi_volume( dvolume ); // mprintf(( 1, "Volume: 0x%x and 0x%x\n", digi_volume, midi_volume )); } //-killed- void digi_stop_current_song() //-killed- { //-killed- if (SongHandle) { //-killed- destroy_midi(SongHandle); //-killed- SongHandle = NULL; //-killed- } //-killed- } //-killed- //-killed- void digi_play_midi_song( char * filename, char * melodic_bank, char * drum_bank, int loop ) //-killed- { //-killed- //char fname[128]; //-killed- //-killed- if (!Digi_initialized) return; //-killed- if ( digi_midi_type <= 0 ) return; //-killed- //-killed- digi_stop_current_song(); //-killed- //-killed- if ( filename == NULL ) return; //-killed- //-killed- strcpy( digi_last_midi_song, filename ); //-killed- strcpy( digi_last_melodic_bank, melodic_bank ); //-killed- strcpy( digi_last_drum_bank, drum_bank ); //-killed- //-killed- if ( midi_volume < 1 ) //-killed- //-killed- SongHandle = NULL; //-killed- //-killed- #if 0 /* needs bank loading to sound right */ //-killed- if (midi_card <= 4) { /* FM cards */ //-killed- int sl; //-killed- sl = strlen( filename ); //-killed- strcpy( fname, filename ); //-killed- fname[sl-1] = 'q'; //-killed- SongHandle = load_hmp(fname); //-killed- } //-killed- #endif //-killed- //-killed- if ( !SongHandle ) //-killed- SongHandle = load_hmp(filename); //-killed- //-killed- if (SongHandle) { //-killed- if (play_midi(SongHandle, loop)) { //-killed- destroy_midi(SongHandle); //-killed- SongHandle = NULL; //-killed- } //-killed- } //-killed- if (!SongHandle) { //-killed- mprintf( (1, "\nAllegro Error : %s", allegro_error )); //-killed- } //-killed- } void digi_get_sound_loc( vms_matrix * listener, vms_vector * listener_pos, int listener_seg, vms_vector * sound_pos, int sound_seg, fix max_volume, int *volume, int *pan, fix max_distance ) { vms_vector vector_to_sound; fix angle_from_ear, cosang,sinang; fix distance; fix path_distance; *volume = 0; *pan = 0; max_distance = (max_distance*5)/4; // Make all sounds travel 1.25 times as far. // Warning: Made the vm_vec_normalized_dir be vm_vec_normalized_dir_quick and got illegal values to acos in the fang computation. distance = vm_vec_normalized_dir_quick( &vector_to_sound, sound_pos, listener_pos ); if (distance < max_distance ) { int num_search_segs = f2i(max_distance/20); if ( num_search_segs < 1 ) num_search_segs = 1; path_distance = find_connected_distance(listener_pos, listener_seg, sound_pos, sound_seg, num_search_segs, WID_RENDPAST_FLAG ); if ( path_distance > -1 ) { *volume = max_volume - fixdiv(path_distance,max_distance); //mprintf( (0, "Sound path distance %.2f, volume is %d / %d\n", f2fl(distance), *volume, max_volume )); if (*volume > 0 ) { angle_from_ear = vm_vec_delta_ang_norm(&listener->rvec,&vector_to_sound,&listener->uvec); fix_sincos(angle_from_ear,&sinang,&cosang); //mprintf( (0, "volume is %.2f\n", f2fl(*volume) )); if (Config_channels_reversed) cosang *= -1; *pan = (cosang + F1_0)/2; } else { *volume = 0; } } } } void digi_init_sounds() { int i; if (!Digi_initialized) return; if (digi_driver_board<1) return; digi_reset_digi_sounds(); for (i=0; i> 8, /* 0-255 */ 1000, (SoundObjects[i].flags & SOF_PLAY_FOREVER) ? 1 : 0 ); if (SoundObjects[i].handle < 0) { mprintf(( 1, "NOT ENOUGH SOUND SLOTS!!! (SoundObject)\n" )); //digi_unlock_sound(SoundObjects[i].soundnum); //never unlocked... } else { SoundObjects[i].flags |= SOF_PLAYING; reset_slots_on_channel( SoundObjects[i].handle ); } } int digi_link_sound_to_object2( int org_soundnum, short objnum, int forever, fix max_volume, fix max_distance ) { int i,volume,pan; object * objp; int soundnum; soundnum = digi_xlat_sound(org_soundnum); if ( max_volume < 0 ) return -1; // if ( max_volume > F1_0 ) max_volume = F1_0; if (!Digi_initialized) return -1; if (soundnum < 0 ) return -1; if (GameSounds[soundnum].data==NULL) { Int3(); return -1; } if ((objnum<0)||(objnum>Highest_object_index)) return -1; if (digi_driver_board<1) return -1; if ( !forever ) { // Hack to keep sounds from building up... digi_get_sound_loc( &Viewer->orient, &Viewer->pos, Viewer->segnum, &Objects[objnum].pos, Objects[objnum].segnum, max_volume,&volume, &pan, max_distance ); digi_play_sample_3d( org_soundnum, pan, volume, 0 ); return -1; } for (i=0; iorient, &Viewer->pos, Viewer->segnum, &objp->pos, objp->segnum, SoundObjects[i].max_volume, &SoundObjects[i].volume, &SoundObjects[i].pan, SoundObjects[i].max_distance ); if (!forever || SoundObjects[i].volume >= MIN_VOLUME) digi_start_sound_object(i); return SoundObjects[i].signature; } int digi_link_sound_to_object( int soundnum, short objnum, int forever, fix max_volume ) { // 10 segs away return digi_link_sound_to_object2( soundnum, objnum, forever, max_volume, 256*F1_0 ); } int digi_link_sound_to_pos2( int org_soundnum, short segnum, short sidenum, vms_vector * pos, int forever, fix max_volume, fix max_distance ) { int i, volume, pan; int soundnum; soundnum = digi_xlat_sound(org_soundnum); if ( max_volume < 0 ) return -1; // if ( max_volume > F1_0 ) max_volume = F1_0; if (!Digi_initialized) return -1; if (soundnum < 0 ) return -1; if (GameSounds[soundnum].data==NULL) { Int3(); return -1; } if (digi_driver_board<1) return -1; if ((segnum<0)||(segnum>Highest_segment_index)) return -1; if ( !forever ) { // Hack to keep sounds from building up... digi_get_sound_loc( &Viewer->orient, &Viewer->pos, Viewer->segnum, pos, segnum, max_volume, &volume, &pan, max_distance ); digi_play_sample_3d( org_soundnum, pan, volume, 0 ); return -1; } for (i=0; iorient, &Viewer->pos, Viewer->segnum, &SoundObjects[i].lp_position, SoundObjects[i].lp_segnum, SoundObjects[i].max_volume, &SoundObjects[i].volume, &SoundObjects[i].pan, SoundObjects[i].max_distance ); if (!forever || SoundObjects[i].volume >= MIN_VOLUME) digi_start_sound_object(i); return SoundObjects[i].signature; } int digi_link_sound_to_pos( int soundnum, short segnum, short sidenum, vms_vector * pos, int forever, fix max_volume ) { return digi_link_sound_to_pos2( soundnum, segnum, sidenum, pos, forever, max_volume, F1_0 * 256 ); } void digi_kill_sound_linked_to_segment( int segnum, int sidenum, int soundnum ) { int i,killed; soundnum = digi_xlat_sound(soundnum); if (!Digi_initialized) return; if (digi_driver_board<1) return; killed = 0; for (i=0; i 1 ) { mprintf( (1, "ERROR: More than 1 sounds were deleted from seg %d\n", segnum )); } } void digi_kill_sound_linked_to_object( int objnum ) { int i,killed; if (!Digi_initialized) return; if (digi_driver_board<1) return; killed = 0; for (i=0; i 1 ) { mprintf( (1, "ERROR: More than 1 sounds were deleted from object %d\n", objnum )); } } void digi_sync_sounds() { int i; int oldvolume, oldpan; if (!Digi_initialized) return; if (digi_driver_board<1) return; for (i=0; iorient, &Viewer->pos, Viewer->segnum, &SoundObjects[i].lp_position, SoundObjects[i].lp_segnum, SoundObjects[i].max_volume, &SoundObjects[i].volume, &SoundObjects[i].pan, SoundObjects[i].max_distance ); } else if ( SoundObjects[i].flags & SOF_LINK_TO_OBJ ) { object * objp; objp = &Objects[SoundObjects[i].lo_objnum]; if ((objp->type==OBJ_NONE) || (objp->signature!=SoundObjects[i].lo_objsignature)) { // The object that this is linked to is dead, so just end this sound if it is looping. if ( (SoundObjects[i].flags & SOF_PLAYING) && (SoundObjects[i].flags & SOF_PLAY_FOREVER)) { deallocate_voice(SoundObjects[i].handle); } SoundObjects[i].flags = 0; // Mark as dead, so some other sound can use this sound continue; // Go on to next sound... } else { digi_get_sound_loc( &Viewer->orient, &Viewer->pos, Viewer->segnum, &objp->pos, objp->segnum, SoundObjects[i].max_volume, &SoundObjects[i].volume, &SoundObjects[i].pan, SoundObjects[i].max_distance ); } } if (oldvolume != SoundObjects[i].volume) { if ( SoundObjects[i].volume < MIN_VOLUME ) { // Sound is too far away, so stop it from playing. if ((SoundObjects[i].flags & SOF_PLAYING)&&(SoundObjects[i].flags & SOF_PLAY_FOREVER)) { deallocate_voice(SoundObjects[i].handle); SoundObjects[i].flags &= ~SOF_PLAYING; // Mark sound as not playing } } else { if (!(SoundObjects[i].flags & SOF_PLAYING)) { digi_start_sound_object(i); } else { voice_set_volume(SoundObjects[i].handle, fixmuldiv(SoundObjects[i].volume,digi_volume,F1_0)); } } } if (oldpan != SoundObjects[i].pan) { if (SoundObjects[i].flags & SOF_PLAYING) voice_set_pan(SoundObjects[i].handle, SoundObjects[i].pan >> 8); } } } } int sound_paused = 0; void digi_pause_all() { int i; if (!Digi_initialized) return; if (sound_paused==0) { // if ( digi_midi_type > 0 ) { // if (SongHandle) // midi_pause(); // } digi_midi_pause(); if (digi_driver_board>0) { for (i=0; i 0 ); if (sound_paused==1) { // resume sound here // if ( digi_midi_type > 0 ) { // if (SongHandle) // midi_resume(); // } digi_midi_resume(); } sound_paused--; } void digi_stop_all() { int i; if (!Digi_initialized) return; // if ( digi_midi_type > 0 ) { // if (SongHandle) { // destroy_midi(SongHandle); // SongHandle = NULL; // } // } digi_midi_stop(); if (digi_driver_board>0) { for (i=0; i0) { for (i=0; ivoices, digi_max_channels, digi_total_locks, active_slots,done_slots,allg_used, next_handle); } #endif