/**********************************************************************
This file is part of Crack dot Com's free source code release of
Golgotha.
for
information about compiling & licensing issues visit this URL
If that doesn't help, contact Jonathan Clark at golgotha_source@usa.net (Subject should have "GOLG" in it) ***********************************************************************/ #include "sound/sfx_id.hh" #include "string/string.hh" #include "file/file.hh" #include "error/error.hh" #include "loaders/wav_load.hh" #include "sound/sound.hh" #include "file/file.hh" #include "math/transform.hh" #include "time/profile.hh" #include "music/stream.hh" #include "main/main.hh" #include "app/registry.hh" #includei4_profile_class pf_sfx_update("sfx_update"); s1_sfx_ref *s1_sfx_ref::first=0; s1_sound_handle *s1_sound_handle::first=0; int show_sfx=0; static char sfx_path[80]; static void get(char *var, char *buffer, int max_buffer, char *def) { if (i4_get_registry(I4_REGISTRY_USER, "SOFTWARE\\Crack dot Com\\i4\\1.0", var, buffer, max_buffer)) return ; char *c=getenv(var); if (c) { strncpy(buffer, c, max_buffer); return ; } strcpy(buffer, def); } char *s1_get_sfx_path() { if (!sfx_path[0]) { get("G_SFX_PATH", sfx_path, 80, "sfx"); } return sfx_path; } static char *add_sfx_path(char *filename, char *buffer) { sprintf(buffer, "%s/%s", s1_get_sfx_path(), filename); return buffer; } s1_sound_handle::~s1_sound_handle() { if (playing) s1_end_looping(*this); } s1_sound_handle::s1_sound_handle() { originator=0; next=0; channel_on=-1; playing=i4_F; } static i4_voice_class *load_sfx(s1_sfx_ref &ref) { char buf[200]; // don't try if no sound if (i4_sound_man==&i4_null_sound) return 0; i4_file_class *fp=i4_open(add_sfx_path(ref.name, buf)); if (!fp) { i4_warning("sfx missing %s (%s:%d)\n", ref.name, ref.file_defined_in, ref.line_defined_on); return 0; } i4_sound_info info; i4_load_wav_info(fp, info); if (info.channels==2) i4_warning("%s is stereo sound (waste of memory)\n", ref.name); if (info.sample_rate>22*1024) i4_warning("%s is %dHz (waste of memory)\n", ref.name, info.sample_rate); else if (show_sfx) i4_warning("%s is %dHz\n", ref.name, info.sample_rate); i4_sound_manager_class::sound_parameters p; p.channels=info.channels; p.sample_size=info.sample_size; p.frequency=info.sample_rate; if (ref.flags & S1_3D) p.capable_3d=i4_T; i4_voice_class *v = i4_sound_man->alloc(info.size, p); if (v) { void *b1,*b2; w32 s1,s2; v->lock(0, info.size, b1, s1, b2, s2); fp->read(b1,s1); v->unlock(b1, s1, b2, s2); } delete fp; return v; } s1_sfx_ref::s1_sfx_ref(char *filename, w8 flags, w8 priority, float dist, char *file, int line) : flags(flags), name(filename), file_defined_in(file), line_defined_on(line), priority(priority), hearable_distance_sqrd(dist*dist) { next=first; first=this; id=0; base_sfx=0; } s1_sfx_ref::~s1_sfx_ref() { if (first==this) first=first->next; else { s1_sfx_ref *last=0, *p=first; for (;p!=this && p;) { last=p; p=p->next; } if (!p) i4_error("~sfx not found"); last->next=next; } } struct channel_info { i4_stream_wav_player *stream; i4_voice_class *voice; s1_sfx_ref *originator; s1_sound_handle *loop_handler; void free() { if (stream) { delete stream; stream=0; voice=0; } else if (voice) { delete voice; voice=0; } if (loop_handler) { loop_handler->channel_on=-1; loop_handler=0; } } }; enum { T_CHANNELS=8 }; static channel_info channels[T_CHANNELS]; static i4_3d_vector camera_pos; static int channels_sorted=0; void s1_get_camera_pos(i4_3d_vector &pos) { pos=camera_pos; } void s1_load() // load all the referenced static & dynamic sounds { for (int j=1; j next) if ((s->flags&S1_STREAMED)==0 && !s->base_sfx) s->base_sfx=load_sfx(*s); for (int i=0; i next) if (s->base_sfx) { delete s->base_sfx; s->base_sfx=0; } } static float get_dist_sqrd(float x, float y, float z) { return (camera_pos.x-x)*(camera_pos.x-x)+ (camera_pos.y-y)*(camera_pos.y-y)+ (camera_pos.z-z)*(camera_pos.z-z); } int channel_compare(const void *va, const void *vb) { const channel_info *a=(channel_info *)va; const channel_info *b=(channel_info *)vb; if (a->originator->priority>b->originator->priority) return 1; else if (a->originator->priority originator->priority) return -1; // non-looping sounds have priority over looping else if (a->loop_handler && !b->loop_handler) return -1; else if (!a->loop_handler && b->loop_handler) return 1; // compare the distance for 2 looping sounds else if (a->loop_handler && b->loop_handler) { float d1=a->loop_handler->dist_sqrd; float d2=b->loop_handler->dist_sqrd; if (d1>d2) return 1; else if (d1 voice->get_sound_position(); int o2=b->voice->get_sound_position(); if (o1 o2) return 1; else return 0; } } int find_channel(s1_sfx_ref *ref, s1_sound_handle *h) { int use=-1,i; for (i=0; i priority originator->priority) use=T_CHANNELS-1; else if (ref->priority==w->originator->priority) // we are not better { if (w->loop_handler && !h) use=T_CHANNELS-1; else if (w->loop_handler && h) { float d1=w->loop_handler->dist_sqrd; float d2=h->dist_sqrd; if (d1>d2) use=T_CHANNELS-1; } else if (!h) use=T_CHANNELS-1; } if (use!=-1) { channels_sorted=0; channels[use].free(); // i4_warning("throwing out %s (priority %d) for %s (p=%d)", // channels[use].originator->name, // channels[use].originator->priority, // ref->name, ref->priority); } // else // i4_warning("no channel for %s (priority %d)", // ref->name, ref->priority); return use; } void s1_set_camera_pos(i4_transform_class &new_cam_transform) { int i=0; pf_sfx_update.start(); i4_3d_vector last_pos=camera_pos; new_cam_transform.inverse_transform(i4_3d_vector(0,0,0), camera_pos); s1_sound_handle *s; i4_3d_point_class temp,cfv,cuv; i4_3d_vector cam_velocity; cam_velocity = i4_3d_vector(camera_pos.x-last_pos.x, camera_pos.y-last_pos.y, camera_pos.z-last_pos.z); i4_sound_man->set_listener_position(-camera_pos.y, camera_pos.z, camera_pos.x); new_cam_transform.inverse_transform_3x3(i4_3d_vector(0,0,1),cfv); new_cam_transform.inverse_transform_3x3(i4_3d_vector(0,-1,0),cuv); //negative y in viewspace is actually "up" //just in case they dont come out exactly correct cfv.normalize(); cuv.normalize(); i4_sound_man->set_listener_orientation(-cfv.y,cfv.z,cfv.x, -cuv.y,cuv.z,cuv.x); i4_sound_man->set_listener_velocity(-cam_velocity.y,cam_velocity.z,cam_velocity.x); for (s=s1_sound_handle::first; s; s=s->next) s->dist_sqrd=get_dist_sqrd(s->x, s->y, s->z); // free any sounds that are done for (i=0; i poll()) channels[i].free(); // is this a looping 3d sound? else if (channels[i].loop_handler) { // see if it has gone out of hearing range s1_sound_handle *h=channels[i].loop_handler; float dist=h->dist_sqrd; if (dist>h->originator->hearable_distance_sqrd) channels[i].free(); else if (show_sfx) { i4_warning("looping %s (dist=%f)", channels[i].originator->name, sqrt(dist)); } } else if (!channels[i].stream && !channels[i].voice->is_playing()) channels[i].free(); else if (show_sfx) i4_warning("non-looping %s (pos=%d)", channels[i].originator->name, channels[i].voice->get_sound_position()); } } for (s=s1_sound_handle::first; s; s=s->next) { // find out if this sound is in hearing range float dist_sqrd=s->dist_sqrd; if (dist_sqrd originator->hearable_distance_sqrd && s->channel_on==-1) { s1_sfx_ref *ref=s->originator; int c=find_channel(ref, s); if (c!=-1 && ref->base_sfx) { i4_voice_class *v = i4_sound_man->duplicate_3d(ref->base_sfx); if (v) { v->set_looping(i4_T); channels[c].voice=v; channels[c].loop_handler=s; channels[c].originator=s->originator; s->channel_on=c; v->set_3d_position(-s->y, s->z, s->x, i4_T); v->set_3d_velocity(-s->yv, s->zv, s->xv, i4_T); v->set_frequency( i4_f_to_i(v->default_frequency * s->frequency_scale)); v->play(); } else channels[c].free(); } } } // now update any 3d sounds & velocities we are playing for (i=0; i set_3d_position(-s->y, s->z, s->x, i4_T); v->set_3d_velocity(-s->yv, s->zv, s->xv, i4_T); v->set_frequency( i4_f_to_i(v->default_frequency * s->frequency_scale)); } } } i4_sound_man->commit_3d_changes(); pf_sfx_update.stop(); } void s1_sfx_ref::play_looping(s1_sound_handle &handle) { if (handle.playing) return; // add the sound to the list of looping 3d sounds // we will determine which ones are heard in set_camera_pos handle.originator=this; handle.next=s1_sound_handle::first; s1_sound_handle::first=&handle; handle.playing=i4_T; } void s1_end_looping(s1_sound_handle &handle) { if (!handle.playing) return; handle.playing=i4_F; int i=handle.channel_on; // if the sound was playing, stop it if (i!=-1) channels[i].free(); // remove the sound from the list of 3d sounds if (&handle==s1_sound_handle::first) s1_sound_handle::first=s1_sound_handle::first->next; else { s1_sound_handle *last=0, *p=s1_sound_handle::first; for (;p;) { if (p==&handle) { last->next=handle.next; return; } last=p; p=p->next; } i4_error("end_looping not in list"); } } void s1_sfx_ref::play(float x, float y, float z) { int i; if (!base_sfx && ((flags&S1_STREAMED)==0)) return ; // sound is too far away if ((flags&S1_3D) && get_dist_sqrd(x,y,z)>hearable_distance_sqrd) return ; int use=find_channel(this, 0); if (use!=-1) { int fail=0; i4_voice_class *s=0; if (flags&S1_STREAMED) { char fn[100]; if (i4_sound_man==&i4_null_sound) fail=1; else { i4_file_class *fp=i4_open(add_sfx_path(name,fn)); if (fp) { i4_stream_wav_player *stream; stream=new i4_stream_wav_player(fp, 64*1024, i4_F, i4_T); if (stream->load_failed()) { i4_warning("stream load failed"); delete stream; fail=1; } else { channels[use].stream=stream; s=stream->i4_voice(); } } else fail=1; } } else s=i4_sound_man->duplicate_3d(base_sfx); if (!fail && s) { if (flags & S1_3D) s->set_3d_position(-y,z,x,i4_T); if ((flags &S1_STREAMED)==0) s->play(); channels[use].voice=s; channels[use].originator=this; channels[use].loop_handler=0; } else channels[use].free(); } } void s1_save_sfx_list(i4_file_class *fp) { if (fp) { for (s1_sfx_ref *s=s1_sfx_ref::first; s; s=s->next) { fp->printf("filename '%s', priority %d, defined at %s:%d ", s->name, s->priority, s->file_defined_in, s->line_defined_on); if (s->flags & S1_3D) fp->printf("3D "); if (s->flags & S1_STREAMED) fp->printf("Streamed "); fp->printf("\n"); } } }