diff --git a/rwcore/loaders/LoaderSDT.cpp b/rwcore/loaders/LoaderSDT.cpp index dd65304b..8517e67f 100644 --- a/rwcore/loaders/LoaderSDT.cpp +++ b/rwcore/loaders/LoaderSDT.cpp @@ -6,9 +6,9 @@ #include "rw/debug.hpp" -bool LoaderSDT::load(const rwfs::path& path) { - const auto sdtName = path.string() + ".SDT"; - const auto rawName = path.string() + ".RAW"; +bool LoaderSDT::load(const rwfs::path& sdtPath, const rwfs::path& rawPath) { + const auto sdtName = sdtPath.string(); + const auto rawName = rawPath.string(); FILE* fp = fopen(sdtName.c_str(), "rb"); if (fp) { @@ -29,7 +29,7 @@ bool LoaderSDT::load(const rwfs::path& path) { m_archive = rawName; return true; } else { - RW_ERROR("Error cannot find " << path); + RW_ERROR("Error cannot open " << sdtName); return false; } } diff --git a/rwcore/loaders/LoaderSDT.hpp b/rwcore/loaders/LoaderSDT.hpp index db9fcec2..8cd27264 100644 --- a/rwcore/loaders/LoaderSDT.hpp +++ b/rwcore/loaders/LoaderSDT.hpp @@ -62,8 +62,7 @@ public: ~LoaderSDT() = default; /// Load the structure of the archive - /// Omit the extension in filename - bool load(const rwfs::path& path); + bool load(const rwfs::path& sdtPath, const rwfs::path& rawPath); /// Load a file from the archive to memory and pass a pointer to it /// Warning: Returns nullptr if by any reason it can't load the file diff --git a/rwengine/CMakeLists.txt b/rwengine/CMakeLists.txt index 523f1a2d..654a8638 100644 --- a/rwengine/CMakeLists.txt +++ b/rwengine/CMakeLists.txt @@ -18,6 +18,8 @@ set(RWENGINE_SOURCES src/audio/alCheck.cpp src/audio/alCheck.hpp + src/audio/SfxParameters.cpp + src/audio/SfxParameters.hpp src/audio/Sound.hpp src/audio/SoundBuffer.cpp src/audio/SoundBuffer.hpp diff --git a/rwengine/src/audio/SfxParameters.cpp b/rwengine/src/audio/SfxParameters.cpp new file mode 100644 index 00000000..c9998245 --- /dev/null +++ b/rwengine/src/audio/SfxParameters.cpp @@ -0,0 +1,160 @@ +#include "audio/SfxParameters.hpp" + +#include +#include +#include + +namespace { +/// Hardcoded array for translating script index into sfx index +/// Valid for gta3. +/// (In origin it also had been hardcoded) +/// (-1 means unlimited) +/// @todo some of them should return +/// random(?) number in range +/// see comment in second column +/// @todo: preparing data for VC and SA +constexpr SoundInstanceData sfxData[] = { + /// for opcode 018c + {78, 2857 /*-2871*/, -1}, /// male pain soft + {79, 2857 /*-2871*/, -1}, /// male pain loud + {80, 2290 /*-2300*/, -1}, /// female pain soft + {81, 2290 /*-2300*/, -1}, /// female pain loud + {82, 161 /*-162*/, -1}, /// item pickup + {83, 161 /*-162*/, -1}, /// item pickup + {92, 147, 40}, /// gate start + {93, 147, 40}, /// gate stop + {94, 337, -1}, /// checkpoint + {97, 336, -1}, /// countdown timer 3 + {98, 336, -1}, /// countdown timer 2 + {99, 336, -1}, /// countdown timer 1 + {100, 337, -1}, /// countdown finish + {106, 176, 50}, /// bullet hit ground + {107, 177, 50}, + {108, 178, 50}, + {110, 389, 80}, /// silent + {111, 389, 80}, + {112, 283, 80}, /// payphone + {114, 151, 60}, /// glass break + {115, 151, 60}, /// glass break + {116, 150, 60}, /// glass damage + {117, 152 /*-155*/, 55}, /// glass shards + {118, 327, 60}, /// boxes destroyed + {119, 328, 60}, /// boxes destroyed + {120, 140 /*-144*/, 60}, /// car collision + {121, 29, 60}, /// tire collision + {122, 167 /*-168*/, 20}, /// gun shell drop, + /// dependent on collision + /// the player is currently + /// standing on + {123, 168, 20}, /// gun shell drop soft + /// for opcode 018d + {4, 390, 30}, + {5, 390, 80}, /// Hepburn Heights + /// southwest tower + {6, 391, 30}, + {7, 391, 80}, /// Hepburn Heights + /// north tower + {8, 392, 30}, + {9, 392, 80}, + {10, 393, 30}, + {11, 393, 80}, + {12, 394, 30}, + {13, 394, 80}, + {14, 395, 30}, + {15, 395, 80}, + {16, 396, 30}, + {17, 396, 80}, + {18, 397, 30}, + {19, 397, 80}, + {20, 398, 30}, + {21, 398, 80}, + {22, 399, 30}, + {23, 399, 80}, + {24, 390, 30}, + {25, 390, 80}, + {26, 391, 30}, + {27, 391, 80}, + {28, 392, 30}, + {29, 392, 80}, + {30, 403, 30}, /// Meeouch Sex Kitten Club + {31, 403, 80}, + {32, 404, 30}, /// Sex Club Seven + {33, 404, 80}, + {34, 405, 30}, + {35, 405, 30}, + {36, 407 /*-408*/, 30}, /// 407 plays continuously + /// while 408 plays intermittently + {37, 407 /*-408*/, 30}, /// Liberty City + /// Sawmills/Bitch'N' Dog Food + {38, 409, 30}, + {39, 409, 80}, + {40, 410 /*-411*/, 30}, /// Both plays continuously + {41, 410 /*-411*/, 30}, /// Mr. Wong's Laundrette + {42, 412, 30}, /// Roast Peking Duck + {43, 412, 80}, + {44, 413, 30}, /// Cipriani's Ristorante + {45, 413, 80}, + {46, 414, 30}, + {47, 414, 30}, + {48, 415, 30}, /// Marco's Bistro (no sound) + {49, 415, 80}, + {50, 416 /*-419*/, 30}, /// Plays separately and intermittently + {51, 416 /*-419*/, 80}, /// Francis International Terminal B + {52, 420 /*-422*/, 30}, + {53, 420 /*-422*/, 30}, /// Chinatown (no sound) + {54, 423 /*-425*/, 30}, /// Plays separately and intermittently + {55, 423 /*-425*/, 80}, + {56, 426, 30}, + {57, 426, 80}, /// Portland Docks (no sound) + {58, 427 /*-431*/, 30}, /// Plays separately and intermittently + {59, 427 /*-431*/, 80}, /// Left side of Misty's apartment + {60, 406, 30}, /// Salvatore's place + {61, 406, 80}, + {62, 432 /*-434*/, 20}, /// South of Sex Club Seven + /// 432 plays continuously + /// while 433-434 + /// plays separately + /// and intermittently + {63, 432 /*-434*/, 80}, + {64, 435 /*-437*/, 20}, /// East of Portland + /// Pay 'N' Spray 435 plays + /// continuously while + /// 436-437 plays separately + /// and intermittently + {65, 435 /*-437*/, 80}, + {66, 438 /*-440*/, 20}, /// Executive Relief 438 + /// plays continuously while + /// 439-440 plays separately + /// and intermittently + {67, 438 /*-440*/, 80}, + {68, 442, 30}, + {69, 442, 80}, /// Bank of Liberty/Staunton + /// police HQ alarm + {70, 441, 30}, + {71, 441, 80}, /// Old school hall + {72, 443, 30}, + {73, 443, 80}, /// Warehouse rave + {76, 179 /*-184*/, 30}, /// Plays separately and intermittently + {77, 179 /*-184*/, 80}, /// Staunton police HQ + {84, 444, 30}, + {85, 444, 80}, + {86, 444, 30}, + {87, 444, 80}, + {88, 445, 30}, + {89, 445, 80}, + {90, 433 /*-434*/, 20}, /// Plays separately and intermittently + {91, 433 /*-434*/, 80}, /// Right side of Misty's apartment + {102, 157, 50} /// Callahan Bridge fire +}; +} // namespace + +const SoundInstanceData* getSoundInstanceData(int scriptId) { + const auto found = std::find_if(std::begin(sfxData), std::end(sfxData), + [&scriptId](const SoundInstanceData data) { + return data.id == scriptId; + }); + if (found != std::end(sfxData)) { + return found; + } + return std::begin(sfxData); +} diff --git a/rwengine/src/audio/SfxParameters.hpp b/rwengine/src/audio/SfxParameters.hpp new file mode 100644 index 00000000..4ea50150 --- /dev/null +++ b/rwengine/src/audio/SfxParameters.hpp @@ -0,0 +1,19 @@ +#ifndef _RWENGINE_SFX_PARAMETERS_HPP_ +#define _RWENGINE_SFX_PARAMETERS_HPP_ + +#include + +/// Script is using different numeration of sounds +/// than postion index in sfx file. +/// Also it is needed to store range of sound. +/// Struct is used by opcodes 018c, 018d. +struct SoundInstanceData { + int id; + int sfx; + int range; +}; + +/// Get metadata for selected script index +const SoundInstanceData* getSoundInstanceData(int scriptId); + +#endif diff --git a/rwengine/src/audio/SoundManager.cpp b/rwengine/src/audio/SoundManager.cpp index 862bbcb2..d2077b23 100644 --- a/rwengine/src/audio/SoundManager.cpp +++ b/rwengine/src/audio/SoundManager.cpp @@ -8,6 +8,9 @@ extern "C" { } #include "audio/alCheck.hpp" +#include "engine/GameData.hpp" +#include "engine/GameWorld.hpp" +#include "render/ViewCamera.hpp" #include @@ -25,7 +28,11 @@ Sound& SoundManager::getSoundRef(const std::string& name) { return sounds[name]; // @todo reloading, how to check is it wav/mp3? } -SoundManager::SoundManager() { +SoundManager::SoundManager(GameWorld* engine) : _engine(engine) { + auto sdtPath = _engine->data->index.findFilePath("audio/sfx.SDT"); + auto rawPath = _engine->data->index.findFilePath("audio/sfx.RAW"); + sdt.load(sdtPath, rawPath); + initializeOpenAL(); initializeAVCodec(); } @@ -71,6 +78,12 @@ bool SoundManager::initializeAVCodec() { av_log_set_level(AV_LOG_ERROR); #endif + // Some older versions of FFmpeg require it + // before calling avformat_alloc_context() +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) + av_register_all(); +#endif + return true; } @@ -97,7 +110,7 @@ bool SoundManager::loadSound(const std::string& name, return sound->isLoaded; } -void SoundManager::loadSfxSound(const rwfs::path& path, size_t index) { +void SoundManager::loadSound(size_t index) { Sound* sound = nullptr; auto emplaced = @@ -106,13 +119,19 @@ void SoundManager::loadSfxSound(const rwfs::path& path, size_t index) { sound = &emplaced.first->second; sound->source = std::make_shared(); - sound->source->loadSfx(path, sdt, index); + sound->source->loadSfx(sdt, index); } size_t SoundManager::createSfxInstance(size_t index) { Sound* sound = nullptr; auto soundRef = sfx.find(index); + if(soundRef == sfx.end()) { + // Sound source is not loaded yet + loadSound(index); + soundRef = sfx.find(index); + } + // Try to reuse first available buffer // (aka with stopped state) for (auto& sound : buffers) { @@ -264,17 +283,21 @@ void SoundManager::pause(bool p) { } } -void SoundManager::setListenerPosition(const glm::vec3& position) { - alListener3f(AL_POSITION, position.x, position.y, position.z); -} +void SoundManager::updateListenerTransform(const ViewCamera& cam) { + // Orientation + auto up = cam.rotation * glm::vec3(0.f, 0.f, 1.f); + auto at = cam.rotation * glm::vec3(1.f, 0.f, 0.f); + float orientation[6] = {at.x, at.y, at.z, up.x, up.y, up.z}; + alListenerfv(AL_ORIENTATION, orientation); -void SoundManager::setListenerVelocity(const glm::vec3& vel) { - alListener3f(AL_VELOCITY, vel.x, vel.y, vel.z); -} + // Position + float position[3] = {cam.position.x, cam.position.y, cam.position.z}; + alListenerfv(AL_POSITION, position); -void SoundManager::setListenerOrientation(const glm::vec3& at) { - float v[6] = {0, at.y, 0, 0, 0, at.z}; - alListenerfv(AL_ORIENTATION, v); + // @todo ShFil119 it should be implemented + // Velocity + // float velocity[3] = ... + // alListenerfv(AL_VELOCITY, velocity); } void SoundManager::setSoundPosition(const std::string& name, diff --git a/rwengine/src/audio/SoundManager.hpp b/rwengine/src/audio/SoundManager.hpp index 2b05b233..e9bdddec 100644 --- a/rwengine/src/audio/SoundManager.hpp +++ b/rwengine/src/audio/SoundManager.hpp @@ -18,6 +18,9 @@ #include #include +class GameWorld; +class ViewCamera; + /// Game's sound manager. /// It handles all stuff connected with sounds. /// Worth noted: there are three types of sounds, @@ -26,14 +29,14 @@ /// instances simultaneously without duplicating raw source). class SoundManager { public: - SoundManager(); + SoundManager(GameWorld* engine); ~SoundManager(); /// Load sound from file and store it with selected name bool loadSound(const std::string& name, const std::string& fileName); - /// Load all sfx sounds - void loadSfxSound(const rwfs::path& path, size_t index); + /// Load selected sfx sound + void loadSound(size_t index); Sound& getSoundRef(size_t name); Sound& getSoundRef(const std::string& name); @@ -73,15 +76,8 @@ public: void playMusic(const std::string& name); void stopMusic(const std::string& name); - /// Setting position of listener for openAL. - void setListenerPosition(const glm::vec3& position); - - /// Setting velocity of velocity for openAL. - void setListenerVelocity(const glm::vec3& vel); - - /// Setting orientation of listener for openAL. - /// Worth noted v = { at.x, at.y, at.z, up.x, up.y, up.z} - void setListenerOrientation(const glm::vec3& at); + /// Updating listener tranform, called by main loop of game. + void updateListenerTransform(const ViewCamera& cam); /// Setting position of sound source in buffer. void setSoundPosition(const std::string& name, const glm::vec3& position); @@ -105,6 +101,7 @@ private: /// Nr of already created buffers size_t bufferNr = 0; + GameWorld* _engine; LoaderSDT sdt{}; }; diff --git a/rwengine/src/audio/SoundSource.cpp b/rwengine/src/audio/SoundSource.cpp index bf7d3c06..3d8f6d2a 100644 --- a/rwengine/src/audio/SoundSource.cpp +++ b/rwengine/src/audio/SoundSource.cpp @@ -254,7 +254,7 @@ static int read_packet(void* opaque, uint8_t* buf, int buf_size) { return buf_size; } -void SoundSource::loadSfx(const rwfs::path& path, LoaderSDT& sdt, size_t index, bool asWave) { +void SoundSource::loadSfx(LoaderSDT& sdt, size_t index, bool asWave) { // Allocate audio frame AVFrame* frame = av_frame_alloc(); if (!frame) { @@ -262,9 +262,6 @@ void SoundSource::loadSfx(const rwfs::path& path, LoaderSDT& sdt, size_t index, return; } - - sdt.load(path / "audio/sfx"); - /// Now we need to prepare "custom" format context /// We need sdt loader for that purpose std::unique_ptr raw_sound = sdt.loadToMemory(index, asWave); diff --git a/rwengine/src/audio/SoundSource.hpp b/rwengine/src/audio/SoundSource.hpp index cbc71ef3..182f3ee4 100644 --- a/rwengine/src/audio/SoundSource.hpp +++ b/rwengine/src/audio/SoundSource.hpp @@ -16,7 +16,7 @@ public: void loadFromFile(const rwfs::path& filePath); /// Load sound from sdt file - void loadSfx(const rwfs::path& path, LoaderSDT& sdt, size_t index, bool asWave = true); + void loadSfx(LoaderSDT& sdt, size_t index, bool asWave = true); private: /// Raw data diff --git a/rwengine/src/engine/GameWorld.cpp b/rwengine/src/engine/GameWorld.cpp index ece8e574..e1799bb0 100644 --- a/rwengine/src/engine/GameWorld.cpp +++ b/rwengine/src/engine/GameWorld.cpp @@ -54,7 +54,7 @@ public: }; GameWorld::GameWorld(Logger* log, GameData* dat) - : logger(log), data(dat) { + : logger(log), data(dat), sound(this) { data->engine = this; collisionConfig = std::make_unique(); diff --git a/rwengine/src/script/ScriptTypes.cpp b/rwengine/src/script/ScriptTypes.cpp index 503478ab..4ad43f75 100644 --- a/rwengine/src/script/ScriptTypes.cpp +++ b/rwengine/src/script/ScriptTypes.cpp @@ -239,6 +239,15 @@ ScriptObjectType ScriptArguments::getScriptObject( } return {param.handleValue(), payphone}; } + +template <> +ScriptObjectType ScriptArguments::getScriptObject( + unsigned int arg) const { + auto& param = (*this)[arg]; + RW_CHECK(param.isLvalue(), "Non lvalue passed as object"); + return {param.handleValue(), &getWorld()->sound.getSoundRef(arg)}; +} + template <> ScriptObjectType ScriptArguments::getScriptObject( unsigned int arg) const { diff --git a/rwengine/src/script/ScriptTypes.hpp b/rwengine/src/script/ScriptTypes.hpp index 9f4b4b39..c2d25a2f 100644 --- a/rwengine/src/script/ScriptTypes.hpp +++ b/rwengine/src/script/ScriptTypes.hpp @@ -11,6 +11,7 @@ #include +#include "audio/Sound.hpp" #include "engine/Garage.hpp" class CharacterObject; @@ -109,9 +110,9 @@ struct BlipData; using ScriptVehicleGenerator = ScriptObjectType; using ScriptBlip = ScriptObjectType; using ScriptPayphone = ScriptObjectType; +using ScriptSound = ScriptObjectType; -/// @todo replace these with real types for sounds etc. -using ScriptSound = ScriptObjectType; +/// @todo replace these with real types using ScriptFire = ScriptObjectType; using ScriptSphere = ScriptObjectType; @@ -349,6 +350,9 @@ ScriptObjectType ScriptArguments::getScriptObject( template <> ScriptObjectType ScriptArguments::getScriptObject( unsigned int arg) const; +template <> +ScriptObjectType ScriptArguments::getScriptObject( + unsigned int arg) const; typedef std::function ScriptFunction; typedef std::function ScriptFunctionBoolean; diff --git a/rwengine/src/script/modules/GTA3ModuleImpl.inl b/rwengine/src/script/modules/GTA3ModuleImpl.inl index 8276df15..9990f030 100644 --- a/rwengine/src/script/modules/GTA3ModuleImpl.inl +++ b/rwengine/src/script/modules/GTA3ModuleImpl.inl @@ -1,20 +1,21 @@ -#include