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