1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-11-25 11:52:40 +01:00

Merge pull request #586 from ShFil119/sfx_script

Sfx - ScriptObject, metadata, opcodes, listener position
This commit is contained in:
Daniel Evans 2018-08-15 18:15:31 +01:00 committed by GitHub
commit ae0192333f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 275 additions and 63 deletions

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,160 @@
#include "audio/SfxParameters.hpp"
#include <algorithm>
#include <cstddef>
#include <iterator>
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);
}

View File

@ -0,0 +1,19 @@
#ifndef _RWENGINE_SFX_PARAMETERS_HPP_
#define _RWENGINE_SFX_PARAMETERS_HPP_
#include <cstddef>
/// 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

View File

@ -8,6 +8,9 @@ extern "C" {
}
#include "audio/alCheck.hpp"
#include "engine/GameData.hpp"
#include "engine/GameWorld.hpp"
#include "render/ViewCamera.hpp"
#include <rw/types.hpp>
@ -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<SoundSource>();
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,

View File

@ -18,6 +18,9 @@
#include <rw/filesystem.hpp>
#include <loaders/LoaderSDT.hpp>
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{};
};

View File

@ -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<char[]> raw_sound = sdt.loadToMemory(index, asWave);

View File

@ -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

View File

@ -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<btDefaultCollisionConfiguration>();

View File

@ -239,6 +239,15 @@ ScriptObjectType<Payphone> ScriptArguments::getScriptObject(
}
return {param.handleValue(), payphone};
}
template <>
ScriptObjectType<Sound> 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<BlipData> ScriptArguments::getScriptObject(
unsigned int arg) const {

View File

@ -11,6 +11,7 @@
#include <rw/debug.hpp>
#include "audio/Sound.hpp"
#include "engine/Garage.hpp"
class CharacterObject;
@ -109,9 +110,9 @@ struct BlipData;
using ScriptVehicleGenerator = ScriptObjectType<VehicleGenerator>;
using ScriptBlip = ScriptObjectType<BlipData>;
using ScriptPayphone = ScriptObjectType<Payphone>;
using ScriptSound = ScriptObjectType<Sound>;
/// @todo replace these with real types for sounds etc.
using ScriptSound = ScriptObjectType<int>;
/// @todo replace these with real types
using ScriptFire = ScriptObjectType<int>;
using ScriptSphere = ScriptObjectType<int>;
@ -349,6 +350,9 @@ ScriptObjectType<VehicleGenerator> ScriptArguments::getScriptObject(
template <>
ScriptObjectType<Garage> ScriptArguments::getScriptObject(
unsigned int arg) const;
template <>
ScriptObjectType<Sound> ScriptArguments::getScriptObject(
unsigned int arg) const;
typedef std::function<void(const ScriptArguments&)> ScriptFunction;
typedef std::function<bool(const ScriptArguments&)> ScriptFunctionBoolean;

View File

@ -1,20 +1,21 @@
#include <script/ScriptTypes.hpp>
#include <script/ScriptMachine.hpp>
#include <script/SCMFile.hpp>
#include <script/ScriptFunctions.hpp>
#include <engine/GameWorld.hpp>
#include <engine/GameState.hpp>
#include <engine/GameData.hpp>
#include <engine/Animator.hpp>
#include <ai/PlayerController.hpp>
#include <audio/SfxParameters.hpp>
#include <core/Logger.hpp>
#include <objects/CutsceneObject.hpp>
#include <data/CutsceneData.hpp>
#include <engine/Animator.hpp>
#include <engine/GameData.hpp>
#include <engine/GameState.hpp>
#include <engine/GameWorld.hpp>
#include <objects/CharacterObject.hpp>
#include <objects/CutsceneObject.hpp>
#include <objects/InstanceObject.hpp>
#include <objects/ObjectTypes.hpp>
#include <objects/PickupObject.hpp>
#include <objects/VehicleObject.hpp>
#include <ai/PlayerController.hpp>
#include <data/CutsceneData.hpp>
#include <script/SCMFile.hpp>
#include <script/ScriptFunctions.hpp>
#include <script/ScriptMachine.hpp>
#include <script/ScriptTypes.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -4280,10 +4281,10 @@ void opcode_018b(const ScriptArguments& args, const ScriptBlip blip, const Scrip
@arg sound
*/
void opcode_018c(const ScriptArguments& args, ScriptVec3 coord, const ScriptSoundType sound) {
RW_UNIMPLEMENTED_OPCODE(0x018c);
RW_UNUSED(coord);
RW_UNUSED(sound);
RW_UNUSED(args);
auto world = args.getWorld();
auto metaData = getSoundInstanceData(sound);
auto name = world->sound.createSfxInstance(metaData->sfx);
world->sound.playSfx(name, coord, false, metaData->range);
}
/**
@ -4295,11 +4296,11 @@ void opcode_018c(const ScriptArguments& args, ScriptVec3 coord, const ScriptSoun
@arg sound1
*/
void opcode_018d(const ScriptArguments& args, ScriptVec3 coord, const ScriptSoundType sound0, ScriptSound& sound1) {
RW_UNIMPLEMENTED_OPCODE(0x018d);
RW_UNUSED(coord);
RW_UNUSED(sound0);
RW_UNUSED(sound1);
RW_UNUSED(args);
auto world = args.getWorld();
auto metaData = getSoundInstanceData(sound0);
auto bufferName = world->sound.createSfxInstance(metaData->sfx);
world->sound.playSfx(bufferName, coord, true, metaData->range);
sound1 = &world->sound.getSoundRef(bufferName);
}
/**
@ -4309,9 +4310,8 @@ void opcode_018d(const ScriptArguments& args, ScriptVec3 coord, const ScriptSoun
@arg sound
*/
void opcode_018e(const ScriptArguments& args, const ScriptSound sound) {
RW_UNIMPLEMENTED_OPCODE(0x018e);
RW_UNUSED(sound);
RW_UNUSED(args);
sound->stop();
}
/**
@ -11329,9 +11329,9 @@ void opcode_03d6(const ScriptArguments& args, const ScriptString gxtEntry) {
@arg coord Coordinates
*/
void opcode_03d7(const ScriptArguments& args, ScriptVec3 coord) {
RW_UNIMPLEMENTED_OPCODE(0x03d7);
RW_UNUSED(coord);
RW_UNUSED(args);
auto world = args.getWorld();
auto& wav = world->sound.getSoundRef(world->missionAudio);
wav.setPosition(coord);
}
/**

View File

@ -604,6 +604,8 @@ void RWGame::render(float alpha, float time) {
viewCam.frustum.fov *= viewCam.frustum.aspectRatio;
}
world->sound.updateListenerTransform(viewCam);
glEnable(GL_DEPTH_TEST);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);