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