source: golgotha/src/i4/sound/sfx_id.cc @ 80

Last change on this file since 80 was 80, checked in by Sam Hocevar, 15 years ago
  • Adding the Golgotha source code. Not sure what's going to be interesting in there, but since it's all public domain, there's certainly stuff to pick up.
File size: 13.6 KB
Line 
1/********************************************************************** <BR>
2  This file is part of Crack dot Com's free source code release of
3  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
4  information about compiling & licensing issues visit this URL</a>
5  <PRE> If that doesn't help, contact Jonathan Clark at
6  golgotha_source@usa.net (Subject should have "GOLG" in it)
7***********************************************************************/
8
9#include "sound/sfx_id.hh"
10#include "string/string.hh"
11#include "file/file.hh"
12#include "error/error.hh"
13#include "loaders/wav_load.hh"
14#include "sound/sound.hh"
15#include "file/file.hh"
16#include "math/transform.hh"
17#include "time/profile.hh"
18#include "music/stream.hh"
19#include "main/main.hh"
20#include "app/registry.hh"
21#include <stdio.h>
22
23i4_profile_class pf_sfx_update("sfx_update");
24
25s1_sfx_ref *s1_sfx_ref::first=0;
26s1_sound_handle *s1_sound_handle::first=0;
27int show_sfx=0;
28
29
30static char sfx_path[80];
31
32
33static void get(char *var, char *buffer, int max_buffer, char *def)
34{
35  if (i4_get_registry(I4_REGISTRY_USER,
36                      "SOFTWARE\\Crack dot Com\\i4\\1.0",
37                      var, buffer, max_buffer))
38    return ;
39 
40  char *c=getenv(var);
41  if (c)
42  {
43    strncpy(buffer, c, max_buffer);
44    return ;
45  }
46 
47  strcpy(buffer, def);
48
49}
50
51
52char *s1_get_sfx_path()
53{
54  if (!sfx_path[0])
55  {
56    get("G_SFX_PATH", sfx_path, 80, "sfx");
57  }
58
59  return sfx_path;
60}
61
62
63static char *add_sfx_path(char *filename, char *buffer)
64{
65  sprintf(buffer, "%s/%s", s1_get_sfx_path(), filename);
66  return buffer;
67}
68
69s1_sound_handle::~s1_sound_handle()
70{
71  if (playing)
72    s1_end_looping(*this);
73}
74
75s1_sound_handle::s1_sound_handle()
76{
77  originator=0;
78  next=0;
79  channel_on=-1;
80  playing=i4_F;
81}
82
83static i4_voice_class *load_sfx(s1_sfx_ref &ref)
84{
85  char buf[200];
86
87  // don't try if no sound
88  if (i4_sound_man==&i4_null_sound)
89    return 0;
90
91  i4_file_class *fp=i4_open(add_sfx_path(ref.name, buf));
92  if (!fp)
93  {
94    i4_warning("sfx missing %s (%s:%d)\n", ref.name,
95               ref.file_defined_in, ref.line_defined_on);
96    return 0;
97  }
98
99  i4_sound_info info;
100  i4_load_wav_info(fp, info);
101
102  if (info.channels==2)
103    i4_warning("%s is stereo sound (waste of memory)\n", ref.name);
104
105  if (info.sample_rate>22*1024)
106    i4_warning("%s is %dHz (waste of memory)\n", ref.name, info.sample_rate);
107  else if (show_sfx)
108    i4_warning("%s is %dHz\n", ref.name, info.sample_rate);
109
110
111  i4_sound_manager_class::sound_parameters p;
112  p.channels=info.channels;
113  p.sample_size=info.sample_size;
114  p.frequency=info.sample_rate;
115
116  if (ref.flags & S1_3D)
117    p.capable_3d=i4_T;
118     
119  i4_voice_class *v = i4_sound_man->alloc(info.size, p);
120  if (v)
121  {
122    void *b1,*b2;
123    w32   s1,s2;
124
125    v->lock(0, info.size, b1, s1, b2, s2);
126    fp->read(b1,s1);         
127    v->unlock(b1, s1, b2, s2);
128  }
129  delete fp;
130
131  return v;
132}
133
134
135
136s1_sfx_ref::s1_sfx_ref(char *filename, w8 flags, w8 priority, float dist, char *file, int line)
137  : flags(flags),
138    name(filename),
139    file_defined_in(file),
140    line_defined_on(line),
141    priority(priority),
142    hearable_distance_sqrd(dist*dist)
143   
144
145  next=first;
146  first=this;
147  id=0;
148  base_sfx=0;
149}
150
151
152s1_sfx_ref::~s1_sfx_ref()
153
154  if (first==this)
155    first=first->next;
156  else
157  {
158    s1_sfx_ref *last=0, *p=first;
159    for (;p!=this && p;)
160    {
161      last=p;
162      p=p->next;
163    }
164    if (!p)
165      i4_error("~sfx not found");
166    last->next=next;
167  }
168}
169
170
171
172
173struct channel_info
174{
175  i4_stream_wav_player *stream;
176  i4_voice_class       *voice;
177  s1_sfx_ref           *originator;
178  s1_sound_handle      *loop_handler;
179
180  void free()
181  {
182    if (stream)
183    {
184      delete stream;
185      stream=0;
186      voice=0;
187    }
188    else if (voice)
189    {   
190      delete voice;
191      voice=0;
192    }
193
194    if (loop_handler)
195    {
196      loop_handler->channel_on=-1;
197      loop_handler=0;   
198    }
199
200  }
201
202};
203
204enum { T_CHANNELS=8 };
205static channel_info channels[T_CHANNELS];
206static i4_3d_vector camera_pos;
207static int channels_sorted=0;
208
209void s1_get_camera_pos(i4_3d_vector &pos)
210{
211  pos=camera_pos;
212}
213
214
215void s1_load()      // load all the referenced static & dynamic sounds
216{
217  for (int j=1; j<i4_global_argc; j++)
218    if (i4_global_argv[j]=="-sfxdbg")
219      show_sfx=1;
220
221  for (s1_sfx_ref *s=s1_sfx_ref::first; s; s=s->next)
222    if ((s->flags&S1_STREAMED)==0 && !s->base_sfx)
223      s->base_sfx=load_sfx(*s);
224
225  for (int i=0; i<T_CHANNELS; i++)
226  {
227    channels[i].voice=0;
228    channels[i].stream=0;
229    channels[i].loop_handler=0;
230    channels[i].originator=0;
231   
232  }
233}
234
235
236void s1_unload()
237{
238  for (s1_sfx_ref *s=s1_sfx_ref::first; s; s=s->next)
239    if (s->base_sfx)
240    {
241      delete s->base_sfx;
242      s->base_sfx=0;
243    }
244}
245
246
247static float get_dist_sqrd(float x, float y, float z)
248{
249  return (camera_pos.x-x)*(camera_pos.x-x)+
250         (camera_pos.y-y)*(camera_pos.y-y)+
251         (camera_pos.z-z)*(camera_pos.z-z);
252}
253
254int channel_compare(const void *va, const void *vb)
255{
256  const channel_info *a=(channel_info *)va;
257  const channel_info *b=(channel_info *)vb;
258
259  if (a->originator->priority>b->originator->priority)
260    return 1;
261  else if (a->originator->priority<b->originator->priority)
262    return -1;
263
264  // non-looping sounds have priority over looping
265  else if (a->loop_handler && !b->loop_handler)
266    return -1;
267  else if (!a->loop_handler && b->loop_handler)
268    return 1;
269  // compare the distance for 2 looping sounds
270  else if (a->loop_handler && b->loop_handler)
271  {
272    float d1=a->loop_handler->dist_sqrd;
273    float d2=b->loop_handler->dist_sqrd;
274    if (d1>d2)
275      return 1;
276    else if (d1<d2)
277      return -1;
278    else return 0;
279  }
280  // compare the offset for non-looping sounds
281  else
282  {
283    int o1=a->voice->get_sound_position();
284    int o2=b->voice->get_sound_position();
285    if (o1<o2) return -1;
286    else if (o1>o2) return 1;
287    else return 0;
288  }
289}
290
291int find_channel(s1_sfx_ref *ref, s1_sound_handle *h)
292{
293  int use=-1,i;
294  for (i=0; i<T_CHANNELS; i++)
295    if (!channels[i].voice)
296    {
297      channels_sorted=0;
298      return i;
299    }
300
301
302
303  if (!channels_sorted)
304  {
305    qsort(channels, T_CHANNELS, sizeof(channel_info), channel_compare);
306    channels_sorted=1;
307  }
308
309  // the worst channel after sorting
310  channel_info *w=channels+T_CHANNELS-1;
311
312  if (ref->priority<w->originator->priority)
313    use=T_CHANNELS-1;
314  else if (ref->priority==w->originator->priority) // we are not better
315  {
316    if (w->loop_handler && !h)
317      use=T_CHANNELS-1;
318    else if (w->loop_handler && h)
319    {
320      float d1=w->loop_handler->dist_sqrd;
321      float d2=h->dist_sqrd;
322      if (d1>d2)
323        use=T_CHANNELS-1;
324    }
325    else if (!h)
326      use=T_CHANNELS-1;
327  }
328
329 
330  if (use!=-1)
331  {
332    channels_sorted=0;
333    channels[use].free();
334
335//     i4_warning("throwing out %s (priority %d) for %s (p=%d)",
336//                channels[use].originator->name,
337//                channels[use].originator->priority,
338//                ref->name, ref->priority);
339  }
340//    else
341//      i4_warning("no channel for %s (priority %d)",
342//                 ref->name, ref->priority);
343
344
345  return use;
346}
347
348
349void s1_set_camera_pos(i4_transform_class &new_cam_transform)
350{
351  int i=0;
352
353  pf_sfx_update.start();
354
355  i4_3d_vector last_pos=camera_pos;
356  new_cam_transform.inverse_transform(i4_3d_vector(0,0,0), camera_pos);
357  s1_sound_handle *s;
358
359 
360  i4_3d_point_class temp,cfv,cuv; 
361  i4_3d_vector cam_velocity;
362  cam_velocity = i4_3d_vector(camera_pos.x-last_pos.x,
363                              camera_pos.y-last_pos.y,
364                              camera_pos.z-last_pos.z);
365
366  i4_sound_man->set_listener_position(-camera_pos.y, camera_pos.z, camera_pos.x);
367 
368  new_cam_transform.inverse_transform_3x3(i4_3d_vector(0,0,1),cfv);
369  new_cam_transform.inverse_transform_3x3(i4_3d_vector(0,-1,0),cuv); //negative y in viewspace is actually "up"
370
371  //just in case they dont come out exactly correct
372  cfv.normalize();
373  cuv.normalize();
374
375  i4_sound_man->set_listener_orientation(-cfv.y,cfv.z,cfv.x,    -cuv.y,cuv.z,cuv.x);
376
377  i4_sound_man->set_listener_velocity(-cam_velocity.y,cam_velocity.z,cam_velocity.x);
378
379
380
381  for (s=s1_sound_handle::first; s; s=s->next)
382    s->dist_sqrd=get_dist_sqrd(s->x, s->y, s->z);
383
384
385
386  // free any sounds that are done
387  for (i=0; i<T_CHANNELS; i++)
388  {
389    if (channels[i].voice)
390    {
391      // streamed sound?
392      if (channels[i].stream && !channels[i].stream->poll())
393        channels[i].free();
394      // is this a looping 3d sound?   
395      else if (channels[i].loop_handler)
396      {
397        // see if it has gone out of hearing range
398        s1_sound_handle *h=channels[i].loop_handler;
399        float dist=h->dist_sqrd;
400
401        if (dist>h->originator->hearable_distance_sqrd)
402          channels[i].free();
403        else if (show_sfx)
404        {
405          i4_warning("looping %s (dist=%f)",
406                     channels[i].originator->name,
407                     sqrt(dist));
408        }
409      }
410      else if (!channels[i].stream && !channels[i].voice->is_playing())
411        channels[i].free();
412      else if (show_sfx)
413        i4_warning("non-looping %s (pos=%d)",
414                   channels[i].originator->name,
415                   channels[i].voice->get_sound_position());
416    }
417  }
418
419
420  for (s=s1_sound_handle::first; s; s=s->next)
421  {
422    // find out if this sound is in hearing range
423    float dist_sqrd=s->dist_sqrd;
424    if (dist_sqrd<s->originator->hearable_distance_sqrd && s->channel_on==-1)
425    {
426      s1_sfx_ref *ref=s->originator;
427
428      int c=find_channel(ref, s);
429      if (c!=-1 && ref->base_sfx)
430      {
431        i4_voice_class *v = i4_sound_man->duplicate_3d(ref->base_sfx);
432        if (v)
433        {
434          v->set_looping(i4_T);
435          channels[c].voice=v;
436          channels[c].loop_handler=s;
437          channels[c].originator=s->originator;       
438          s->channel_on=c;
439
440          v->set_3d_position(-s->y, s->z, s->x, i4_T);
441          v->set_3d_velocity(-s->yv, s->zv, s->xv,  i4_T);
442          v->set_frequency( i4_f_to_i(v->default_frequency * s->frequency_scale));
443
444          v->play();
445        }
446        else
447          channels[c].free();
448
449      }
450    }
451
452  }
453
454  // now update any 3d sounds & velocities we are playing
455  for (i=0; i<T_CHANNELS; i++)
456  {
457    i4_voice_class *v=channels[i].voice;
458    if (v)
459    {
460      s1_sound_handle *s=channels[i].loop_handler;
461      if (s)
462      {
463        v->set_3d_position(-s->y, s->z, s->x,  i4_T);
464        v->set_3d_velocity(-s->yv, s->zv,  s->xv, i4_T);
465        v->set_frequency( i4_f_to_i(v->default_frequency * s->frequency_scale));
466      }
467    }
468  }
469
470  i4_sound_man->commit_3d_changes();
471
472  pf_sfx_update.stop(); 
473}
474
475
476void s1_sfx_ref::play_looping(s1_sound_handle &handle)
477{
478  if (handle.playing)
479    return;
480
481  // add the sound to the list of looping 3d sounds
482  // we will determine which ones are heard in set_camera_pos
483  handle.originator=this;
484  handle.next=s1_sound_handle::first;
485  s1_sound_handle::first=&handle;
486  handle.playing=i4_T;
487}
488
489
490void s1_end_looping(s1_sound_handle &handle)
491{
492  if (!handle.playing)
493    return;
494
495  handle.playing=i4_F;
496  int i=handle.channel_on;
497
498  // if the sound was playing, stop it
499  if (i!=-1)
500    channels[i].free();
501
502  // remove the sound from the list of 3d sounds
503  if (&handle==s1_sound_handle::first)
504    s1_sound_handle::first=s1_sound_handle::first->next;
505  else
506  {
507    s1_sound_handle *last=0, *p=s1_sound_handle::first;
508    for (;p;)
509    {
510      if (p==&handle)
511      {
512        last->next=handle.next;
513        return;
514      }
515      last=p;
516      p=p->next;
517    }
518    i4_error("end_looping not in list");
519  }
520
521}
522
523
524void s1_sfx_ref::play(float x, float y, float z)
525{
526  int i;
527  if (!base_sfx && ((flags&S1_STREAMED)==0))
528    return ;
529
530  // sound is too far away
531  if ((flags&S1_3D) && get_dist_sqrd(x,y,z)>hearable_distance_sqrd)
532    return ;
533   
534  int use=find_channel(this, 0);
535
536  if (use!=-1)
537  {
538    int fail=0;
539    i4_voice_class *s=0;
540
541
542    if (flags&S1_STREAMED)
543    {
544      char fn[100];     
545      if (i4_sound_man==&i4_null_sound)
546        fail=1;
547      else
548      {
549        i4_file_class *fp=i4_open(add_sfx_path(name,fn));
550        if (fp)
551        {   
552          i4_stream_wav_player *stream;
553          stream=new i4_stream_wav_player(fp, 64*1024, i4_F, i4_T);
554          if (stream->load_failed())
555          {
556            i4_warning("stream load failed");
557            delete stream;
558            fail=1;
559          }
560          else
561          {
562            channels[use].stream=stream;
563            s=stream->i4_voice();
564          }
565        }
566        else
567          fail=1;
568      }
569    }
570    else
571      s=i4_sound_man->duplicate_3d(base_sfx);
572
573     
574
575    if (!fail && s)
576    {
577      if (flags & S1_3D)
578        s->set_3d_position(-y,z,x,i4_T);
579
580      if ((flags &S1_STREAMED)==0)
581        s->play();
582
583      channels[use].voice=s;
584      channels[use].originator=this;
585      channels[use].loop_handler=0;
586    }
587    else
588      channels[use].free();
589
590  }
591}
592
593
594void s1_save_sfx_list(i4_file_class *fp)
595{
596
597  if (fp)
598  {
599    for (s1_sfx_ref *s=s1_sfx_ref::first; s; s=s->next)
600    {
601      fp->printf("filename '%s', priority %d, defined at %s:%d ",
602                 s->name, s->priority, s->file_defined_in, s->line_defined_on);
603     
604      if (s->flags & S1_3D)
605        fp->printf("3D ");
606
607      if (s->flags & S1_STREAMED)
608        fp->printf("Streamed ");
609
610      fp->printf("\n");
611    }
612  }
613}
Note: See TracBrowser for help on using the repository browser.