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