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