added workaround for broken MIDI volume behaviour for Windows platform
[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 *getCurrentlyPlayingMusicFilename()
729 {
730   return currently_playing_music_filename;
731 }
732
733 int getSoundListPropertyMappingSize()
734 {
735   return sound_info->num_property_mapping_entries;
736 }
737
738 int getMusicListPropertyMappingSize()
739 {
740   return music_info->num_property_mapping_entries;
741 }
742
743 struct PropertyMapping *getSoundListPropertyMapping()
744 {
745   return sound_info->property_mapping;
746 }
747
748 struct PropertyMapping *getMusicListPropertyMapping()
749 {
750   return music_info->property_mapping;
751 }
752
753 void InitSoundList(struct ConfigInfo *config_list, int num_file_list_entries,
754                    struct ConfigTypeInfo *config_suffix_list,
755                    char **base_prefixes, char **ext1_suffixes,
756                    char **ext2_suffixes, char **ext3_suffixes,
757                    char **ignore_tokens)
758 {
759   int i;
760
761   sound_info = checked_calloc(sizeof(struct ArtworkListInfo));
762   sound_info->type = ARTWORK_TYPE_SOUNDS;
763
764   /* ---------- initialize file list and suffix lists ---------- */
765
766   sound_info->num_file_list_entries = num_file_list_entries;
767   sound_info->num_dynamic_file_list_entries = 0;
768
769   sound_info->file_list =
770     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
771                               num_file_list_entries);
772   sound_info->dynamic_file_list = NULL;
773
774   sound_info->num_suffix_list_entries = 0;
775   for (i = 0; config_suffix_list[i].token != NULL; i++)
776     sound_info->num_suffix_list_entries++;
777
778   sound_info->suffix_list = config_suffix_list;
779
780   /* ---------- initialize base prefix and suffixes lists ---------- */
781
782   sound_info->num_base_prefixes = 0;
783   for (i = 0; base_prefixes[i] != NULL; i++)
784     sound_info->num_base_prefixes++;
785
786   sound_info->num_ext1_suffixes = 0;
787   for (i = 0; ext1_suffixes[i] != NULL; i++)
788     sound_info->num_ext1_suffixes++;
789
790   sound_info->num_ext2_suffixes = 0;
791   for (i = 0; ext2_suffixes[i] != NULL; i++)
792     sound_info->num_ext2_suffixes++;
793
794   sound_info->num_ext3_suffixes = 0;
795   for (i = 0; ext3_suffixes[i] != NULL; i++)
796     sound_info->num_ext3_suffixes++;
797
798   sound_info->num_ignore_tokens = 0;
799   for (i = 0; ignore_tokens[i] != NULL; i++)
800     sound_info->num_ignore_tokens++;
801
802   sound_info->base_prefixes = base_prefixes;
803   sound_info->ext1_suffixes = ext1_suffixes;
804   sound_info->ext2_suffixes = ext2_suffixes;
805   sound_info->ext3_suffixes = ext3_suffixes;
806   sound_info->ignore_tokens = ignore_tokens;
807
808   sound_info->num_property_mapping_entries = 0;
809
810   sound_info->property_mapping = NULL;
811
812   /* ---------- initialize artwork reference and content lists ---------- */
813
814   sound_info->sizeof_artwork_list_entry = sizeof(SoundInfo *);
815
816   sound_info->artwork_list =
817     checked_calloc(num_file_list_entries * sizeof(SoundInfo *));
818   sound_info->dynamic_artwork_list = NULL;
819
820   sound_info->content_list = NULL;
821
822   /* ---------- initialize artwork loading/freeing functions ---------- */
823
824   sound_info->load_artwork = Load_WAV;
825   sound_info->free_artwork = FreeSound;
826 }
827
828 void InitMusicList(struct ConfigInfo *config_list, int num_file_list_entries,
829                    struct ConfigTypeInfo *config_suffix_list,
830                    char **base_prefixes, char **ext1_suffixes,
831                    char **ext2_suffixes, char **ext3_suffixes,
832                    char **ignore_tokens)
833 {
834   int i;
835
836   music_info = checked_calloc(sizeof(struct ArtworkListInfo));
837   music_info->type = ARTWORK_TYPE_MUSIC;
838
839   /* ---------- initialize file list and suffix lists ---------- */
840
841   music_info->num_file_list_entries = num_file_list_entries;
842   music_info->num_dynamic_file_list_entries = 0;
843
844   music_info->file_list =
845     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
846                               num_file_list_entries);
847   music_info->dynamic_file_list = NULL;
848
849   music_info->num_suffix_list_entries = 0;
850   for (i = 0; config_suffix_list[i].token != NULL; i++)
851     music_info->num_suffix_list_entries++;
852
853   music_info->suffix_list = config_suffix_list;
854
855   /* ---------- initialize base prefix and suffixes lists ---------- */
856
857   music_info->num_base_prefixes = 0;
858   for (i = 0; base_prefixes[i] != NULL; i++)
859     music_info->num_base_prefixes++;
860
861   music_info->num_ext1_suffixes = 0;
862   for (i = 0; ext1_suffixes[i] != NULL; i++)
863     music_info->num_ext1_suffixes++;
864
865   music_info->num_ext2_suffixes = 0;
866   for (i = 0; ext2_suffixes[i] != NULL; i++)
867     music_info->num_ext2_suffixes++;
868
869   music_info->num_ext3_suffixes = 0;
870   for (i = 0; ext3_suffixes[i] != NULL; i++)
871     music_info->num_ext3_suffixes++;
872
873   music_info->num_ignore_tokens = 0;
874   for (i = 0; ignore_tokens[i] != NULL; i++)
875     music_info->num_ignore_tokens++;
876
877   music_info->base_prefixes = base_prefixes;
878   music_info->ext1_suffixes = ext1_suffixes;
879   music_info->ext2_suffixes = ext2_suffixes;
880   music_info->ext3_suffixes = ext3_suffixes;
881   music_info->ignore_tokens = ignore_tokens;
882
883   music_info->num_property_mapping_entries = 0;
884
885   music_info->property_mapping = NULL;
886
887   /* ---------- initialize artwork reference and content lists ---------- */
888
889   music_info->sizeof_artwork_list_entry = sizeof(MusicInfo *);
890
891   music_info->artwork_list =
892     checked_calloc(num_file_list_entries * sizeof(MusicInfo *));
893   music_info->dynamic_artwork_list = NULL;
894
895   music_info->content_list = NULL;
896
897   /* ---------- initialize artwork loading/freeing functions ---------- */
898
899   music_info->load_artwork = Load_WAV_or_MOD;
900   music_info->free_artwork = FreeMusic;
901 }
902
903 void PlayMusic(int nr)
904 {
905   if (!audio.music_available)
906     return;
907
908   PlaySoundMusic(nr);
909 }
910
911 void PlaySound(int nr)
912 {
913   if (!setup.sound_simple)
914     return;
915
916   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_SOUND);
917 }
918
919 void PlaySoundStereo(int nr, int stereo_position)
920 {
921   if (!setup.sound_simple)
922     return;
923
924   PlaySoundExt(nr, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_SOUND);
925 }
926
927 void PlaySoundLoop(int nr)
928 {
929   if (!setup.sound_loops)
930     return;
931
932   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_LOOP);
933 }
934
935 void PlaySoundMusic(int nr)
936 {
937   if (!setup.sound_music)
938     return;
939
940   PlaySoundExt(nr, SOUND_MAX_VOLUME, SOUND_MIDDLE, SND_CTRL_PLAY_MUSIC);
941 }
942
943 void PlaySoundExt(int nr, int volume, int stereo_position, int state)
944 {
945   SoundControl snd_ctrl;
946
947   if (!audio.sound_available ||
948       !audio.sound_enabled ||
949       audio.sound_deactivated)
950     return;
951
952   volume = SETUP_SOUND_VOLUME(volume, state);
953
954   if (volume < SOUND_MIN_VOLUME)
955     volume = SOUND_MIN_VOLUME;
956   else if (volume > SOUND_MAX_VOLUME)
957     volume = SOUND_MAX_VOLUME;
958
959   if (stereo_position < SOUND_MAX_LEFT)
960     stereo_position = SOUND_MAX_LEFT;
961   else if (stereo_position > SOUND_MAX_RIGHT)
962     stereo_position = SOUND_MAX_RIGHT;
963
964   clear_mem(&snd_ctrl, sizeof(SoundControl));   /* to make valgrind happy */
965
966   snd_ctrl.active = TRUE;
967   snd_ctrl.nr = nr;
968   snd_ctrl.volume = volume;
969   snd_ctrl.stereo_position = stereo_position;
970   snd_ctrl.state = state;
971
972   HandleSoundRequest(snd_ctrl);
973 }
974
975 void FadeMusic(void)
976 {
977   if (!audio.music_available)
978     return;
979
980   StopSoundExt(-1, SND_CTRL_FADE_MUSIC);
981 }
982
983 void FadeSound(int nr)
984 {
985   StopSoundExt(nr, SND_CTRL_FADE_SOUND);
986 }
987
988 void FadeSounds()
989 {
990   StopSoundExt(-1, SND_CTRL_FADE_ALL);
991 }
992
993 void FadeSoundsAndMusic()
994 {
995   FadeSounds();
996   FadeMusic();
997 }
998
999 void StopMusic(void)
1000 {
1001   if (!audio.music_available)
1002     return;
1003
1004   StopSoundExt(-1, SND_CTRL_STOP_MUSIC);
1005 }
1006
1007 void StopSound(int nr)
1008 {
1009   StopSoundExt(nr, SND_CTRL_STOP_SOUND);
1010 }
1011
1012 void StopSounds()
1013 {
1014   StopMusic();
1015   StopSoundExt(-1, SND_CTRL_STOP_ALL);
1016 }
1017
1018 void StopSoundExt(int nr, int state)
1019 {
1020   SoundControl snd_ctrl;
1021
1022   if (!audio.sound_available)
1023     return;
1024
1025   clear_mem(&snd_ctrl, sizeof(SoundControl));   /* to make valgrind happy */
1026
1027   snd_ctrl.active = FALSE;
1028   snd_ctrl.nr = nr;
1029   snd_ctrl.state = state;
1030
1031   HandleSoundRequest(snd_ctrl);
1032 }
1033
1034 void ExpireSoundLoops(boolean active)
1035 {
1036   SoundControl snd_ctrl;
1037
1038   if (!audio.sound_available)
1039     return;
1040
1041   clear_mem(&snd_ctrl, sizeof(SoundControl));   /* to make valgrind happy */
1042
1043   snd_ctrl.active = active;
1044   snd_ctrl.state = SND_CTRL_EXPIRE_LOOPS;
1045
1046   HandleSoundRequest(snd_ctrl);
1047 }
1048
1049 static void ReloadCustomSounds()
1050 {
1051   LoadArtworkConfig(sound_info);
1052   ReloadCustomArtworkList(sound_info);
1053 }
1054
1055 static void ReloadCustomMusic()
1056 {
1057   LoadArtworkConfig(music_info);
1058   ReloadCustomArtworkList(music_info);
1059
1060   /* load all music files from directory not defined in "musicinfo.conf" */
1061   LoadCustomMusic_NoConf();
1062 }
1063
1064 void InitReloadCustomSounds(char *set_identifier)
1065 {
1066   if (!audio.sound_available)
1067     return;
1068
1069   ReloadCustomSounds();
1070 }
1071
1072 void InitReloadCustomMusic(char *set_identifier)
1073 {
1074   if (!audio.music_available)
1075     return;
1076
1077   ReloadCustomMusic();
1078 }
1079
1080 void FreeSound(void *ptr)
1081 {
1082   SoundInfo *sound = (SoundInfo *)ptr;
1083
1084   if (sound == NULL)
1085     return;
1086
1087   if (sound->data_ptr)
1088   {
1089     Mix_FreeChunk(sound->data_ptr);
1090   }
1091
1092   checked_free(sound->source_filename);
1093
1094   free(sound);
1095 }
1096
1097 void FreeMusic(void *ptr)
1098 {
1099   MusicInfo *music = (MusicInfo *)ptr;
1100
1101   if (music == NULL)
1102     return;
1103
1104   if (music->data_ptr)
1105   {
1106     if (music->type == MUS_TYPE_MOD)
1107       Mix_FreeMusic(music->data_ptr);
1108     else
1109       Mix_FreeChunk(music->data_ptr);
1110   }
1111
1112   free(music);
1113 }
1114
1115 static void FreeAllMusic_NoConf()
1116 {
1117   int i;
1118
1119   if (Music_NoConf == NULL)
1120     return;
1121
1122   for (i = 0; i < num_music_noconf; i++)
1123     FreeMusic(Music_NoConf[i]);
1124
1125   free(Music_NoConf);
1126
1127   Music_NoConf = NULL;
1128   num_music_noconf = 0;
1129 }
1130
1131 void FreeAllSounds()
1132 {
1133   FreeCustomArtworkLists(sound_info);
1134 }
1135
1136 void FreeAllMusic()
1137 {
1138   FreeCustomArtworkLists(music_info);
1139   FreeAllMusic_NoConf();
1140 }
1141
1142 /* THE STUFF ABOVE IS ONLY USED BY THE MAIN PROCESS                          */
1143 /* ========================================================================= */