diff --git a/.gitattributes b/.gitattributes index be20bd9..aa2a309 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,4 @@ **/*.png filter=lfs diff=lfs merge=lfs -text **/*.ttf filter=lfs diff=lfs merge=lfs -text +**/*.wav filter=lfs diff=lfs merge=lfs -text +**/*.mp3 filter=lfs diff=lfs merge=lfs -text diff --git a/CMakeLists.txt b/CMakeLists.txt index ffcd097..4ea0a8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # Set the output directory for the build executables and libraries set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) +set(THREADS_PREFER_PTHREAD_FLAG ON) # Set the executable name set(EXECUTABLE wolfenstein) @@ -62,6 +68,7 @@ add_subdirectory(src/NavigationManager) add_subdirectory(src/ShootingManager) add_subdirectory(src/Strike) add_subdirectory(src/State) +add_subdirectory(src/SoundManager) add_subdirectory(src/TextureManager) add_subdirectory(src/TimeManager) add_subdirectory(src/Utility) diff --git a/assets/sounds/npc_attack.wav b/assets/sounds/npc_attack.wav new file mode 100644 index 0000000..f3923bd Binary files /dev/null and b/assets/sounds/npc_attack.wav differ diff --git a/assets/sounds/npc_death.wav b/assets/sounds/npc_death.wav new file mode 100644 index 0000000..1fa6dd3 Binary files /dev/null and b/assets/sounds/npc_death.wav differ diff --git a/assets/sounds/npc_pain.wav b/assets/sounds/npc_pain.wav new file mode 100644 index 0000000..799ab59 Binary files /dev/null and b/assets/sounds/npc_pain.wav differ diff --git a/assets/sounds/player_pain.wav b/assets/sounds/player_pain.wav new file mode 100644 index 0000000..6a6d22e Binary files /dev/null and b/assets/sounds/player_pain.wav differ diff --git a/assets/sounds/shotgun.wav b/assets/sounds/shotgun.wav new file mode 100644 index 0000000..74f6acd Binary files /dev/null and b/assets/sounds/shotgun.wav differ diff --git a/assets/sounds/theme.mp3 b/assets/sounds/theme.mp3 new file mode 100644 index 0000000..196bf2f Binary files /dev/null and b/assets/sounds/theme.mp3 differ diff --git a/src/SoundManager/CMakeLists.txt b/src/SoundManager/CMakeLists.txt new file mode 100644 index 0000000..1335401 --- /dev/null +++ b/src/SoundManager/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library( + sound_manager + STATIC + src/sound_manager.cpp +) +target_include_directories(sound_manager PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include +) +target_link_libraries( + sound_manager + PUBLIC + SDL2 + SDL2_mixer +) diff --git a/src/SoundManager/include/SoundManager/sound_manager.h b/src/SoundManager/include/SoundManager/sound_manager.h new file mode 100644 index 0000000..e1f7f8b --- /dev/null +++ b/src/SoundManager/include/SoundManager/sound_manager.h @@ -0,0 +1,53 @@ +/** + * @file sound_manager.h + * @author Bilal Kahraman (kahramannbilal@gmail.com) + * @brief + * @version 0.1 + * @date 2024-12-12 + * + * @copyright Copyright (c) 2024 + * + */ + +#ifndef SOUND_MANAGER_INCLUDE_SOUND_MANAGER_H +#define SOUND_MANAGER_INCLUDE_SOUND_MANAGER_H + +#include +#include +#include +#include +#include +#include + +namespace wolfenstein { + +class SoundManager +{ + + public: + static SoundManager& GetInstance(); + + SoundManager(const SoundManager&) = delete; + SoundManager& operator=(const SoundManager&) = delete; + ~SoundManager(); + + void InitManager(); + void PlayEffect(std::string requester_id, std::string sound_effect); + + private: + SoundManager() = default; + void LoadSound(const std::string sound_name, const std::string& sound_path); + void PlaySound(Mix_Chunk* sound, int channel = -1, int loops = 0); + Mix_Chunk* GetSound(std::string sound_name); + + static SoundManager* instance_; + bool initialized_{false}; + int channel_counter; + std::unordered_map chunks_; + std::unordered_map id_to_channel_; + Mix_Music* main_theme; +}; + +} // namespace wolfenstein + +#endif // SOUND_MANAGER_INCLUDE_SOUND_MANAGER_H diff --git a/src/SoundManager/src/sound_manager.cpp b/src/SoundManager/src/sound_manager.cpp new file mode 100644 index 0000000..2da1ef3 --- /dev/null +++ b/src/SoundManager/src/sound_manager.cpp @@ -0,0 +1,105 @@ +#include "SoundManager/sound_manager.h" +#include +#include +#include + +namespace wolfenstein { + +SoundManager* SoundManager::instance_ = nullptr; + +SoundManager& SoundManager::GetInstance() { + if (instance_ == nullptr) { + instance_ = new SoundManager(); + } + return *instance_; +} + +SoundManager::~SoundManager() { + for (auto chunk : chunks_) { + Mix_FreeChunk(chunk.second); + } + Mix_FreeMusic(main_theme); + Mix_CloseAudio(); + delete instance_; +} + +void SoundManager::InitManager() { + if (initialized_) { + return; + } + if (SDL_Init(SDL_INIT_AUDIO) != 0) { + SDL_Log("Unable to initialize SDL: %s", SDL_GetError()); + exit(EXIT_FAILURE); + } + + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) { + std::cerr << "Failed to initialize SDL_mixer: " << Mix_GetError() + << std::endl; + exit(EXIT_FAILURE); + } + + // Allocate channels (default is 8; increase if necessary) + Mix_AllocateChannels(16); + + std::string sound_path = std::string(RESOURCE_DIR) + "sounds/"; + LoadSound("npc_attack", sound_path + "npc_attack.wav"); + LoadSound("npc_pain", sound_path + "npc_pain.wav"); + LoadSound("npc_death", sound_path + "npc_death.wav"); + LoadSound("player_pain", sound_path + "player_pain.wav"); + LoadSound("shotgun", sound_path + "shotgun.wav"); + + Mix_VolumeChunk(GetSound("npc_attack"), 32); + Mix_VolumeChunk(GetSound("npc_pain"), 32); + Mix_VolumeChunk(GetSound("npc_death"), 64); + Mix_VolumeChunk(GetSound("player_pain"), 64); + Mix_VolumeChunk(GetSound("shotgun"), 64); + + std::string main_music = sound_path + "theme.mp3"; + main_theme = Mix_LoadMUS(main_music.c_str()); + Mix_VolumeMusic(64); + + // Play the MP3 file + if (Mix_PlayMusic(main_theme, -1) == -1) { + std::cerr << "Failed to play MP3 file: " << Mix_GetError() << std::endl; + exit(EXIT_FAILURE); + } + channel_counter = 0; + initialized_ = true; +} + +// keep track of the ids +void SoundManager::PlayEffect(std::string requester_id, + std::string sound_effect) { + auto channel = id_to_channel_.find(requester_id); + if (channel == id_to_channel_.end()) { + id_to_channel_.insert({requester_id, channel_counter++}); + PlaySound(GetSound(sound_effect), id_to_channel_[requester_id]); + } + else { + Mix_HaltChannel(channel->second); + PlaySound(GetSound(sound_effect), id_to_channel_[requester_id]); + } +} + +void SoundManager::LoadSound(const std::string sound_name, + const std::string& sound_path) { + Mix_Chunk* sound = Mix_LoadWAV(sound_path.c_str()); + if (!sound) { + std::cerr << "Failed to load WAV file: " << Mix_GetError() << std::endl; + return exit(EXIT_FAILURE); + } + chunks_.insert({sound_name, sound}); +} + +Mix_Chunk* SoundManager::GetSound(std::string sound_name) { + return chunks_[sound_name]; +} + +void SoundManager::PlaySound(Mix_Chunk* sound, int channel, int loops) { + // Play the sound on the specified channel with the specified number of loops + if (Mix_PlayChannel(channel, sound, loops) == -1) { + std::cerr << "Failed to play sound: " << Mix_GetError() << std::endl; + } +} + +} // namespace wolfenstein