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