1 #include "AppHdr.h"
2 
3 #include "sound.h"
4 
5 #include "libutil.h"
6 #include "options.h"
7 #include "unicode.h"
8 
9 #ifdef USE_SOUND
10 
11 #if defined(WINMM_PLAY_SOUNDS)
12 #    include <windows.h>
13 #    ifndef SND_ASYNC
14 #        define SND_ASYNC 0x0001
15 #    endif
16 #    ifndef SND_NODEFAULT
17 #        define SND_NODEFAULT 0x0002
18 #    endif
19 #elif defined(USE_SDL)
20 #    ifdef __ANDROID__
21 #        include <SDL_mixer.h>
22 #    else
23 #        include <SDL2/SDL_mixer.h>
24 #    endif
25     Mix_Chunk* sdl_sound_to_play = nullptr;
26 #endif
27 
28 // Plays a sound based on the given message; it must match a regex pattern
29 // in an option file for a sound to be played.
parse_sound(const string & message)30 void parse_sound(const string& message)
31 {
32     play_sound(check_sound_patterns(message));
33 }
34 
35 // This function will return the sound_mapping it finds that matches
36 // the given string. If none is found, then a sound mapping with an empty
37 // string for the soundfile is returned.
38 //
39 // The rational for NOT playing sounds within this function is to enable
40 // conditional behaviour from the calling function, i.e. only do something
41 // when a sound mapping is found.
check_sound_patterns(const string & message)42 sound_mapping check_sound_patterns(const string& message)
43 {
44     sound_mapping matched_sound;
45 
46     matched_sound.pattern = "";
47     matched_sound.soundfile = "";
48     matched_sound.interrupt_game = false;
49 
50     for (const sound_mapping &sound : Options.sound_mappings)
51     {
52         // Maybe we should allow message channel matching as for
53         // force_more_message?
54         if (sound.pattern.matches(message))
55         {
56             //play_sound(sound.soundfile.c_str(), sound.interrupt_game);
57             matched_sound = sound;
58             break;
59         }
60     }
61 
62     return matched_sound;
63 }
64 
play_sound(sound_mapping sound_data)65 void play_sound(sound_mapping sound_data)
66 {
67     if (!sound_data.soundfile.empty())
68         play_sound(sound_data.soundfile.c_str(), sound_data.interrupt_game);
69 }
70 
71 // TODO: Make interrupt_game apply to any sound-playing method, not just SOUND_PLAY_COMMAND
72 // TODO: Add in-game option for disabling interrupt sounds (and sounds in general)
play_sound(const char * file,bool interrupt_game)73 void play_sound(const char *file, bool interrupt_game)
74 {
75     UNUSED(interrupt_game);
76 
77     if (Options.sounds_on)
78     {
79 #if defined(WINMM_PLAY_SOUNDS)
80         // Check whether file exists, is readable, etc.?
81         if (file && *file)
82             sndPlaySoundW(OUTW(file), SND_ASYNC | SND_NODEFAULT);
83 
84 #elif defined(SOUND_PLAY_COMMAND)
85         char command[255];
86         command[0] = 0;
87         if (file && *file && (strlen(file) + strlen(SOUND_PLAY_COMMAND) < 255)
88             && shell_safe(file))
89         {
90     #if defined(HOLD_SOUND_PLAY_COMMAND)
91             if (interrupt_game)
92                 snprintf(command, sizeof command, HOLD_SOUND_PLAY_COMMAND, file);
93             else
94     #endif
95                 snprintf(command, sizeof command, SOUND_PLAY_COMMAND, file);
96 
97             system(OUTS(command));
98         }
99 #elif defined(USE_SDL)
100         static int last_channel = -1;
101 
102         if (Options.one_SDL_sound_channel
103              && last_channel != -1
104              && Mix_Playing(last_channel))
105         {
106             Mix_HaltChannel(last_channel);
107         }
108         if (sdl_sound_to_play != nullptr)
109             Mix_FreeChunk(sdl_sound_to_play);
110 
111         sdl_sound_to_play = Mix_LoadWAV(OUTS(file));
112         last_channel = Mix_PlayChannel(-1, sdl_sound_to_play, 0);
113 #endif
114     } // End if (Options.sounds_on)
115 }
116 
117 #endif // USE_SOUND
118