1 #include "sound.h"
2 
3 #if ENABLE_SOUND
4   #define FMOD_DYN_IMPL
5   #define FMOD_DYN_NOASSERT
6 
7   #include "cake.h"
8   #include "vars.h"
9   #include "commands.h"
10   #include "console.h"
11   #include "system.h"
12   #include "files.h"
13   #include "types.h"
14   #include "math.h"
15   #include "timer.h"
16 
17   #if defined(WIN32) || defined(__WATCOMC__)
18     #include <conio.h>
19     #include <windows.h>
20     #pragma comment(lib, "cake/fmod/lib/fmodvc.lib")
21     #pragma warning(disable : 4311) /* pointer trunctation from 'FARPROC' to 'unsigned int' */
22   #elif defined(__linux__)
23     #include "fmod/wincompat.h"
24   #endif
25 
26   #include "fmod/fmod.h"
27   #include "fmod/fmoddyn.h"
28   #include "fmod/fmod_errors.h"
29 
30   int musicvolume = 64;
31   int soundvolume = 128;
32   int mixrate = 22050;
33   #define MAX_SOFTWARE_CHANNELS 256   // max is 1024
34   #define BGMUSIC_CHANNELS    2
35   #define SOUND_CHANNELS      (MAX_SOFTWARE_CHANNELS-BGMUSIC_CHANNELS)
36   #define MAX_SOUNDNAME_LENGTH  256
37 
38   typedef struct
39   {
40     FSOUND_SAMPLE *sample;
41     int channel;
42     bool loop;
43     char name[MAX_SOUNDNAME_LENGTH];
44     bool free;  // sound is free
45   } sound_t;
46 
47   sound_t     bgmusic[BGMUSIC_CHANNELS];
48   bool      playing_intro = false;
49   sound_t     sounds[SOUND_CHANNELS];
50 
51   Var snd_mindistance("snd_mindistance", 64.f);
52   Var snd_maxdistance("snd_maxdistance", 16384.f);
53 
cmd_musicvolume(int argc,char * argv[])54   void cmd_musicvolume(int argc, char *argv[])
55   {
56     if (argc > 1)
57     {
58       musicvolume = atol(argv[1]);
59       if (musicvolume < 0) musicvolume = 0;
60       if (musicvolume > 255) musicvolume = 255;
61       if (bgmusic[0].channel != -1) FSOUND_SetVolume(bgmusic[0].channel, musicvolume);
62       if (bgmusic[1].channel != -1) FSOUND_SetVolume(bgmusic[1].channel, musicvolume);
63     }
64     else
65     {
66       gConsole->Insertln("usage: %s <value>", argv[0]);
67     }
68     gConsole->Insertln("current music volume is %d", musicvolume);
69   }
70 
cmd_soundvolume(int argc,char * argv[])71   void cmd_soundvolume(int argc, char *argv[])
72   {
73     if (argc > 1)
74     {
75       soundvolume = atol(argv[1]);
76       if (soundvolume < 0) soundvolume = 0;
77       if (soundvolume > 255) soundvolume = 255;
78       for (int i = 0; i < SOUND_CHANNELS; ++i)
79         if (sounds[i].channel != -1) FSOUND_SetVolume(sounds[i].channel, soundvolume);
80     }
81     else
82     {
83       gConsole->Insertln("usage: %s <value>", argv[0]);
84     }
85     gConsole->Insertln("current sound volume is %d", soundvolume);
86   }
87 
cmd_setbgmusic(int argc,char * argv[])88   void cmd_setbgmusic(int argc, char *argv[])
89   {
90     if (argc > 1)
91     {
92       char args[256] = { '\0' };
93       if (argc > 2) sprintf(args, "%s %s", argv[1], argv[2]);
94       else strcpy(args, argv[1]);
95 
96       setBGMusic(args);
97     }
98     else gConsole->Insertln("usage: %s <string> [<string>]", argv[0]);
99   }
100 
cmd_stopbgmusic(int argc,char * argv[])101   void cmd_stopbgmusic(int argc, char *argv[])
102   {
103     freeBGMusic();
104   }
105 
cmd_playsound(int argc,char * argv[])106   void cmd_playsound(int argc, char *argv[])
107   {
108     if (argc > 1)
109     {
110       if (argc > 4)
111       {
112         vec3_t pos;
113         pos[0] = (float) atof(argv[2]);
114         pos[1] = (float) atof(argv[3]);
115         pos[2] = (float) atof(argv[4]);
116         if (argc > 5 && atoi(argv[5])) loadSound(argv[1], pos, true, true, false);
117         else loadSound(argv[1], pos, false, true, false);
118       }
119       else
120       {
121         if (argc > 2 && atoi(argv[5])) loadSound(argv[1], NULL, true, true, false);
122         else loadSound(argv[1], NULL, false, true, false);
123       }
124     }
125     else gConsole->Insertln("usage: %s <string> [<value> <value> <value>] [<bool>]", argv[0]);
126   }
127 
cmd_snd_distancefactor(int argc,char * argv[])128   void cmd_snd_distancefactor(int argc, char *argv[])
129   {
130     if (argc > 1)
131     {
132       FSOUND_3D_SetDistanceFactor((float) atof(argv[1]));
133       FSOUND_Update();
134     }
135     else
136     {
137       gConsole->Insertln("usage: %s <value>", argv[0]);
138     }
139   }
140 #endif
141 
initSoundSystem(void)142 bool initSoundSystem(void)
143 {
144   #if !ENABLE_SOUND
145     return false;
146   #else
147     int i;
148 
149     gCommands->AddCommand("musicvolume", cmd_musicvolume, "Sets the music volume. The valid volume range is defined between 0 to 255.");
150     gCommands->AddCommand("setbgmusic", cmd_setbgmusic, "Set the current background music. If command has 2 parameters, first music is played once as intro and second is played in loop.");
151     gCommands->AddCommand("stopbgmusic", cmd_stopbgmusic, "Stops the current background music.");
152     gCommands->AddCommand("soundvolume", cmd_soundvolume, "Sets the sound volume. The valid volume range is defined between 0 to 255.");
153     gCommands->AddCommand("playsound", cmd_playsound, "Play a sound.");
154     gCommands->AddCommand("snd_distancefactor", cmd_snd_distancefactor, "Sets the 3d sound distance factor.");
155     gVars->RegisterVar(snd_mindistance);
156     gVars->RegisterVar(snd_maxdistance);
157 
158     gConsole->Insertln("<br/><b>^6----- Initializing sound system -------------------------------</b>");
159 
160     for (i = 0; i < BGMUSIC_CHANNELS; ++i)
161     {
162       bgmusic[i].channel = -1;
163       bgmusic[i].sample = NULL;
164       memset(bgmusic[i].name, '\0', MAX_SOUNDNAME_LENGTH*sizeof(char)); // not used for background music
165       bgmusic[i].loop = false;                      // not used for background music
166       bgmusic[i].free = true;
167     }
168 
169     for (i = 0; i < SOUND_CHANNELS; ++i)
170     {
171       sounds[i].channel = -1;
172       sounds[i].sample = NULL;
173       memset(sounds[i].name, '\0', MAX_SOUNDNAME_LENGTH*sizeof(char));
174       sounds[i].loop = false;
175       sounds[i].free = true;
176     }
177 
178     if (FSOUND_GetVersion() < FMOD_VERSION)
179       {
180           gConsole->Insertln("^1ERROR: You are using the wrong DLL version!");
181       gConsole->Insertln("^1You should be using FMOD %.02f", FMOD_VERSION);
182           return true;
183       }
184     else
185     {
186       #if defined(WIN32) || defined(__CYGWIN32__) || defined(__WATCOMC__)
187         FSOUND_SetOutput(FSOUND_OUTPUT_WINMM);
188         //FSOUND_SetOutput(FSOUND_OUTPUT_DSOUND);
189         //FSOUND_SetOutput(FSOUND_OUTPUT_ASIO);
190         //FSOUND_SetOutput(FSOUND_OUTPUT_A3D);
191       #elif defined(__linux__)
192         FSOUND_SetOutput(FSOUND_OUTPUT_OSS);
193         //FSOUND_SetOutput(FSOUND_OUTPUT_ESD);
194         //FSOUND_SetOutput(FSOUND_OUTPUT_ALSA);
195       #endif
196       //FSOUND_SetOutput(FSOUND_OUTPUT_NOSOUND);
197 
198       switch (FSOUND_GetOutput())
199       {
200         case FSOUND_OUTPUT_NOSOUND:   gConsole->Insertln("NoSound"); break;
201 
202         #if defined(WIN32) || defined(__CYGWIN32__)  || defined(__WATCOMC__)
203         case FSOUND_OUTPUT_WINMM:   gConsole->Insertln("Using Windows Multimedia Waveout output"); break;
204         case FSOUND_OUTPUT_DSOUND:  gConsole->Insertln("Using Direct Sound output"); break;
205         case FSOUND_OUTPUT_ASIO:    gConsole->Insertln("Using ASIO output"); break;
206         case FSOUND_OUTPUT_A3D:   gConsole->Insertln("Using A3D output"); break;
207 
208         #elif defined(__linux__)
209         case FSOUND_OUTPUT_OSS:     gConsole->Insertln("Using Open Sound System output"); break;
210         case FSOUND_OUTPUT_ESD:     gConsole->Insertln("Using Enlightment Sound Daemon output"); break;
211         case FSOUND_OUTPUT_ALSA:    gConsole->Insertln("Using Alsa output"); break;
212 
213         #endif
214         default:          gConsole->Insertln("^5WARNING: Not using any output !"); break;
215       }
216 
217       if (FSOUND_Init(mixrate, MAX_SOFTWARE_CHANNELS, 0))
218       {
219         gConsole->Insertln("fmod %.2f successfully loaded, sound enabled", FMOD_VERSION);
220         gConsole->Insertln("current settings:");
221         gConsole->Insertln("\tsound volume: %d", soundvolume);
222         gConsole->Insertln("\tmusic volume: %d", musicvolume);
223         gConsole->Insertln("\tmix rate: %d", mixrate);
224         gConsole->Insertln("\tmax software channels: %d", MAX_SOFTWARE_CHANNELS);
225 
226         gConsole->Insertln("sound driver list:");
227         for (i = 0; i < FSOUND_GetNumDrivers(); ++i)
228         {
229           gConsole->Insertln("\t%s", FSOUND_GetDriverName(i));    // print driver names
230           unsigned int caps = 0;
231 
232           FSOUND_GetDriverCaps(i, &caps);
233 
234           if (caps & FSOUND_CAPS_HARDWARE)
235             gConsole->Insertln("\t\t  * Driver supports hardware 3D sound");
236           if (caps & FSOUND_CAPS_EAX2)
237             gConsole->Insertln("\t\t  * Driver supports EAX 2 reverb");
238           if (caps & FSOUND_CAPS_EAX3)
239             gConsole->Insertln("\t\t  * Driver supports EAX 3 reverb");
240         }
241 
242         FSOUND_3D_SetDistanceFactor(64.f);
243         FSOUND_Update();
244 
245         return true;
246       }
247       else
248       {
249         gConsole->Insertln("^1ERROR: sound initialisation failed");
250         return false;
251       }
252     }
253   #endif
254 }
255 
shutdownSoundSystem(void)256 void shutdownSoundSystem(void)
257 {
258   #if !ENABLE_SOUND
259     return;
260   #else
261     freeBGMusic();
262     freeSound();
263 
264     FSOUND_StopSound(FSOUND_ALL);
265     FSOUND_Close();
266 
267     gCommands->RemoveCommand("musicvolume");
268     gCommands->RemoveCommand("setbgmusic");
269     gCommands->RemoveCommand("stopbgmusic");
270     gCommands->RemoveCommand("soundvolume");
271     gCommands->RemoveCommand("playsound");
272     gCommands->RemoveCommand("snd_distancefactor");
273     gVars->UnregisterVar(snd_mindistance);
274     gVars->UnregisterVar(snd_maxdistance);
275   #endif
276 }
277 
setBGMusic(const char * filename,bool start_paused)278 void setBGMusic(const char* filename, bool start_paused)
279 {
280   #if !ENABLE_SOUND
281     return;
282   #else
283     freeBGMusic();
284 
285     if (!filename || !strlen(filename)) return;
286 
287     int argc = GetNArgs(filename);
288     int errnum = 0;
289 
290     if (argc > 1)
291     {
292       int l = (int) strlen(filename);
293       char *tmp = new char[l+1];
294       if (!tmp) ThrowException(ALLOCATION_ERROR, "setBGMusic.tmp");
295 
296       memset(tmp, '\0', l+1);
297       GetArg(filename, 0, tmp);
298       VFile *file_intro = new VFile(tmp);
299       if (!file_intro) ThrowException(ALLOCATION_ERROR, "setBGMusic.file_intro");
300       if (!file_intro->mem)
301       {
302         gConsole->Insertln("^5WARNING: setBackgroundMusic() could not open file %s", file_intro->fname);
303         delete file_intro;
304         return;
305       }
306 
307       memset(tmp, '\0', l+1);
308       GetArg(filename, 1, tmp);
309       VFile *file_loop = new VFile(tmp);
310       if (!file_loop) ThrowException(ALLOCATION_ERROR, "setBGMusic.file_loop");
311       if (!file_loop->mem)
312       {
313         gConsole->Insertln("^5WARNING: setBackgroundMusic() could not open file %s", file_loop->fname);
314         delete file_loop;
315         return;
316       }
317 
318       bgmusic[0].sample = FSOUND_Sample_Load(FSOUND_FREE,
319                     (char*) file_loop->mem,
320                     FSOUND_LOADMEMORY|FSOUND_LOOP_NORMAL|FSOUND_2D,
321                     file_loop->size);
322       delete file_loop;
323 
324       if ((errnum = FSOUND_GetError()))
325       {
326         gConsole->Insertln("FSOUND_Sample_Load(%s): %s", filename, GetSoundErrorString(errnum));
327         return;
328       }
329 
330       bgmusic[1].sample = FSOUND_Sample_Load(FSOUND_FREE,
331                     (char*) file_intro->mem,
332                     FSOUND_LOADMEMORY|FSOUND_2D,
333                     file_intro->size);
334       delete file_intro;
335 
336       if ((errnum = FSOUND_GetError()))
337       {
338         gConsole->Insertln("FSOUND_Sample_Load(%s): %s", filename, GetSoundErrorString(errnum));
339         return;
340       }
341 
342 
343       if (bgmusic[1].sample)
344       {
345         bgmusic[1].channel = FSOUND_PlaySoundEx(FSOUND_FREE, bgmusic[1].sample, NULL, TRUE);
346 
347         if (bgmusic[1].channel == -1)
348         {
349           bgmusic[1].free = true;
350           gConsole->Insertln("^1setBGMusic()->FSOUND_PlaySoundEx() failed");
351           freeBGMusic(1);
352           return;
353         }
354         bgmusic[0].free = false;
355         bgmusic[1].free = false;
356         playing_intro = true;
357       }
358       else
359       {
360         gConsole->Insertln("^5WARNING: cannot play %s", filename);
361         gConsole->Insertln("^5FSOUND_Sample_Load() return NULL");
362         playing_intro = false;
363       }
364 
365       FSOUND_SetVolume(bgmusic[1].channel, musicvolume);
366       if (!start_paused) FSOUND_SetPaused(bgmusic[1].channel, FALSE);
367     }
368     else
369     {
370       VFile *file = new VFile(filename);
371       if (!file) ThrowException(ALLOCATION_ERROR, "setBGMusic.file");
372       if (!file->mem)
373       {
374         gConsole->Insertln("^5WARNING: setBackgroundMusic() could not open file %s", filename);
375         delete file;
376         return;
377       }
378 
379       bgmusic[0].sample = FSOUND_Sample_Load(0,
380                     (char*) file->mem,
381                     FSOUND_LOADMEMORY|FSOUND_LOOP_NORMAL|FSOUND_2D,
382                     file->size);
383       delete file;
384       if ((errnum = FSOUND_GetError()))
385       {
386         gConsole->Insertln("FSOUND_Sample_Load(%s): %s", filename, GetSoundErrorString(errnum));
387         return;
388       }
389 
390       if (bgmusic[0].sample)
391       {
392         bgmusic[0].channel = FSOUND_PlaySoundEx(FSOUND_FREE, bgmusic[0].sample, NULL, TRUE);
393 
394         if (bgmusic[0].channel == -1)
395         {
396           gConsole->Insertln("^1setBGMusic()->FSOUND_PlaySoundEx() failed");
397           freeBGMusic(0);
398           return;
399         }
400         bgmusic[0].free = false;
401       }
402       else
403       {
404         gConsole->Insertln("^5WARNING: cannot play %s", filename);
405         gConsole->Insertln("^5FSOUND_Sample_Load() return NULL");
406       }
407 
408       playing_intro = false;
409 
410       FSOUND_SetVolume(bgmusic[0].channel, musicvolume);
411       if (!start_paused) FSOUND_SetPaused(bgmusic[0].channel, FALSE);
412     }
413   #endif
414 }
415 
toggleBGMusicPause(void)416 void toggleBGMusicPause(void)
417 {
418   #if !ENABLE_SOUND
419     return;
420   #else
421     for (int i = 0; i < BGMUSIC_CHANNELS; ++i)
422     {
423       if (!bgmusic[i].free && bgmusic[i].sample)
424       {
425         if (FSOUND_GetPaused(bgmusic[i].channel))
426           FSOUND_SetPaused(bgmusic[i].channel, FALSE);
427         else
428           FSOUND_SetPaused(bgmusic[i].channel, TRUE);
429       }
430     }
431   #endif
432 }
433 
playSound(int num,float x,float y,float z,bool stop_if_playing)434 void playSound(int num, float x, float y, float z, bool stop_if_playing)
435 {
436   #if !ENABLE_SOUND
437     return;
438   #else
439     if (num < 0 || num >= SOUND_CHANNELS) return;
440     if (!sounds[num].sample || sounds[num].free) return;
441 
442     vec3_t pos;
443     VectorSet(pos, x, y, z);
444     playSound(num, pos, stop_if_playing);
445   #endif
446 }
447 
playSound(int num,vec3_t location,bool stop_if_playing)448 void playSound(int num, vec3_t location, bool stop_if_playing)
449 {
450   #if !ENABLE_SOUND
451     return;
452   #else
453     if (num < 0 || num >= SOUND_CHANNELS) return;
454     if (!sounds[num].sample || sounds[num].free) return;
455 
456     if (stop_if_playing && FSOUND_IsPlaying(sounds[num].channel)) return;
457 
458     sounds[num].channel = FSOUND_PlaySoundEx(FSOUND_FREE, sounds[num].sample, NULL, FALSE);
459 
460     if (sounds[num].channel == -1)
461     {
462       gConsole->Insertln("^5WARNING: playSound()->FSOUND_PlaySoundEx() failed");
463       return;
464     }
465 
466     if (location)
467     {
468       vec3_t pos;
469       pos[0] = location[0];
470       pos[1] = location[2];
471       pos[2] = location[1];
472       FSOUND_3D_SetAttributes(sounds[num].channel, pos, NULL);
473     }
474 
475     FSOUND_SetVolume(sounds[num].channel, soundvolume);
476   #endif
477 }
478 
loadSound(const char * filename,float x,float y,float z,bool loop,bool start_playing,bool start_paused)479 int loadSound(const char *filename, float x, float y, float z, bool loop, bool start_playing, bool start_paused)
480 {
481   #if !ENABLE_SOUND
482     return -1;
483   #else
484     if (!filename || !strlen(filename)) return -1;
485 
486     vec3_t pos;
487     VectorSet(pos, x, y, z);
488     return loadSound(filename, pos, loop, start_playing, start_paused);
489   #endif
490 }
491 
loadSound(const char * filename,vec3_t location,bool loop,bool start_playing,bool start_paused)492 int loadSound(const char *filename, vec3_t location, bool loop, bool start_playing, bool start_paused)
493 {
494   #if !ENABLE_SOUND
495     return -1;
496   #else
497     if (!filename || !strlen(filename)) return -1;
498 
499     // search for a free sound channel
500     int freesound;
501     for (freesound = 0; freesound < SOUND_CHANNELS; ++freesound)
502     {
503       if (sounds[freesound].free) break;
504     }
505 
506     if (freesound >= SOUND_CHANNELS)
507     {
508       gConsole->Insertln("^5WARNING: No more free sound.");
509       return -1;
510     }
511 
512     // sound is not already loaded, need to load it
513     VFile *file = new VFile(filename);
514     if (!file) ThrowException(ALLOCATION_ERROR, "loadSound.file");
515     if (!file->mem)
516     {
517       gConsole->Insertln("^5WARNING: loadSound() could not open file %s", filename);
518       delete file;
519       return -1;
520     }
521 
522     int errnum = 0, flags = 0;
523     if (loop) flags |= FSOUND_LOOP_NORMAL;
524 
525     if (location)
526     {
527       flags |= FSOUND_LOADMEMORY|FSOUND_HW3D;
528       sounds[freesound].sample = FSOUND_Sample_Load(FSOUND_FREE,
529                    (char*) file->mem,
530                    flags,
531                    file->size);
532       delete file;
533       if ((errnum = FSOUND_GetError()))
534       {
535         gConsole->Insertln("FSOUND_Sample_Load(%s): %s", filename, GetSoundErrorString(errnum));
536         return -1;
537       }
538 
539       FSOUND_Sample_SetMinMaxDistance(sounds[freesound].sample, snd_mindistance.fvalue, snd_maxdistance.fvalue);
540       if ((errnum = FSOUND_GetError()))
541       {
542         gConsole->Insertln("FSOUND_Sample_SetMinMaxDistance: %s", GetSoundErrorString(errnum));
543         return -1;
544       }
545     }
546     else
547     {
548       flags |= FSOUND_LOADMEMORY|FSOUND_2D;
549       sounds[freesound].sample = FSOUND_Sample_Load(FSOUND_FREE,
550                     (char*) file->mem,
551                     flags,
552                     file->size);
553       delete file;
554       if ((errnum = FSOUND_GetError()))
555       {
556         gConsole->Insertln("FSOUND_Sample_Load(%s): %s", filename, GetSoundErrorString(errnum));
557         return -1;
558       }
559     }
560 
561     if (sounds[freesound].sample)
562     {
563       memset(sounds[freesound].name, '\0', MAX_SOUNDNAME_LENGTH*sizeof(char));
564       strcpy(sounds[freesound].name, filename);
565       sounds[freesound].loop = loop;
566 
567       if (start_playing)
568       {
569         sounds[freesound].channel = FSOUND_PlaySoundEx(FSOUND_FREE, sounds[freesound].sample, NULL, TRUE);
570 
571         if (sounds[freesound].channel == -1)
572         {
573           gConsole->Insertln("^1playSound()->FSOUND_PlaySoundEx() failed");
574           return -1;
575         }
576 
577         if (location)
578         {
579           vec3_t pos;
580           pos[0] = location[0];
581           pos[1] = location[2];
582           pos[2] = location[1];
583           FSOUND_3D_SetAttributes(sounds[freesound].channel, pos, NULL);
584         }
585 
586         FSOUND_SetVolume(sounds[freesound].channel, soundvolume);
587         if (!start_paused) FSOUND_SetPaused(sounds[freesound].channel, FALSE);
588       }
589       sounds[freesound].free = false;
590     }
591     else
592     {
593       gConsole->Insertln("^5WARNING: cannot play %s", filename);
594       gConsole->Insertln("^5FSOUND_Sample_Load() return NULL");
595     }
596 
597     return freesound;
598   #endif
599 }
600 
load3DSound(const char * filename,vec3_t location,bool loop,bool start_playing,bool start_paused)601 int load3DSound(const char *filename, vec3_t location, bool loop, bool start_playing, bool start_paused)
602 {
603   #if !ENABLE_SOUND
604     return -1;
605   #else
606     if (!filename || !strlen(filename)) return -1;
607 
608     vec3_t pos;
609     if (!location) VectorClear(pos);
610     else VectorCopy(location, pos);
611     return loadSound(filename, pos, loop, start_playing, start_paused);
612   #endif
613 }
614 
toggleSoundPause(int num)615 void toggleSoundPause(int num)
616 {
617   #if !ENABLE_SOUND
618     return;
619   #else
620     if (num < 0 || num >= SOUND_CHANNELS)
621     {
622       for (int i = 0; i < SOUND_CHANNELS; ++i) toggleSoundPause(i);
623     }
624     else
625     {
626       if (!sounds[num].free && sounds[num].sample)
627       {
628         if (FSOUND_GetPaused(sounds[num].channel))
629           FSOUND_SetPaused(sounds[num].channel, FALSE);
630         else
631           FSOUND_SetPaused(sounds[num].channel, TRUE);
632       }
633     }
634   #endif
635 }
636 
SoundUpdate(vec3_t position,vec3_t forward)637 void SoundUpdate(vec3_t position, vec3_t forward)
638 {
639   #if !ENABLE_SOUND
640     return;
641   #else
642     // update background music
643     if (playing_intro)
644     {
645       if (!FSOUND_IsPlaying(bgmusic[1].channel))
646       {
647         freeBGMusic(1);
648         if (!bgmusic[0].free && bgmusic[0].sample)
649         {
650           bgmusic[0].channel = FSOUND_PlaySoundEx(FSOUND_FREE, bgmusic[0].sample, NULL, TRUE);
651 
652           if (bgmusic[0].channel == -1)
653           {
654             gConsole->Insertln("^1setBGMusic()->FSOUND_PlaySoundEx() failed");
655             freeBGMusic();
656             return;
657           }
658         }
659         else
660         {
661           gConsole->Insertln("^1FSOUND_Sample_Load() return NULL");
662           bgmusic[0].free = true;
663         }
664         FSOUND_SetVolume(bgmusic[0].channel, musicvolume);
665         FSOUND_SetPaused(bgmusic[0].channel, FALSE);
666 
667         playing_intro = false;
668       }
669     }
670 
671     // update listener location
672     vec3_t pos;
673     pos[0] = position[0];
674     pos[1] = position[2];
675     pos[2] = position[1];
676 
677     FSOUND_3D_Listener_SetAttributes(pos, NULL, forward[0], forward[2], forward[1], 0, 1, 0);
678     FSOUND_Update();
679   #endif
680 }
681 
freeBGMusic(int num)682 void freeBGMusic(int num)
683 {
684   #if !ENABLE_SOUND
685     return;
686   #else
687     if (num < 0 || num >= BGMUSIC_CHANNELS)
688     {
689       for (int i = 0; i < BGMUSIC_CHANNELS; ++i)
690         freeBGMusic(i);
691     }
692     else
693     {
694       int errnum = 0;
695       if (!bgmusic[num].free && bgmusic[num].sample)
696       {
697         if (FSOUND_IsPlaying(bgmusic[num].channel))
698           FSOUND_StopSound(bgmusic[num].channel);
699 
700         FSOUND_Sample_Free(bgmusic[num].sample);
701         if ((errnum = FSOUND_GetError()))
702         {
703           gConsole->Insertln("FSOUND_Sample_Free: %s", GetSoundErrorString(errnum));
704           return;
705         }
706 
707         bgmusic[num].channel = -1;
708         bgmusic[num].sample = NULL;
709         memset(bgmusic[num].name, '\0', MAX_SOUNDNAME_LENGTH*sizeof(char));
710         bgmusic[num].loop = false;
711         bgmusic[num].free = true;
712       }
713     }
714   #endif
715 }
716 
freeSound(int num)717 void freeSound(int num)
718 {
719   #if !ENABLE_SOUND
720     return;
721   #else
722     if (num < 0 || num >= SOUND_CHANNELS)
723     {
724       for (int i = 0; i < SOUND_CHANNELS; ++i) freeSound(i);
725     }
726     else
727     {
728       if (!sounds[num].free && sounds[num].sample)
729       {
730         int errnum = 0;
731 
732         if (sounds[num].channel >= 0 && FSOUND_IsPlaying(sounds[num].channel))
733           FSOUND_StopSound(sounds[num].channel);
734 
735         FSOUND_Sample_Free(sounds[num].sample);
736         if ((errnum = FSOUND_GetError()))
737         {
738           gConsole->Insertln("FSOUND_Sample_Free: %s", GetSoundErrorString(errnum));
739           return;
740         }
741 
742         sounds[num].channel = -1;
743         sounds[num].sample = NULL;
744         memset(sounds[num].name, '\0', MAX_SOUNDNAME_LENGTH*sizeof(char));
745         sounds[num].loop = false;
746         sounds[num].free = true;
747       }
748     }
749   #endif
750 }
751 
GetSoundErrorString(int errnum)752 const char* GetSoundErrorString(int errnum)
753 {
754   #if !ENABLE_SOUND
755     return NULL;
756   #else
757     switch (errnum)
758     {
759       case FMOD_ERR_BUSY:       return "^1Cannot call this command after FSOUND_Init. Call FSOUND_Close first.";
760       case FMOD_ERR_UNINITIALIZED:  return "^1This command failed because FSOUND_Init or FSOUND_SetOutput was not called";
761       case FMOD_ERR_INIT:       return "^1Error initializing output device.";
762       case FMOD_ERR_ALLOCATED:    return "^1Error initializing output device, but more specifically, the output device is already in use and cannot be reused.";
763       case FMOD_ERR_PLAY:       return "^1Playing the sound failed.";
764       case FMOD_ERR_OUTPUT_FORMAT:  return "^1Soundcard does not support the features needed for this soundsystem (16bit stereo output)";
765       case FMOD_ERR_COOPERATIVELEVEL: return "^1Error setting cooperative level for hardware.";
766       case FMOD_ERR_CREATEBUFFER:   return "^1Error creating hardware sound buffer.";
767       case FMOD_ERR_FILE_NOTFOUND:  return "^1File not found";
768       case FMOD_ERR_FILE_FORMAT:    return "^1Unknown file format";
769       case FMOD_ERR_FILE_BAD:     return "^1Error loading file";
770       case FMOD_ERR_MEMORY:     return "^1Not enough memory or resources";
771       case FMOD_ERR_VERSION:      return "^1The version number of this file format is not supported";
772       case FMOD_ERR_INVALID_PARAM:  return "^1An invalid parameter was passed to this function";
773       case FMOD_ERR_NO_EAX:     return "^1Tried to use an EAX command on a non EAX enabled channel or output.";
774       case FMOD_ERR_CHANNEL_ALLOC:  return "^1Failed to allocate a new channel";
775       case FMOD_ERR_RECORD:     return "^1Recording is not supported on this machine";
776       case FMOD_ERR_MEDIAPLAYER:    return "^1Windows Media Player not installed so cannot play wma or use internet streaming.";
777       case FMOD_ERR_CDDEVICE:     return "^1An error occured trying to open the specified CD device";
778       case FMOD_ERR_NONE:
779       default: return "^1No errors";
780     }
781   #endif
782 }