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