1 /*
2 BStone: A Source port of
3 Blake Stone: Aliens of Gold and Blake Stone: Planet Strike
4 
5 Copyright (c) 1992-2013 Apogee Entertainment, LLC
6 Copyright (c) 2013-2015 Boris I. Bendovsky (bibendovsky@hotmail.com)
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the
20 Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 
24 
25 #ifndef BSTONE_AUDIO_MIXER_INCLUDED
26 #define BSTONE_AUDIO_MIXER_INCLUDED
27 
28 
29 #define BSTONE_AUDIO_MIXER_USE_THREAD (0)
30 
31 
32 #include <atomic>
33 #include <list>
34 #include <memory>
35 
36 #if BSTONE_AUDIO_MIXER_USE_THREAD
37 #include <mutex>
38 #endif // BSTONE_AUDIO_MIXER_USE_THREAD
39 
40 #include <thread>
41 #include <vector>
42 #include "SDL.h"
43 #include "bstone_atomic.h"
44 #include "bstone_audio_decoder.h"
45 #include "bstone_mt_queue_1r1w.h"
46 
47 
48 namespace bstone
49 {
50 
51 
52 enum ActorType
53 {
54     AT_NONE,
55     AT_ACTOR,
56     AT_DOOR,
57     AT_WALL
58 }; // ActorType
59 
60 enum ActorChannel
61 {
62     AC_VOICE,
63     AC_WEAPON,
64     AC_ITEM,
65     AC_HIT_WALL,
66     AC_NO_WAY,
67     AC_INTERROGATION,
68 }; // ActorChannel
69 
70 enum SoundType
71 {
72     ST_NONE,
73     ST_ADLIB_MUSIC,
74     ST_ADLIB_SFX,
75     ST_PCM
76 }; // SoundType
77 
78 
79 class AudioMixer
80 {
81 public:
82     AudioMixer();
83 
84     AudioMixer(
85         const AudioMixer& that) = delete;
86 
87     AudioMixer& operator=(
88         const AudioMixer& that) = delete;
89 
90     ~AudioMixer();
91 
92 
93     // Note: Mix size in milliseconds.
94     bool initialize(
95         int dst_rate,
96         int mix_size_ms);
97 
98     void uninitialize();
99 
100     bool is_initialized() const;
101 
102     bool play_adlib_music(
103         int music_index,
104         const void* data,
105         int data_size);
106 
107     // Negative index of an actor defines a non-positional sound.
108     bool play_adlib_sound(
109         int sound_index,
110         int priority,
111         const void* data,
112         int data_size,
113         int actor_index = -1,
114         ActorType actor_type = AT_NONE,
115         ActorChannel actor_channel = AC_VOICE);
116 
117     // Negative index of an actor defines a non-positional sound.
118     bool play_pcm_sound(
119         int sound_index,
120         int priority,
121         const void* data,
122         int data_size,
123         int actor_index = -1,
124         ActorType actor_type = AT_NONE,
125         ActorChannel actor_channel = AC_VOICE);
126 
127     bool update_positions();
128 
129     bool stop_music();
130 
131     bool stop_all_sfx();
132 
133     bool set_mute(
134         bool value);
135 
136     bool set_sfx_volume(
137         float volume);
138 
139     bool set_music_volume(
140         float volume);
141 
142     bool is_music_playing() const;
143 
144     bool is_any_sfx_playing() const;
145 
146     bool is_player_channel_playing(
147         ActorChannel channel) const;
148 
149     static int get_min_rate();
150 
151     static int get_default_rate();
152 
153     static int get_min_mix_size_ms();
154 
155     static int get_default_mix_size_ms();
156 
157     static int get_max_channels();
158 
159     static int get_max_commands();
160 
161 
162 private:
163     using Sample = int16_t;
164     using Samples = std::vector<Sample>;
165 
166     using MixSample = float;
167     using MixSamples = std::vector<MixSample>;
168 
169     class CacheItem
170     {
171     public:
172         bool is_active;
173         bool is_invalid;
174         SoundType sound_type;
175         int samples_count;
176         int decoded_count;
177         Samples samples;
178         std::unique_ptr<AudioDecoder> decoder;
179 
180         CacheItem();
181 
182         CacheItem(
183             const CacheItem& that);
184 
185         ~CacheItem();
186 
187         CacheItem& operator=(
188             const CacheItem& that);
189 
190         bool is_decoded() const;
191     }; // CacheItem
192 
193     using Cache = std::vector<CacheItem>;
194 
195     class Location
196     {
197     public:
198         Atomic<int> x;
199         Atomic<int> y;
200     }; // Location
201 
202     using Locations = std::vector<Location>;
203 
204     class PlayerLocation
205     {
206     public:
207         Atomic<int> view_x;
208         Atomic<int> view_y;
209         Atomic<int> view_cos;
210         Atomic<int> view_sin;
211     }; // PlayerLocation
212 
213     class Positions
214     {
215     public:
216         PlayerLocation player;
217         Locations actors;
218         Locations doors;
219         Location wall;
220 
221         void initialize();
222 
223         void fixed_copy_to(
224             Positions& positions);
225     }; // Positions
226 
227     class Sound
228     {
229     public:
230         SoundType type;
231         int priority;
232         CacheItem* cache;
233         int decode_offset;
234         int actor_index;
235         ActorType actor_type;
236         ActorChannel actor_channel;
237         float left_volume;
238         float right_volume;
239 
240         bool is_audible() const;
241     }; // Sound
242 
243     using Sounds = std::list<Sound>;
244 
245     enum CommandType
246     {
247         CMD_PLAY,
248         CMD_STOP_MUSIC,
249         CMD_STOP_ALL_SFX
250     }; // CommandType
251 
252     class Command
253     {
254     public:
255         CommandType command;
256         Sound sound;
257         const void* data;
258         int data_size;
259     }; // Command
260 
261     using Commands = bstone::MtQueue1R1W<Command>;
262 
263 #if BSTONE_AUDIO_MIXER_USE_THREAD
264     using Mutex = std::mutex;
265     using MutexGuard = std::lock_guard<Mutex>;
266 #endif // BSTONE_AUDIO_MIXER_USE_THREAD
267 
268     bool is_initialized_;
269     int dst_rate_;
270     SDL_AudioDeviceID device_id_;
271 
272 #if BSTONE_AUDIO_MIXER_USE_THREAD
273     Mutex mutex_;
274     std::thread thread_;
275 #endif // BSTONE_AUDIO_MIXER_USE_THREAD
276 
277     int mix_samples_count_;
278     Samples buffer_;
279     MixSamples mix_buffer_;
280     std::atomic_bool is_data_available_;
281 
282 #if BSTONE_AUDIO_MIXER_USE_THREAD
283     std::atomic_bool quit_thread_;
284 #endif // BSTONE_AUDIO_MIXER_USE_THREAD
285 
286     Sounds sounds_;
287     Commands commands_;
288     bool mute_;
289     Cache adlib_music_cache_;
290     Cache adlib_sfx_cache_;
291     Cache pcm_cache_;
292     Positions positions_;
293     std::atomic_int player_channels_state_;
294     std::atomic_bool is_music_playing_;
295     std::atomic_bool is_any_sfx_playing_;
296     std::atomic<float> sfx_volume_;
297     std::atomic<float> music_volume_;
298     int mix_size_ms_;
299 
300     void callback(
301         uint8_t* dst_data,
302         int dst_length);
303 
304     void mix();
305 
306     void mix_samples();
307 
308     void handle_commands();
309 
310     void handle_play_command(
311         const Command& command);
312 
313     void handle_stop_music_command();
314 
315     void handle_stop_all_sfx_command();
316 
317     bool initialize_cache_item(
318         const Command& command,
319         CacheItem& cache_item);
320 
321     bool decode_sound(
322         const Sound& sound);
323 
324     void spatialize_sound(
325         Sound& sound);
326 
327     void spatialize_sounds();
328 
329     bool play_sound(
330         SoundType sound_type,
331         int sound_index,
332         int priority,
333         const void* data,
334         int data_size,
335         int actor_index = -1,
336         ActorType actor_type = AT_NONE,
337         ActorChannel actor_channel = AC_VOICE);
338 
339     CacheItem* get_cache_item(
340         SoundType sound_type,
341         int sound_index);
342 
343     void set_player_channel_state(
344         const Sound& sound,
345         bool state);
346 
347     void lock();
348 
349     void unlock();
350 
351     static void callback_proxy(
352         void* user_data,
353         uint8_t* dst_data,
354         int dst_length);
355 
356     static int mix_proxy(
357         void* user_data);
358 
359     static int calculate_mix_samples_count(
360         int dst_rate,
361         int mix_size_ms);
362 
363     static AudioDecoder* create_decoder_by_sound_type(
364         SoundType sound_type);
365 
366     static bool is_sound_type_valid(
367         SoundType sound_type);
368 
369     static bool is_sound_index_valid(
370         int sound_index,
371         SoundType sound_type);
372 }; // AudioMixer
373 
374 
375 } // bstone
376 
377 
378 #endif // BSTONE_AUDIO_MIXER_INCLUDED
379