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