added checking pointer
[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 //                  https://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       DelayReachedExt2(&mixer[channel].playing_starttime,
170                        SOUND_LOOP_EXPIRATION_TIME, Counter()))
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_WINDOWS)
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_WINDOWS)
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         Debug("audio", "Mixer_InsertSound: Channel %d inactive", i);
413         Debug("audio", "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     Warn("cannot read sound file '%s': %s", filename, Mix_GetError());
542
543     free(snd_info);
544
545     return NULL;
546   }
547
548   snd_info->data_len = ((Mix_Chunk *)snd_info->data_ptr)->alen;
549
550   snd_info->type = SND_TYPE_WAV;
551   snd_info->source_filename = getStringCopy(filename);
552
553   return snd_info;
554 }
555
556 static void *Load_MOD(char *filename)
557 {
558   MusicInfo *mod_info;
559
560   if (!audio.sound_available)
561     return NULL;
562
563   mod_info = checked_calloc(sizeof(MusicInfo));
564
565   if ((mod_info->data_ptr = Mix_LoadMUS(filename)) == NULL)
566   {
567     Warn("cannot read music file '%s': %s", filename, Mix_GetError());
568
569     free(mod_info);
570
571     return NULL;
572   }
573
574   mod_info->type = MUS_TYPE_MOD;
575   mod_info->source_filename = getStringCopy(filename);
576
577   return mod_info;
578 }
579
580 static void *Load_WAV_or_MOD(char *filename)
581 {
582   if (FileIsMusic(filename))
583     return Load_MOD(filename);
584   else if (FileIsSound(filename))
585     return Load_WAV(filename);
586   else
587     return NULL;
588 }
589
590 static void LoadCustomMusic_NoConf(void)
591 {
592   static boolean draw_init_text = TRUE;         // only draw at startup
593   static char *last_music_directory = NULL;
594   char *music_directory = getCustomMusicDirectory_NoConf();
595   Directory *dir;
596   DirectoryEntry *dir_entry;
597   int num_music = getMusicListSize();
598
599   if (!audio.sound_available)
600     return;
601
602   if (last_music_directory != NULL &&
603       strEqual(last_music_directory, music_directory))
604     return;     // old and new music directory are the same
605
606   if (last_music_directory != NULL)
607     free(last_music_directory);
608   last_music_directory = getStringCopy(music_directory);
609
610   FreeAllMusic_NoConf();
611
612   if (music_directory == NULL)
613   {
614     Warn("cannot find music directory with unconfigured music");
615
616     audio.music_available = FALSE;
617
618     return;
619   }
620   else if ((dir = openDirectory(music_directory)) == NULL)
621   {
622     Warn("cannot read music directory '%s'", music_directory);
623
624     audio.music_available = FALSE;
625
626     return;
627   }
628
629   if (draw_init_text)
630     DrawInitTextHead("Loading music");
631
632   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
633   {
634     char *basename = dir_entry->basename;
635     MusicInfo *mus_info = NULL;
636     boolean music_already_used = FALSE;
637     int i;
638
639     // skip all music files that are configured in music config file
640     for (i = 0; i < num_music; i++)
641     {
642       struct FileInfo *music = getMusicListEntry(i);
643
644       if (strEqual(basename, music->filename))
645       {
646         music_already_used = TRUE;
647         break;
648       }
649     }
650
651     if (music_already_used)
652       continue;
653
654     if (draw_init_text)
655       DrawInitTextItem(basename);
656
657     if (FileIsMusic(dir_entry->filename))
658       mus_info = Load_WAV_or_MOD(dir_entry->filename);
659
660     if (mus_info)
661     {
662       num_music_noconf++;
663       Music_NoConf = checked_realloc(Music_NoConf,
664                                      num_music_noconf * sizeof(MusicInfo *));
665       Music_NoConf[num_music_noconf - 1] = mus_info;
666     }
667   }
668
669   closeDirectory(dir);
670
671   draw_init_text = FALSE;
672 }
673
674 int getSoundListSize(void)
675 {
676   return (sound_info->num_file_list_entries +
677           sound_info->num_dynamic_file_list_entries);
678 }
679
680 int getMusicListSize(void)
681 {
682   return (music_info->num_file_list_entries +
683           music_info->num_dynamic_file_list_entries);
684 }
685
686 struct FileInfo *getSoundListEntry(int pos)
687 {
688   int num_sounds = getSoundListSize();
689   int num_list_entries = sound_info->num_file_list_entries;
690   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
691
692   if (pos < 0 || pos >= num_sounds)     // invalid sound
693     return NULL;
694
695   return (pos < num_list_entries ? &sound_info->file_list[list_pos] :
696           &sound_info->dynamic_file_list[list_pos]);
697 }
698
699 struct FileInfo *getMusicListEntry(int pos)
700 {
701   int num_music = getMusicListSize();
702   int num_list_entries = music_info->num_file_list_entries;
703   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
704
705   if (pos < 0 || pos >= num_music)      // invalid music
706     return NULL;
707
708   return (pos < num_list_entries ? &music_info->file_list[list_pos] :
709           &music_info->dynamic_file_list[list_pos]);
710 }
711
712 static SoundInfo *getSoundInfoEntryFromSoundID(int pos)
713 {
714   int num_sounds = getSoundListSize();
715   int num_list_entries = sound_info->num_file_list_entries;
716   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
717   SoundInfo **snd_info =
718     (SoundInfo **)(pos < num_list_entries ? sound_info->artwork_list :
719                    sound_info->dynamic_artwork_list);
720
721   if (pos < 0 || pos >= num_sounds)     // invalid sound
722     return NULL;
723
724   return snd_info[list_pos];
725 }
726
727 static MusicInfo *getMusicInfoEntryFromMusicID(int pos)
728 {
729   int num_music = getMusicListSize();
730   int num_list_entries = music_info->num_file_list_entries;
731   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
732   MusicInfo **mus_info =
733     (MusicInfo **)(pos < num_list_entries ? music_info->artwork_list :
734                    music_info->dynamic_artwork_list);
735
736   if (pos >= num_music)                 // invalid music
737     return NULL;
738
739   if (pos < 0)                          // undefined music
740   {
741     if (num_music_noconf == 0)          // no fallback music available
742       return NULL;
743
744     pos = UNMAP_NOCONF_MUSIC(pos) % num_music_noconf;
745
746     return Music_NoConf[pos];
747   }
748
749   return mus_info[list_pos];
750 }
751
752 char *getSoundInfoEntryFilename(int pos)
753 {
754   SoundInfo *snd_info = getSoundInfoEntryFromSoundID(pos);
755
756   if (snd_info == NULL)
757     return NULL;
758
759   return getBaseNamePtr(snd_info->source_filename);
760 }
761
762 char *getMusicInfoEntryFilename(int pos)
763 {
764   MusicInfo *mus_info = getMusicInfoEntryFromMusicID(pos);
765
766   if (mus_info == NULL)
767     return NULL;
768
769   return getBaseNamePtr(mus_info->source_filename);
770 }
771
772 char *getCurrentlyPlayingMusicFilename(void)
773 {
774   return currently_playing_music_filename;
775 }
776
777 int getSoundListPropertyMappingSize(void)
778 {
779   return sound_info->num_property_mapping_entries;
780 }
781
782 int getMusicListPropertyMappingSize(void)
783 {
784   return music_info->num_property_mapping_entries;
785 }
786
787 struct PropertyMapping *getSoundListPropertyMapping(void)
788 {
789   return sound_info->property_mapping;
790 }
791
792 struct PropertyMapping *getMusicListPropertyMapping(void)
793 {
794   return music_info->property_mapping;
795 }
796
797 void InitSoundList(struct ConfigInfo *config_list, int num_file_list_entries,
798                    struct ConfigTypeInfo *config_suffix_list,
799                    char **base_prefixes, char **ext1_suffixes,
800                    char **ext2_suffixes, char **ext3_suffixes,
801                    char **ignore_tokens)
802 {
803   int i;
804
805   sound_info = checked_calloc(sizeof(struct ArtworkListInfo));
806   sound_info->type = ARTWORK_TYPE_SOUNDS;
807
808   // ---------- initialize file list and suffix lists ----------
809
810   sound_info->num_file_list_entries = num_file_list_entries;
811   sound_info->num_dynamic_file_list_entries = 0;
812
813   sound_info->file_list =
814     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
815                               num_file_list_entries);
816   sound_info->dynamic_file_list = NULL;
817
818   sound_info->num_suffix_list_entries = 0;
819   for (i = 0; config_suffix_list[i].token != NULL; i++)
820     sound_info->num_suffix_list_entries++;
821
822   sound_info->suffix_list = config_suffix_list;
823
824   // ---------- initialize base prefix and suffixes lists ----------
825
826   sound_info->num_base_prefixes = 0;
827   for (i = 0; base_prefixes[i] != NULL; i++)
828     sound_info->num_base_prefixes++;
829
830   sound_info->num_ext1_suffixes = 0;
831   for (i = 0; ext1_suffixes[i] != NULL; i++)
832     sound_info->num_ext1_suffixes++;
833
834   sound_info->num_ext2_suffixes = 0;
835   for (i = 0; ext2_suffixes[i] != NULL; i++)
836     sound_info->num_ext2_suffixes++;
837
838   sound_info->num_ext3_suffixes = 0;
839   for (i = 0; ext3_suffixes[i] != NULL; i++)
840     sound_info->num_ext3_suffixes++;
841
842   sound_info->num_ignore_tokens = 0;
843   for (i = 0; ignore_tokens[i] != NULL; i++)
844     sound_info->num_ignore_tokens++;
845
846   sound_info->base_prefixes = base_prefixes;
847   sound_info->ext1_suffixes = ext1_suffixes;
848   sound_info->ext2_suffixes = ext2_suffixes;
849   sound_info->ext3_suffixes = ext3_suffixes;
850   sound_info->ignore_tokens = ignore_tokens;
851
852   sound_info->num_property_mapping_entries = 0;
853
854   sound_info->property_mapping = NULL;
855
856   // ---------- initialize artwork reference and content lists ----------
857
858   sound_info->sizeof_artwork_list_entry = sizeof(SoundInfo *);
859
860   sound_info->artwork_list =
861     checked_calloc(num_file_list_entries * sizeof(SoundInfo *));
862   sound_info->dynamic_artwork_list = NULL;
863
864   sound_info->content_list = NULL;
865
866   // ---------- initialize artwork loading/freeing functions ----------
867
868   sound_info->load_artwork = Load_WAV;
869   sound_info->free_artwork = FreeSound;
870 }
871
872 void InitMusicList(struct ConfigInfo *config_list, int num_file_list_entries,
873                    struct ConfigTypeInfo *config_suffix_list,
874                    char **base_prefixes, char **ext1_suffixes,
875                    char **ext2_suffixes, char **ext3_suffixes,
876                    char **ignore_tokens)
877 {
878   int i;
879
880   music_info = checked_calloc(sizeof(struct ArtworkListInfo));
881   music_info->type = ARTWORK_TYPE_MUSIC;
882
883   // ---------- initialize file list and suffix lists ----------
884
885   music_info->num_file_list_entries = num_file_list_entries;
886   music_info->num_dynamic_file_list_entries = 0;
887
888   music_info->file_list =
889     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
890                               num_file_list_entries);
891   music_info->dynamic_file_list = NULL;
892
893   music_info->num_suffix_list_entries = 0;
894   for (i = 0; config_suffix_list[i].token != NULL; i++)
895     music_info->num_suffix_list_entries++;
896
897   music_info->suffix_list = config_suffix_list;
898
899   // ---------- initialize base prefix and suffixes lists ----------
900
901   music_info->num_base_prefixes = 0;
902   for (i = 0; base_prefixes[i] != NULL; i++)
903     music_info->num_base_prefixes++;
904
905   music_info->num_ext1_suffixes = 0;
906   for (i = 0; ext1_suffixes[i] != NULL; i++)
907     music_info->num_ext1_suffixes++;
908
909   music_info->num_ext2_suffixes = 0;
910   for (i = 0; ext2_suffixes[i] != NULL; i++)
911     music_info->num_ext2_suffixes++;
912
913   music_info->num_ext3_suffixes = 0;
914   for (i = 0; ext3_suffixes[i] != NULL; i++)
915     music_info->num_ext3_suffixes++;
916
917   music_info->num_ignore_tokens = 0;
918   for (i = 0; ignore_tokens[i] != NULL; i++)
919     music_info->num_ignore_tokens++;
920
921   music_info->base_prefixes = base_prefixes;
922   music_info->ext1_suffixes = ext1_suffixes;
923   music_info->ext2_suffixes = ext2_suffixes;
924   music_info->ext3_suffixes = ext3_suffixes;
925   music_info->ignore_tokens = ignore_tokens;
926
927   music_info->num_property_mapping_entries = 0;
928
929   music_info->property_mapping = NULL;
930
931   // ---------- initialize artwork reference and content lists ----------
932
933   music_info->sizeof_artwork_list_entry = sizeof(MusicInfo *);
934
935   music_info->artwork_list =
936     checked_calloc(num_file_list_entries * sizeof(MusicInfo *));
937   music_info->dynamic_artwork_list = NULL;
938
939   music_info->content_list = NULL;
940
941   // ---------- initialize artwork loading/freeing functions ----------
942
943   music_info->load_artwork = Load_WAV_or_MOD;
944   music_info->free_artwork = FreeMusic;
945 }
946
947 void PlayMusic(int nr)
948 {
949   if (!audio.music_available)
950     return;
951
952   PlaySoundMusic(nr);
953 }
954
955 void PlayMusicLoop(int nr)
956 {
957   if (!audio.music_available)
958     return;
959
960   PlaySoundMusicLoop(nr);
961 }
962
963 void PlaySound(int nr)
964 {
965   if (!setup.sound_simple)
966     return;
967
968   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_SOUND);
969 }
970
971 void PlaySoundStereo(int nr, int stereo_position)
972 {
973   if (!setup.sound_simple)
974     return;
975
976   PlaySoundExt(nr, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_SOUND);
977 }
978
979 void PlaySoundLoop(int nr)
980 {
981   if (!setup.sound_loops)
982     return;
983
984   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_LOOP);
985 }
986
987 void PlaySoundMusic(int nr)
988 {
989   if (!setup.sound_music)
990     return;
991
992   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_MUSIC);
993 }
994
995 void PlaySoundMusicLoop(int nr)
996 {
997   if (!setup.sound_music)
998     return;
999
1000   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_MUSIC_LOOP);
1001 }
1002
1003 void PlaySoundExt(int nr, int volume, int stereo_position, int state)
1004 {
1005   SoundControl snd_ctrl;
1006
1007   if (!audio.sound_available ||
1008       !audio.sound_enabled ||
1009       audio.sound_deactivated)
1010     return;
1011
1012   volume = SETUP_SOUND_VOLUME(volume, state);
1013
1014   if (volume < SOUND_MIN_VOLUME)
1015     volume = SOUND_MIN_VOLUME;
1016   else if (volume > SOUND_MAX_VOLUME)
1017     volume = SOUND_MAX_VOLUME;
1018
1019   if (stereo_position < SOUND_MAX_LEFT)
1020     stereo_position = SOUND_MAX_LEFT;
1021   else if (stereo_position > SOUND_MAX_RIGHT)
1022     stereo_position = SOUND_MAX_RIGHT;
1023
1024   clear_mem(&snd_ctrl, sizeof(SoundControl));   // to make valgrind happy
1025
1026   snd_ctrl.active = TRUE;
1027   snd_ctrl.nr = nr;
1028   snd_ctrl.volume = volume;
1029   snd_ctrl.stereo_position = stereo_position;
1030   snd_ctrl.state = state;
1031
1032   HandleSoundRequest(snd_ctrl);
1033 }
1034
1035 void FadeMusic(void)
1036 {
1037   if (!audio.music_available)
1038     return;
1039
1040   StopSoundExt(-1, SND_CTRL_FADE_MUSIC);
1041 }
1042
1043 void FadeSound(int nr)
1044 {
1045   StopSoundExt(nr, SND_CTRL_FADE_SOUND);
1046 }
1047
1048 void FadeSounds(void)
1049 {
1050   StopSoundExt(-1, SND_CTRL_FADE_ALL);
1051 }
1052
1053 void FadeSoundsAndMusic(void)
1054 {
1055   FadeSounds();
1056   FadeMusic();
1057 }
1058
1059 void StopMusic(void)
1060 {
1061   if (!audio.music_available)
1062     return;
1063
1064   StopSoundExt(-1, SND_CTRL_STOP_MUSIC);
1065 }
1066
1067 void StopSound(int nr)
1068 {
1069   StopSoundExt(nr, SND_CTRL_STOP_SOUND);
1070 }
1071
1072 void StopSounds(void)
1073 {
1074   StopMusic();
1075   StopSoundExt(-1, SND_CTRL_STOP_ALL);
1076 }
1077
1078 void StopSoundExt(int nr, int state)
1079 {
1080   SoundControl snd_ctrl;
1081
1082   if (!audio.sound_available)
1083     return;
1084
1085   clear_mem(&snd_ctrl, sizeof(SoundControl));   // to make valgrind happy
1086
1087   snd_ctrl.active = FALSE;
1088   snd_ctrl.nr = nr;
1089   snd_ctrl.state = state;
1090
1091   HandleSoundRequest(snd_ctrl);
1092 }
1093
1094 void ExpireSoundLoops(boolean active)
1095 {
1096   SoundControl snd_ctrl;
1097
1098   if (!audio.sound_available)
1099     return;
1100
1101   clear_mem(&snd_ctrl, sizeof(SoundControl));   // to make valgrind happy
1102
1103   snd_ctrl.active = active;
1104   snd_ctrl.state = SND_CTRL_EXPIRE_LOOPS;
1105
1106   HandleSoundRequest(snd_ctrl);
1107 }
1108
1109 static void ReloadCustomSounds(void)
1110 {
1111   LoadArtworkConfig(sound_info);
1112   ReloadCustomArtworkList(sound_info);
1113 }
1114
1115 static void ReloadCustomMusic(void)
1116 {
1117   LoadArtworkConfig(music_info);
1118   ReloadCustomArtworkList(music_info);
1119
1120   // load all music files from directory not defined in "musicinfo.conf"
1121   LoadCustomMusic_NoConf();
1122 }
1123
1124 void InitReloadCustomSounds(void)
1125 {
1126   if (!audio.sound_available)
1127     return;
1128
1129   ReloadCustomSounds();
1130 }
1131
1132 void InitReloadCustomMusic(void)
1133 {
1134   if (!audio.music_available)
1135     return;
1136
1137   ReloadCustomMusic();
1138 }
1139
1140 void FreeSound(void *ptr)
1141 {
1142   SoundInfo *sound = (SoundInfo *)ptr;
1143
1144   if (sound == NULL)
1145     return;
1146
1147   if (sound->data_ptr)
1148   {
1149     Mix_FreeChunk(sound->data_ptr);
1150   }
1151
1152   checked_free(sound->source_filename);
1153
1154   free(sound);
1155 }
1156
1157 void FreeMusic(void *ptr)
1158 {
1159   MusicInfo *music = (MusicInfo *)ptr;
1160
1161   if (music == NULL)
1162     return;
1163
1164   if (music->data_ptr)
1165   {
1166     if (music->type == MUS_TYPE_MOD)
1167       Mix_FreeMusic(music->data_ptr);
1168     else
1169       Mix_FreeChunk(music->data_ptr);
1170   }
1171
1172   free(music);
1173 }
1174
1175 static void FreeAllMusic_NoConf(void)
1176 {
1177   int i;
1178
1179   if (Music_NoConf == NULL)
1180     return;
1181
1182   for (i = 0; i < num_music_noconf; i++)
1183     FreeMusic(Music_NoConf[i]);
1184
1185   free(Music_NoConf);
1186
1187   Music_NoConf = NULL;
1188   num_music_noconf = 0;
1189 }
1190
1191 void FreeAllSounds(void)
1192 {
1193   FreeCustomArtworkLists(sound_info);
1194 }
1195
1196 void FreeAllMusic(void)
1197 {
1198   FreeCustomArtworkLists(music_info);
1199   FreeAllMusic_NoConf();
1200 }
1201
1202 // THE STUFF ABOVE IS ONLY USED BY THE MAIN PROCESS
1203 // ============================================================================