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