added boundary checks for sound/music ID for some sound and music functions
[rocksndiamonds.git] / src / libgame / sound.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // sound.c
10 // ============================================================================
11
12 #include <sys/types.h>
13 #include <sys/time.h>
14 #include <string.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <dirent.h>
18 #include <signal.h>
19 #include <math.h>
20 #include <errno.h>
21
22 #include "platform.h"
23 #include "system.h"
24 #include "sound.h"
25 #include "misc.h"
26 #include "setup.h"
27 #include "text.h"
28
29
30 /* expiration time (in milliseconds) for sound loops */
31 #define SOUND_LOOP_EXPIRATION_TIME      200
32
33 /* one second fading interval == 1000 ticks (milliseconds) */
34 #define SOUND_FADING_INTERVAL           1000
35
36 #define SND_TYPE_NONE                   0
37 #define SND_TYPE_WAV                    1
38
39 #define MUS_TYPE_NONE                   0
40 #define MUS_TYPE_WAV                    1
41 #define MUS_TYPE_MOD                    2
42
43 #define DEVICENAME_DSP                  "/dev/dsp"
44 #define DEVICENAME_SOUND_DSP            "/dev/sound/dsp"
45 #define DEVICENAME_AUDIO                "/dev/audio"
46 #define DEVICENAME_AUDIOCTL             "/dev/audioCtl"
47
48 #define SOUND_VOLUME_LEFT(x)            (stereo_volume[x])
49 #define SOUND_VOLUME_RIGHT(x)           (stereo_volume[SOUND_MAX_LEFT2RIGHT-x])
50
51 #define SAME_SOUND_NR(x,y)              ((x).nr == (y).nr)
52 #define SAME_SOUND_DATA(x,y)            ((x).data_ptr == (y).data_ptr)
53
54 #define SOUND_VOLUME_FROM_PERCENT(v,p)  ((p) < 0   ? SOUND_MIN_VOLUME : \
55                                          (p) > 100 ? (v) :              \
56                                          (p) * (v) / 100)
57
58 #define SOUND_VOLUME_SIMPLE(v) SOUND_VOLUME_FROM_PERCENT(v, setup.volume_simple)
59 #define SOUND_VOLUME_LOOPS(v)  SOUND_VOLUME_FROM_PERCENT(v, setup.volume_loops)
60 #define SOUND_VOLUME_MUSIC(v)  SOUND_VOLUME_FROM_PERCENT(v, setup.volume_music)
61
62 #define SETUP_SOUND_VOLUME(v,s)         ((s) == SND_CTRL_PLAY_MUSIC ?   \
63                                          SOUND_VOLUME_MUSIC(v) :        \
64                                          (s) == SND_CTRL_PLAY_LOOP ?    \
65                                          SOUND_VOLUME_LOOPS(v) :        \
66                                          SOUND_VOLUME_SIMPLE(v))
67
68 struct AudioFormatInfo
69 {
70   boolean stereo;               /* availability of stereo sound */
71   int format;                   /* size and endianess of sample data */
72   int sample_rate;              /* sample frequency */
73   int fragment_size;            /* audio device fragment size in bytes */
74 };
75
76 struct SampleInfo
77 {
78   char *source_filename;
79   int num_references;
80
81   int type;
82   int format;
83   void *data_ptr;               /* pointer to first sample (8 or 16 bit) */
84   int data_len;                 /* number of samples, NOT number of bytes */
85   int num_channels;             /* mono: 1 channel, stereo: 2 channels */
86 };
87 typedef struct SampleInfo SoundInfo;
88 typedef struct SampleInfo MusicInfo;
89
90 struct SoundControl
91 {
92   boolean active;
93
94   int nr;
95   int volume;
96   int stereo_position;
97
98   int state;
99
100   unsigned int playing_starttime;
101   unsigned int playing_pos;
102
103   int type;
104   int format;
105   void *data_ptr;               /* pointer to first sample (8 or 16 bit) */
106   int data_len;         /* number of samples, NOT number of bytes */
107   int num_channels;             /* mono: 1 channel, stereo: 2 channels */
108 };
109 typedef struct SoundControl SoundControl;
110
111 static struct ArtworkListInfo *sound_info = NULL;
112 static struct ArtworkListInfo *music_info = NULL;
113
114 static MusicInfo **Music_NoConf = NULL;
115
116 static int num_music_noconf = 0;
117 static int stereo_volume[SOUND_MAX_LEFT2RIGHT + 1];
118
119 static char *currently_playing_music_filename = NULL;
120
121
122 /* ========================================================================= */
123 /* THE STUFF BELOW IS ONLY USED BY THE SOUND SERVER CHILD PROCESS            */
124
125 static struct SoundControl mixer[NUM_MIXER_CHANNELS];
126 static int mixer_active_channels = 0;
127 static boolean expire_loop_sounds = FALSE;
128
129 static void ReloadCustomSounds();
130 static void ReloadCustomMusic();
131 static void FreeSound(void *);
132 static void FreeMusic(void *);
133 static void FreeAllMusic_NoConf();
134
135 static SoundInfo *getSoundInfoEntryFromSoundID(int);
136 static MusicInfo *getMusicInfoEntryFromMusicID(int);
137
138
139 /* ------------------------------------------------------------------------- */
140 /* mixer functions                                                           */
141 /* ------------------------------------------------------------------------- */
142
143 void Mixer_InitChannels()
144 {
145   int i;
146
147   for (i = 0; i < audio.num_channels; i++)
148     mixer[i].active = FALSE;
149   mixer_active_channels = 0;
150 }
151
152 static void Mixer_ResetChannelExpiration(int channel)
153 {
154   mixer[channel].playing_starttime = Counter();
155
156   if (expire_loop_sounds &&
157       IS_LOOP(mixer[channel]) && !IS_MUSIC(mixer[channel]))
158     Mix_ExpireChannel(channel, SOUND_LOOP_EXPIRATION_TIME);
159 }
160
161 static boolean Mixer_ChannelExpired(int channel)
162 {
163   if (!mixer[channel].active)
164     return TRUE;
165
166   if (expire_loop_sounds &&
167       IS_LOOP(mixer[channel]) && !IS_MUSIC(mixer[channel]) &&
168       DelayReached(&mixer[channel].playing_starttime,
169                    SOUND_LOOP_EXPIRATION_TIME))
170     return TRUE;
171
172   if (!Mix_Playing(channel))
173     return TRUE;
174
175   return FALSE;
176 }
177
178 static boolean Mixer_AllocateChannel(int channel)
179 {
180   return TRUE;
181 }
182
183 static void Mixer_SetChannelProperties(int channel)
184 {
185   Mix_Volume(channel, mixer[channel].volume);
186   Mix_SetPanning(channel,
187                  SOUND_VOLUME_LEFT(mixer[channel].stereo_position),
188                  SOUND_VOLUME_RIGHT(mixer[channel].stereo_position));
189 }
190
191 static void Mixer_StartChannel(int channel)
192 {
193   Mix_PlayChannel(channel, mixer[channel].data_ptr,
194                   IS_LOOP(mixer[channel]) ? -1 : 0);
195 }
196
197 static void Mixer_PlayChannel(int channel)
198 {
199   /* start with inactive channel in case something goes wrong */
200   mixer[channel].active = FALSE;
201
202   if (mixer[channel].type != MUS_TYPE_WAV)
203     return;
204
205   if (!Mixer_AllocateChannel(channel))
206     return;
207
208   Mixer_SetChannelProperties(channel);
209   Mixer_StartChannel(channel);
210
211   Mixer_ResetChannelExpiration(channel);
212
213   mixer[channel].playing_pos = 0;
214   mixer[channel].active = TRUE;
215   mixer_active_channels++;
216 }
217
218 static void Mixer_PlayMusicChannel()
219 {
220   Mixer_PlayChannel(audio.music_channel);
221
222   if (mixer[audio.music_channel].type != MUS_TYPE_WAV)
223   {
224     // use short fade-in to prevent "plop" sound for certain music files
225     // (this may happen when switching on music while playing the game)
226     Mix_VolumeMusic(mixer[audio.music_channel].volume);
227     Mix_FadeInMusic(mixer[audio.music_channel].data_ptr, -1, 100);
228
229 #if defined(PLATFORM_WIN32)
230     // playing MIDI music is broken since Windows Vista, as it sets the volume
231     // for MIDI music also for all other sounds and music, which cannot be set
232     // back to normal unless playing MIDI music again with that desired volume
233     // (more details: https://www.artsoft.org/forum/viewtopic.php?f=7&t=2253)
234     // => workaround: always play MIDI music with maximum volume
235     if (Mix_GetMusicType(NULL) == MUS_MID)
236       Mix_VolumeMusic(SOUND_MAX_VOLUME);
237 #endif
238   }
239 }
240
241 static void Mixer_StopChannel(int channel)
242 {
243   if (!mixer[channel].active)
244     return;
245
246   Mix_HaltChannel(channel);
247
248   mixer[channel].active = FALSE;
249   mixer_active_channels--;
250 }
251
252 static void Mixer_StopMusicChannel()
253 {
254   Mixer_StopChannel(audio.music_channel);
255
256   Mix_HaltMusic();
257
258   setString(&currently_playing_music_filename, NULL);
259 }
260
261 static void Mixer_FadeChannel(int channel)
262 {
263   if (!mixer[channel].active)
264     return;
265
266   mixer[channel].state |= SND_CTRL_FADE;
267
268   Mix_FadeOutChannel(channel, SOUND_FADING_INTERVAL);
269 }
270
271 static void Mixer_FadeMusicChannel()
272 {
273   Mixer_FadeChannel(audio.music_channel);
274
275   Mix_FadeOutMusic(SOUND_FADING_INTERVAL);
276
277 #if defined(PLATFORM_WIN32)
278   // playing MIDI music is broken since Windows Vista, as it sets the volume
279   // for MIDI music also for all other sounds and music, which cannot be set
280   // back to normal unless playing MIDI music again with that desired volume
281   // (more details: https://www.artsoft.org/forum/viewtopic.php?f=7&t=2253)
282   // => workaround: never fade MIDI music to lower volume, but just stop it
283   if (Mix_GetMusicType(NULL) == MUS_MID)
284     Mixer_StopMusicChannel();
285 #endif
286
287   setString(&currently_playing_music_filename, NULL);
288 }
289
290 static void Mixer_UnFadeChannel(int channel)
291 {
292   if (!mixer[channel].active || !IS_FADING(mixer[channel]))
293     return;
294
295   mixer[channel].state &= ~SND_CTRL_FADE;
296   mixer[channel].volume = SOUND_MAX_VOLUME;
297
298   Mix_ExpireChannel(channel, -1);
299   Mix_Volume(channel, mixer[channel].volume);
300 }
301
302 static void Mixer_InsertSound(SoundControl snd_ctrl)
303 {
304   SoundInfo *snd_info;
305   int i, k;
306   int num_sounds = getSoundListSize();
307   int num_music  = getMusicListSize();
308
309   if (IS_MUSIC(snd_ctrl))
310   {
311     if (snd_ctrl.nr >= num_music)       /* invalid music */
312       return;
313
314     if (snd_ctrl.nr < 0)                /* undefined music */
315     {
316       if (num_music_noconf == 0)        /* no fallback music available */
317         return;
318
319       snd_ctrl.nr = UNMAP_NOCONF_MUSIC(snd_ctrl.nr) % num_music_noconf;
320       snd_info = Music_NoConf[snd_ctrl.nr];
321     }
322     else
323       snd_info = getMusicInfoEntryFromMusicID(snd_ctrl.nr);
324   }
325   else
326   {
327     if (snd_ctrl.nr < 0 || snd_ctrl.nr >= num_sounds)
328       return;
329
330     snd_info = getSoundInfoEntryFromSoundID(snd_ctrl.nr);
331   }
332
333   if (snd_info == NULL)
334     return;
335
336   /* copy sound sample and format information */
337   snd_ctrl.type         = snd_info->type;
338   snd_ctrl.format       = snd_info->format;
339   snd_ctrl.data_ptr     = snd_info->data_ptr;
340   snd_ctrl.data_len     = snd_info->data_len;
341   snd_ctrl.num_channels = snd_info->num_channels;
342
343   /* play music samples on a dedicated music channel */
344   if (IS_MUSIC(snd_ctrl))
345   {
346     Mixer_StopMusicChannel();
347
348     mixer[audio.music_channel] = snd_ctrl;
349     Mixer_PlayMusicChannel();
350
351     setString(&currently_playing_music_filename,
352               getBaseNamePtr(snd_info->source_filename));
353
354     return;
355   }
356
357   /* check if (and how often) this sound sample is already playing */
358   for (k = 0, i = audio.first_sound_channel; i < audio.num_channels; i++)
359     if (mixer[i].active && SAME_SOUND_DATA(mixer[i], snd_ctrl))
360       k++;
361
362   /* reset expiration delay for already playing loop sounds */
363   if (k > 0 && IS_LOOP(snd_ctrl))
364   {
365     for (i = audio.first_sound_channel; i < audio.num_channels; i++)
366     {
367       if (mixer[i].active && SAME_SOUND_DATA(mixer[i], snd_ctrl))
368       {
369         if (IS_FADING(mixer[i]))
370           Mixer_UnFadeChannel(i);
371
372         /* restore settings like volume and stereo position */
373         mixer[i].volume = snd_ctrl.volume;
374         mixer[i].stereo_position = snd_ctrl.stereo_position;
375
376         Mixer_SetChannelProperties(i);
377         Mixer_ResetChannelExpiration(i);
378       }
379     }
380
381     return;
382   }
383
384   /* don't play sound more than n times simultaneously (with n == 2 for now) */
385   if (k >= 2)
386   {
387     unsigned int playing_current = Counter();
388     int longest = 0, longest_nr = audio.first_sound_channel;
389
390     /* look for oldest equal sound */
391     for (i = audio.first_sound_channel; i < audio.num_channels; i++)
392     {
393       int playing_time = playing_current - mixer[i].playing_starttime;
394       int actual;
395
396       if (!mixer[i].active || !SAME_SOUND_NR(mixer[i], snd_ctrl))
397         continue;
398
399       actual = 1000 * playing_time / mixer[i].data_len;
400
401       if (actual >= longest)
402       {
403         longest = actual;
404         longest_nr = i;
405       }
406     }
407
408     Mixer_StopChannel(longest_nr);
409   }
410
411   /* If all (non-music) channels are active, stop the channel that has
412      played its sound sample most completely (in percent of the sample
413      length). As we cannot currently get the actual playing position
414      of the channel's sound sample when compiling with the SDL mixer
415      library, we use the current playing time (in milliseconds) instead. */
416
417 #if DEBUG
418   /* channel allocation sanity check -- should not be needed */
419   if (mixer_active_channels ==
420       audio.num_channels - (mixer[audio.music_channel].active ? 0 : 1))
421   {
422     for (i = audio.first_sound_channel; i < audio.num_channels; i++)
423     {
424       if (!mixer[i].active)
425       {
426         Error(ERR_INFO, "Mixer_InsertSound: Channel %d inactive", i);
427         Error(ERR_INFO, "Mixer_InsertSound: This should never happen!");
428
429         mixer_active_channels--;
430       }
431     }
432   }
433 #endif
434
435   if (mixer_active_channels ==
436       audio.num_channels - (mixer[audio.music_channel].active ? 0 : 1))
437   {
438     unsigned int playing_current = Counter();
439     int longest = 0, longest_nr = audio.first_sound_channel;
440
441     for (i = audio.first_sound_channel; i < audio.num_channels; i++)
442     {
443       int playing_time = playing_current - mixer[i].playing_starttime;
444       int actual = 1000 * playing_time / mixer[i].data_len;
445
446       if (!IS_LOOP(mixer[i]) && actual > longest)
447       {
448         longest = actual;
449         longest_nr = i;
450       }
451     }
452
453     Mixer_StopChannel(longest_nr);
454   }
455
456   /* add the new sound to the mixer */
457   for (i = audio.first_sound_channel; i < audio.num_channels; i++)
458   {
459     if (!mixer[i].active)
460     {
461       mixer[i] = snd_ctrl;
462       Mixer_PlayChannel(i);
463
464       break;
465     }
466   }
467 }
468
469 static void HandleSoundRequest(SoundControl snd_ctrl)
470 {
471   int i;
472
473   /* deactivate channels that have expired since the last request */
474   for (i = 0; i < audio.num_channels; i++)
475     if (mixer[i].active && Mixer_ChannelExpired(i))
476       Mixer_StopChannel(i);
477
478   if (IS_RELOADING(snd_ctrl))           /* load new sound or music files */
479   {
480     Mixer_StopMusicChannel();
481     for (i = audio.first_sound_channel; i < audio.num_channels; i++)
482       Mixer_StopChannel(i);
483
484     if (snd_ctrl.state & SND_CTRL_RELOAD_SOUNDS)
485       ReloadCustomSounds();
486     else
487       ReloadCustomMusic();
488   }
489   else if (IS_FADING(snd_ctrl))         /* fade out existing sound or music */
490   {
491     if (IS_MUSIC(snd_ctrl))
492     {
493       Mixer_FadeMusicChannel();
494       return;
495     }
496
497     for (i = audio.first_sound_channel; i < audio.num_channels; i++)
498       if (SAME_SOUND_NR(mixer[i], snd_ctrl) || ALL_SOUNDS(snd_ctrl))
499         Mixer_FadeChannel(i);
500   }
501   else if (IS_STOPPING(snd_ctrl))       /* stop existing sound or music */
502   {
503     if (IS_MUSIC(snd_ctrl))
504     {
505       Mixer_StopMusicChannel();
506       return;
507     }
508
509     for (i = audio.first_sound_channel; i < audio.num_channels; i++)
510       if (SAME_SOUND_NR(mixer[i], snd_ctrl) || ALL_SOUNDS(snd_ctrl))
511         Mixer_StopChannel(i);
512   }
513   else if (SET_EXPIRE_LOOPS(snd_ctrl))  /* set loop expiration on or off */
514   {
515     expire_loop_sounds = snd_ctrl.active;
516   }
517   else if (snd_ctrl.active)             /* add new sound to mixer */
518   {
519     Mixer_InsertSound(snd_ctrl);
520   }
521 }
522
523 void StartMixer(void)
524 {
525   int i;
526
527   if (!audio.sound_available)
528     return;
529
530   /* initialize stereo position conversion information */
531   for (i = 0; i <= SOUND_MAX_LEFT2RIGHT; i++)
532     stereo_volume[i] =
533       (int)sqrt((float)(SOUND_MAX_LEFT2RIGHT * SOUND_MAX_LEFT2RIGHT - i * i));
534 }
535
536
537 /* THE STUFF ABOVE IS ONLY USED BY THE SOUND SERVER CHILD PROCESS            */
538 /* ========================================================================= */
539 /* THE STUFF BELOW IS ONLY USED BY THE MAIN PROCESS                          */
540
541 #define CHUNK_ID_LEN            4       /* IFF style chunk id length */
542 #define WAV_HEADER_SIZE         16      /* size of WAV file header */
543
544 static void *Load_WAV(char *filename)
545 {
546   SoundInfo *snd_info;
547
548   if (!audio.sound_available)
549     return NULL;
550
551   snd_info = checked_calloc(sizeof(SoundInfo));
552
553   if ((snd_info->data_ptr = Mix_LoadWAV(filename)) == NULL)
554   {
555     Error(ERR_WARN, "cannot read sound file '%s': %s", filename, Mix_GetError());
556     free(snd_info);
557     return NULL;
558   }
559
560   snd_info->data_len = ((Mix_Chunk *)snd_info->data_ptr)->alen;
561
562   snd_info->type = SND_TYPE_WAV;
563   snd_info->source_filename = getStringCopy(filename);
564
565   return snd_info;
566 }
567
568 static void *Load_MOD(char *filename)
569 {
570   MusicInfo *mod_info;
571
572   if (!audio.sound_available)
573     return NULL;
574
575   mod_info = checked_calloc(sizeof(MusicInfo));
576
577   if ((mod_info->data_ptr = Mix_LoadMUS(filename)) == NULL)
578   {
579     Error(ERR_WARN, "cannot read music file '%s': %s", filename, Mix_GetError());
580     free(mod_info);
581     return NULL;
582   }
583
584   mod_info->type = MUS_TYPE_MOD;
585   mod_info->source_filename = getStringCopy(filename);
586
587   return mod_info;
588 }
589
590 static void *Load_WAV_or_MOD(char *filename)
591 {
592   if (FileIsMusic(filename))
593     return Load_MOD(filename);
594   else if (FileIsSound(filename))
595     return Load_WAV(filename);
596   else
597     return NULL;
598 }
599
600 void LoadCustomMusic_NoConf(void)
601 {
602   static boolean draw_init_text = TRUE;         /* only draw at startup */
603   static char *last_music_directory = NULL;
604   char *music_directory = getCustomMusicDirectory();
605   Directory *dir;
606   DirectoryEntry *dir_entry;
607   int num_music = getMusicListSize();
608
609   if (!audio.sound_available)
610     return;
611
612   if (last_music_directory != NULL &&
613       strEqual(last_music_directory, music_directory))
614     return;     /* old and new music directory are the same */
615
616   if (last_music_directory != NULL)
617     free(last_music_directory);
618   last_music_directory = getStringCopy(music_directory);
619
620   FreeAllMusic_NoConf();
621
622   if ((dir = openDirectory(music_directory)) == NULL)
623   {
624     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
625
626     audio.music_available = FALSE;
627
628     return;
629   }
630
631   if (draw_init_text)
632     DrawInitText("Loading music", 120, FC_GREEN);
633
634   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
635   {
636     char *basename = dir_entry->basename;
637     MusicInfo *mus_info = NULL;
638     boolean music_already_used = FALSE;
639     int i;
640
641     /* skip all music files that are configured in music config file */
642     for (i = 0; i < num_music; i++)
643     {
644       struct FileInfo *music = getMusicListEntry(i);
645
646       if (strEqual(basename, music->filename))
647       {
648         music_already_used = TRUE;
649         break;
650       }
651     }
652
653     if (music_already_used)
654       continue;
655
656     if (draw_init_text)
657       DrawInitText(basename, 150, FC_YELLOW);
658
659     if (FileIsMusic(dir_entry->filename))
660       mus_info = Load_WAV_or_MOD(dir_entry->filename);
661
662     if (mus_info)
663     {
664       num_music_noconf++;
665       Music_NoConf = checked_realloc(Music_NoConf,
666                                      num_music_noconf * sizeof(MusicInfo *));
667       Music_NoConf[num_music_noconf - 1] = mus_info;
668     }
669   }
670
671   closeDirectory(dir);
672
673   draw_init_text = FALSE;
674 }
675
676 int getSoundListSize()
677 {
678   return (sound_info->num_file_list_entries +
679           sound_info->num_dynamic_file_list_entries);
680 }
681
682 int getMusicListSize()
683 {
684   return (music_info->num_file_list_entries +
685           music_info->num_dynamic_file_list_entries);
686 }
687
688 struct FileInfo *getSoundListEntry(int pos)
689 {
690   int num_sounds = getSoundListSize();
691   int num_list_entries = sound_info->num_file_list_entries;
692   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
693
694   if (pos < 0 || pos >= num_sounds)     /* invalid sound */
695     return NULL;
696
697   return (pos < num_list_entries ? &sound_info->file_list[list_pos] :
698           &sound_info->dynamic_file_list[list_pos]);
699 }
700
701 struct FileInfo *getMusicListEntry(int pos)
702 {
703   int num_music = getMusicListSize();
704   int num_list_entries = music_info->num_file_list_entries;
705   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
706
707   if (pos < 0 || pos >= num_music)      /* invalid music */
708     return NULL;
709
710   return (pos < num_list_entries ? &music_info->file_list[list_pos] :
711           &music_info->dynamic_file_list[list_pos]);
712 }
713
714 static SoundInfo *getSoundInfoEntryFromSoundID(int pos)
715 {
716   int num_sounds = getSoundListSize();
717   int num_list_entries = sound_info->num_file_list_entries;
718   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
719   SoundInfo **snd_info =
720     (SoundInfo **)(pos < num_list_entries ? sound_info->artwork_list :
721                    sound_info->dynamic_artwork_list);
722
723   if (pos < 0 || pos >= num_sounds)     /* invalid sound */
724     return NULL;
725
726   return snd_info[list_pos];
727 }
728
729 static MusicInfo *getMusicInfoEntryFromMusicID(int pos)
730 {
731   int num_music = getMusicListSize();
732   int num_list_entries = music_info->num_file_list_entries;
733   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
734   MusicInfo **mus_info =
735     (MusicInfo **)(pos < num_list_entries ? music_info->artwork_list :
736                    music_info->dynamic_artwork_list);
737
738   if (pos < 0 || pos >= num_music)      /* invalid music */
739     return NULL;
740
741   return mus_info[list_pos];
742 }
743
744 char *getMusicInfoEntryFilename(int pos)
745 {
746   MusicInfo *mus_info = getMusicInfoEntryFromMusicID(pos);
747
748   if (mus_info == NULL)
749     return NULL;
750
751   return getBaseNamePtr(mus_info->source_filename);
752 }
753
754 char *getCurrentlyPlayingMusicFilename()
755 {
756   return currently_playing_music_filename;
757 }
758
759 int getSoundListPropertyMappingSize()
760 {
761   return sound_info->num_property_mapping_entries;
762 }
763
764 int getMusicListPropertyMappingSize()
765 {
766   return music_info->num_property_mapping_entries;
767 }
768
769 struct PropertyMapping *getSoundListPropertyMapping()
770 {
771   return sound_info->property_mapping;
772 }
773
774 struct PropertyMapping *getMusicListPropertyMapping()
775 {
776   return music_info->property_mapping;
777 }
778
779 void InitSoundList(struct ConfigInfo *config_list, int num_file_list_entries,
780                    struct ConfigTypeInfo *config_suffix_list,
781                    char **base_prefixes, char **ext1_suffixes,
782                    char **ext2_suffixes, char **ext3_suffixes,
783                    char **ignore_tokens)
784 {
785   int i;
786
787   sound_info = checked_calloc(sizeof(struct ArtworkListInfo));
788   sound_info->type = ARTWORK_TYPE_SOUNDS;
789
790   /* ---------- initialize file list and suffix lists ---------- */
791
792   sound_info->num_file_list_entries = num_file_list_entries;
793   sound_info->num_dynamic_file_list_entries = 0;
794
795   sound_info->file_list =
796     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
797                               num_file_list_entries);
798   sound_info->dynamic_file_list = NULL;
799
800   sound_info->num_suffix_list_entries = 0;
801   for (i = 0; config_suffix_list[i].token != NULL; i++)
802     sound_info->num_suffix_list_entries++;
803
804   sound_info->suffix_list = config_suffix_list;
805
806   /* ---------- initialize base prefix and suffixes lists ---------- */
807
808   sound_info->num_base_prefixes = 0;
809   for (i = 0; base_prefixes[i] != NULL; i++)
810     sound_info->num_base_prefixes++;
811
812   sound_info->num_ext1_suffixes = 0;
813   for (i = 0; ext1_suffixes[i] != NULL; i++)
814     sound_info->num_ext1_suffixes++;
815
816   sound_info->num_ext2_suffixes = 0;
817   for (i = 0; ext2_suffixes[i] != NULL; i++)
818     sound_info->num_ext2_suffixes++;
819
820   sound_info->num_ext3_suffixes = 0;
821   for (i = 0; ext3_suffixes[i] != NULL; i++)
822     sound_info->num_ext3_suffixes++;
823
824   sound_info->num_ignore_tokens = 0;
825   for (i = 0; ignore_tokens[i] != NULL; i++)
826     sound_info->num_ignore_tokens++;
827
828   sound_info->base_prefixes = base_prefixes;
829   sound_info->ext1_suffixes = ext1_suffixes;
830   sound_info->ext2_suffixes = ext2_suffixes;
831   sound_info->ext3_suffixes = ext3_suffixes;
832   sound_info->ignore_tokens = ignore_tokens;
833
834   sound_info->num_property_mapping_entries = 0;
835
836   sound_info->property_mapping = NULL;
837
838   /* ---------- initialize artwork reference and content lists ---------- */
839
840   sound_info->sizeof_artwork_list_entry = sizeof(SoundInfo *);
841
842   sound_info->artwork_list =
843     checked_calloc(num_file_list_entries * sizeof(SoundInfo *));
844   sound_info->dynamic_artwork_list = NULL;
845
846   sound_info->content_list = NULL;
847
848   /* ---------- initialize artwork loading/freeing functions ---------- */
849
850   sound_info->load_artwork = Load_WAV;
851   sound_info->free_artwork = FreeSound;
852 }
853
854 void InitMusicList(struct ConfigInfo *config_list, int num_file_list_entries,
855                    struct ConfigTypeInfo *config_suffix_list,
856                    char **base_prefixes, char **ext1_suffixes,
857                    char **ext2_suffixes, char **ext3_suffixes,
858                    char **ignore_tokens)
859 {
860   int i;
861
862   music_info = checked_calloc(sizeof(struct ArtworkListInfo));
863   music_info->type = ARTWORK_TYPE_MUSIC;
864
865   /* ---------- initialize file list and suffix lists ---------- */
866
867   music_info->num_file_list_entries = num_file_list_entries;
868   music_info->num_dynamic_file_list_entries = 0;
869
870   music_info->file_list =
871     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
872                               num_file_list_entries);
873   music_info->dynamic_file_list = NULL;
874
875   music_info->num_suffix_list_entries = 0;
876   for (i = 0; config_suffix_list[i].token != NULL; i++)
877     music_info->num_suffix_list_entries++;
878
879   music_info->suffix_list = config_suffix_list;
880
881   /* ---------- initialize base prefix and suffixes lists ---------- */
882
883   music_info->num_base_prefixes = 0;
884   for (i = 0; base_prefixes[i] != NULL; i++)
885     music_info->num_base_prefixes++;
886
887   music_info->num_ext1_suffixes = 0;
888   for (i = 0; ext1_suffixes[i] != NULL; i++)
889     music_info->num_ext1_suffixes++;
890
891   music_info->num_ext2_suffixes = 0;
892   for (i = 0; ext2_suffixes[i] != NULL; i++)
893     music_info->num_ext2_suffixes++;
894
895   music_info->num_ext3_suffixes = 0;
896   for (i = 0; ext3_suffixes[i] != NULL; i++)
897     music_info->num_ext3_suffixes++;
898
899   music_info->num_ignore_tokens = 0;
900   for (i = 0; ignore_tokens[i] != NULL; i++)
901     music_info->num_ignore_tokens++;
902
903   music_info->base_prefixes = base_prefixes;
904   music_info->ext1_suffixes = ext1_suffixes;
905   music_info->ext2_suffixes = ext2_suffixes;
906   music_info->ext3_suffixes = ext3_suffixes;
907   music_info->ignore_tokens = ignore_tokens;
908
909   music_info->num_property_mapping_entries = 0;
910
911   music_info->property_mapping = NULL;
912
913   /* ---------- initialize artwork reference and content lists ---------- */
914
915   music_info->sizeof_artwork_list_entry = sizeof(MusicInfo *);
916
917   music_info->artwork_list =
918     checked_calloc(num_file_list_entries * sizeof(MusicInfo *));
919   music_info->dynamic_artwork_list = NULL;
920
921   music_info->content_list = NULL;
922
923   /* ---------- initialize artwork loading/freeing functions ---------- */
924
925   music_info->load_artwork = Load_WAV_or_MOD;
926   music_info->free_artwork = FreeMusic;
927 }
928
929 void PlayMusic(int nr)
930 {
931   if (!audio.music_available)
932     return;
933
934   PlaySoundMusic(nr);
935 }
936
937 void PlaySound(int nr)
938 {
939   if (!setup.sound_simple)
940     return;
941
942   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_SOUND);
943 }
944
945 void PlaySoundStereo(int nr, int stereo_position)
946 {
947   if (!setup.sound_simple)
948     return;
949
950   PlaySoundExt(nr, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_SOUND);
951 }
952
953 void PlaySoundLoop(int nr)
954 {
955   if (!setup.sound_loops)
956     return;
957
958   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_LOOP);
959 }
960
961 void PlaySoundMusic(int nr)
962 {
963   if (!setup.sound_music)
964     return;
965
966   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_MUSIC);
967 }
968
969 void PlaySoundExt(int nr, int volume, int stereo_position, int state)
970 {
971   SoundControl snd_ctrl;
972
973   if (!audio.sound_available ||
974       !audio.sound_enabled ||
975       audio.sound_deactivated)
976     return;
977
978   volume = SETUP_SOUND_VOLUME(volume, state);
979
980   if (volume < SOUND_MIN_VOLUME)
981     volume = SOUND_MIN_VOLUME;
982   else if (volume > SOUND_MAX_VOLUME)
983     volume = SOUND_MAX_VOLUME;
984
985   if (stereo_position < SOUND_MAX_LEFT)
986     stereo_position = SOUND_MAX_LEFT;
987   else if (stereo_position > SOUND_MAX_RIGHT)
988     stereo_position = SOUND_MAX_RIGHT;
989
990   clear_mem(&snd_ctrl, sizeof(SoundControl));   /* to make valgrind happy */
991
992   snd_ctrl.active = TRUE;
993   snd_ctrl.nr = nr;
994   snd_ctrl.volume = volume;
995   snd_ctrl.stereo_position = stereo_position;
996   snd_ctrl.state = state;
997
998   HandleSoundRequest(snd_ctrl);
999 }
1000
1001 void FadeMusic(void)
1002 {
1003   if (!audio.music_available)
1004     return;
1005
1006   StopSoundExt(-1, SND_CTRL_FADE_MUSIC);
1007 }
1008
1009 void FadeSound(int nr)
1010 {
1011   StopSoundExt(nr, SND_CTRL_FADE_SOUND);
1012 }
1013
1014 void FadeSounds()
1015 {
1016   StopSoundExt(-1, SND_CTRL_FADE_ALL);
1017 }
1018
1019 void FadeSoundsAndMusic()
1020 {
1021   FadeSounds();
1022   FadeMusic();
1023 }
1024
1025 void StopMusic(void)
1026 {
1027   if (!audio.music_available)
1028     return;
1029
1030   StopSoundExt(-1, SND_CTRL_STOP_MUSIC);
1031 }
1032
1033 void StopSound(int nr)
1034 {
1035   StopSoundExt(nr, SND_CTRL_STOP_SOUND);
1036 }
1037
1038 void StopSounds()
1039 {
1040   StopMusic();
1041   StopSoundExt(-1, SND_CTRL_STOP_ALL);
1042 }
1043
1044 void StopSoundExt(int nr, int state)
1045 {
1046   SoundControl snd_ctrl;
1047
1048   if (!audio.sound_available)
1049     return;
1050
1051   clear_mem(&snd_ctrl, sizeof(SoundControl));   /* to make valgrind happy */
1052
1053   snd_ctrl.active = FALSE;
1054   snd_ctrl.nr = nr;
1055   snd_ctrl.state = state;
1056
1057   HandleSoundRequest(snd_ctrl);
1058 }
1059
1060 void ExpireSoundLoops(boolean active)
1061 {
1062   SoundControl snd_ctrl;
1063
1064   if (!audio.sound_available)
1065     return;
1066
1067   clear_mem(&snd_ctrl, sizeof(SoundControl));   /* to make valgrind happy */
1068
1069   snd_ctrl.active = active;
1070   snd_ctrl.state = SND_CTRL_EXPIRE_LOOPS;
1071
1072   HandleSoundRequest(snd_ctrl);
1073 }
1074
1075 static void ReloadCustomSounds()
1076 {
1077   LoadArtworkConfig(sound_info);
1078   ReloadCustomArtworkList(sound_info);
1079 }
1080
1081 static void ReloadCustomMusic()
1082 {
1083   LoadArtworkConfig(music_info);
1084   ReloadCustomArtworkList(music_info);
1085
1086   /* load all music files from directory not defined in "musicinfo.conf" */
1087   LoadCustomMusic_NoConf();
1088 }
1089
1090 void InitReloadCustomSounds(char *set_identifier)
1091 {
1092   if (!audio.sound_available)
1093     return;
1094
1095   ReloadCustomSounds();
1096 }
1097
1098 void InitReloadCustomMusic(char *set_identifier)
1099 {
1100   if (!audio.music_available)
1101     return;
1102
1103   ReloadCustomMusic();
1104 }
1105
1106 void FreeSound(void *ptr)
1107 {
1108   SoundInfo *sound = (SoundInfo *)ptr;
1109
1110   if (sound == NULL)
1111     return;
1112
1113   if (sound->data_ptr)
1114   {
1115     Mix_FreeChunk(sound->data_ptr);
1116   }
1117
1118   checked_free(sound->source_filename);
1119
1120   free(sound);
1121 }
1122
1123 void FreeMusic(void *ptr)
1124 {
1125   MusicInfo *music = (MusicInfo *)ptr;
1126
1127   if (music == NULL)
1128     return;
1129
1130   if (music->data_ptr)
1131   {
1132     if (music->type == MUS_TYPE_MOD)
1133       Mix_FreeMusic(music->data_ptr);
1134     else
1135       Mix_FreeChunk(music->data_ptr);
1136   }
1137
1138   free(music);
1139 }
1140
1141 static void FreeAllMusic_NoConf()
1142 {
1143   int i;
1144
1145   if (Music_NoConf == NULL)
1146     return;
1147
1148   for (i = 0; i < num_music_noconf; i++)
1149     FreeMusic(Music_NoConf[i]);
1150
1151   free(Music_NoConf);
1152
1153   Music_NoConf = NULL;
1154   num_music_noconf = 0;
1155 }
1156
1157 void FreeAllSounds()
1158 {
1159   FreeCustomArtworkLists(sound_info);
1160 }
1161
1162 void FreeAllMusic()
1163 {
1164   FreeCustomArtworkLists(music_info);
1165   FreeAllMusic_NoConf();
1166 }
1167
1168 /* THE STUFF ABOVE IS ONLY USED BY THE MAIN PROCESS                          */
1169 /* ========================================================================= */