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