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