diff --git a/src/xrSound/SoundRender_Core.cpp b/src/xrSound/SoundRender_Core.cpp index 095a134fd4f..61cc3e5f29d 100644 --- a/src/xrSound/SoundRender_Core.cpp +++ b/src/xrSound/SoundRender_Core.cpp @@ -45,6 +45,71 @@ void CSoundRender_Core::_initialize() bPresent = true; +#ifdef USE_PHONON + if (supports_float_pcm && psSoundFlags.test(ss_UseFloat32) && psSoundFlags.test(ss_EFX)) + { + IPLSIMDLevel simdLevel = IPL_SIMDLEVEL_SSE2; + if (CPU::HasAVX512F) + simdLevel = IPL_SIMDLEVEL_AVX512; + else if (CPU::HasAVX2) + simdLevel = IPL_SIMDLEVEL_AVX2; + else if (CPU::HasAVX) + simdLevel = IPL_SIMDLEVEL_AVX; + else if (CPU::HasSSE42) + simdLevel = IPL_SIMDLEVEL_SSE4; + + const IPLContextFlags flags + { + strstr(Core.Params, "-steamaudio_validate") + ? IPL_CONTEXTFLAGS_VALIDATION + : IPLContextFlags{} + }; + + IPLContextSettings contextSettings + { + STEAMAUDIO_VERSION, + [](IPLLogLevel level, const char* message) + { + // These warnings are incorrect, values are correct. + if (0 == xr_strcmp(message, "Warning: setInputs: invalid IPLfloat32: (&inputs->directivity)->dipoleWeight = 0.000000\n")) + return; + if (0 == xr_strcmp(message, "Warning: apply: invalid IPLTransmissionType: params->flags = 31\n")) + return; + + char mark = '\0'; + switch (level) + { + case IPL_LOGLEVEL_INFO: mark = '*'; break; + case IPL_LOGLEVEL_WARNING: mark = '~'; break; + case IPL_LOGLEVEL_ERROR: mark = '!'; break; + case IPL_LOGLEVEL_DEBUG: mark = '#'; break; + } + Msg("%c SOUND: SteamAudio: %s", mark, message); + }, + [](IPLsize size, IPLsize alignment) + { + return Memory.mem_alloc(size, alignment); + }, + [](void* memoryBlock) + { + Memory.mem_free(memoryBlock); + }, + simdLevel, + flags, + }; + + iplContextCreate(&contextSettings, &m_ipl_context); + IPLHRTFSettings hrtfSettings + { + IPL_HRTFTYPE_DEFAULT, + nullptr, nullptr, 0, + 1.0f, IPL_HRTFNORMTYPE_NONE + }; + + m_ipl_settings = { 48000, 19200 }; + iplHRTFCreate(m_ipl_context, &m_ipl_settings, &hrtfSettings, &m_ipl_hrtf); + } +#endif bReady = true; } @@ -52,6 +117,13 @@ void CSoundRender_Core::_clear() { bReady = false; +#ifdef USE_PHONON + if (m_ipl_hrtf) + iplHRTFRelease(&m_ipl_hrtf); + if (m_ipl_context) + iplContextRelease(&m_ipl_context); +#endif + // remove sources for (auto& kv : s_sources) { @@ -187,6 +259,31 @@ void CSoundRender_Core::update_listener(const Fvector& P, const Fvector& D, cons if (!psSoundFlags.test(ss_EFX) || !bListenerMoved) return; +#ifdef USE_PHONON + if (m_ipl_context) + { + const IPLCoordinateSpace3 listenerCoordinates + { + reinterpret_cast(R), + reinterpret_cast(N), + reinterpret_cast(D), + reinterpret_cast(P) + }; // the world-space position and orientation of the listener + + IPLSimulationSharedInputs sharedInputs + { + listenerCoordinates, + 64, 8, + 2.0f, 1, + 1.0f, + nullptr, nullptr + }; + + for (const auto scene : m_scenes) + iplSimulatorSetSharedInputs(scene->ipl_simulator(), IPL_SIMULATIONFLAGS_DIRECT, &sharedInputs); + } +#endif + bListenerMoved = false; } diff --git a/src/xrSound/SoundRender_Core.h b/src/xrSound/SoundRender_Core.h index 3273a1501ed..35128237ccd 100644 --- a/src/xrSound/SoundRender_Core.h +++ b/src/xrSound/SoundRender_Core.h @@ -72,6 +72,12 @@ class CSoundRender_Core : public ISoundManager u32 s_emitters_u; // emitter update marker xr_vector s_targets; +#ifdef USE_PHONON + IPLContext m_ipl_context{}; + IPLHRTF m_ipl_hrtf{}; + IPLAudioSettings m_ipl_settings{}; +#endif + public: bool supports_float_pcm{}; @@ -111,6 +117,13 @@ class CSoundRender_Core : public ISoundManager void refresh_sources() override; +#ifdef USE_PHONON + [[nodiscard]] + auto ipl_context() const { return m_ipl_context; } + const auto& ipl_settings() const { return m_ipl_settings; } + auto ipl_hrtf() const { return m_ipl_hrtf; } +#endif + public: CSoundRender_Source* i_create_source(pcstr name); diff --git a/src/xrSound/SoundRender_Core_Processor.cpp b/src/xrSound/SoundRender_Core_Processor.cpp index 98db8840259..e7ed71b05a5 100644 --- a/src/xrSound/SoundRender_Core_Processor.cpp +++ b/src/xrSound/SoundRender_Core_Processor.cpp @@ -30,6 +30,9 @@ void CSoundRender_Core::update(const Fvector& P, const Fvector& D, const Fvector } s_emitters_u++; + // update listener + update_listener(P, D, N, R, fTimer_Delta); + const auto update_emitter = [this](CSoundRender_Emitter* emitter) { const bool ignore = emitter->bIgnoringTimeFactor; @@ -69,9 +72,6 @@ void CSoundRender_Core::update(const Fvector& P, const Fvector& D, const Fvector } } - // update listener - update_listener(P, D, N, R, fTimer_Delta); - // Events for (CSoundRender_Scene* scene : m_scenes) scene->update(); diff --git a/src/xrSound/SoundRender_Emitter.cpp b/src/xrSound/SoundRender_Emitter.cpp index fba33c11b81..5a8f5ac303a 100644 --- a/src/xrSound/SoundRender_Emitter.cpp +++ b/src/xrSound/SoundRender_Emitter.cpp @@ -12,7 +12,11 @@ extern float psSoundVEffects; void CSoundRender_Emitter::set_position(const Fvector& pos) { - if (source()->channels_num() == 1) + if (source()->channels_num() == 1 +#ifdef USE_PHONON + || m_ipl_source +#endif + ) p_source.position = pos; else p_source.position.set(0, 0, 0); @@ -43,10 +47,24 @@ CSoundRender_Emitter::CSoundRender_Emitter(CSoundRender_Scene* s) fade_volume(1.f), m_current_state(stStopped), bMoved(true), - marker(0xabababab) {} + marker(0xabababab) +{ +#ifdef USE_PHONON + if (const auto simulator = scene->ipl_simulator()) + { + IPLSourceSettings sourceSettings{ IPL_SIMULATIONFLAGS_DIRECT }; + iplSourceCreate(simulator, &sourceSettings, &m_ipl_source); + } +#endif +} CSoundRender_Emitter::~CSoundRender_Emitter() { +#ifdef USE_PHONON + if (m_ipl_source) + iplSourceRelease(&m_ipl_source); +#endif + // try to release dependencies, events, for example Event_ReleaseOwner(); wait_prefill(); @@ -143,7 +161,7 @@ void CSoundRender_Emitter::move_cursor(int offset) set_cursor(get_cursor(true) + offset); } -void CSoundRender_Emitter::fill_data(void* dest, u32 offset, u32 size) const +void CSoundRender_Emitter::fill_data(void* dest, u32 offset, u32 size) { source()->decompress(dest, offset, size, ovf); } @@ -237,6 +255,25 @@ std::pair CSoundRender_Emitter::obtain_block() if (current_block >= sdef_target_count_prefill) current_block = 0; --filled_blocks; +#ifdef USE_PHONON + if (psSoundFlags.test(ss_EFX) && scene->ipl_scene_mesh() && !is_2D()) + { + const auto context = SoundRender->ipl_context(); + + IPLSimulationOutputs outputs{}; + outputs.direct.flags = static_cast( + IPL_DIRECTEFFECTFLAGS_APPLYAIRABSORPTION | + IPL_DIRECTEFFECTFLAGS_APPLYDIRECTIVITY | + IPL_DIRECTEFFECTFLAGS_APPLYOCCLUSION | + IPL_DIRECTEFFECTFLAGS_APPLYTRANSMISSION + ); + iplSourceGetOutputs(m_ipl_source, IPL_SIMULATIONFLAGS_DIRECT, &outputs); + + iplAudioBufferDeinterleave(context, (float*)result.first, &ipl_buffers.direct_input); + iplDirectEffectApply(ipl_effects.direct, &outputs.direct, &ipl_buffers.direct_input, &ipl_buffers.direct_output); + iplAudioBufferInterleave(context, &ipl_buffers.direct_output, (float*)result.first); + } +#endif return std::move(result); } diff --git a/src/xrSound/SoundRender_Emitter.h b/src/xrSound/SoundRender_Emitter.h index ccb2347bd67..f841f27f470 100644 --- a/src/xrSound/SoundRender_Emitter.h +++ b/src/xrSound/SoundRender_Emitter.h @@ -39,6 +39,26 @@ class CSoundRender_Emitter final : public CSound_emitter [[nodiscard]] CSoundRender_Source* source() const { return (CSoundRender_Source*)owner_data->handle; } +#ifdef USE_PHONON +private: + IPLSource m_ipl_source{}; + + struct + { + IPLDirectEffect direct{}; + IPLReflectionEffect reflection{}; + IPLPathEffect path{}; + } ipl_effects{}; + struct + { + IPLAudioBuffer direct_input{}; + IPLAudioBuffer direct_output{}; + } ipl_buffers{}; + +public: + auto ipl_source() const { return m_ipl_source; } +#endif + [[nodiscard]] u32 get_bytes_total() const; [[nodiscard]] @@ -85,7 +105,7 @@ class CSoundRender_Emitter final : public CSound_emitter int filled_blocks{}; void fill_block(void* ptr, u32 size); - void fill_data(void* dest, u32 offset, u32 size) const; + void fill_data(void* dest, u32 offset, u32 size); void fill_all_blocks(); void dispatch_prefill(); diff --git a/src/xrSound/SoundRender_Emitter_FSM.cpp b/src/xrSound/SoundRender_Emitter_FSM.cpp index 37a3eba1972..ddc9c04fe75 100644 --- a/src/xrSound/SoundRender_Emitter_FSM.cpp +++ b/src/xrSound/SoundRender_Emitter_FSM.cpp @@ -283,6 +283,51 @@ void CSoundRender_Emitter::update(float fTime, float dt) owner_data->feedback = 0; owner_data = 0; } + +#ifdef USE_PHONON + if (m_ipl_source && !bStopping) + { + const auto& listener = SoundRender->listener_params(); + + const IPLCoordinateSpace3 sourceCoordinates + { + reinterpret_cast(listener.orientation[2]), + reinterpret_cast(listener.orientation[1]), + reinterpret_cast(listener.orientation[0]), + reinterpret_cast(p_source.position) + }; + IPLSimulationInputs inputs + { + /*.flags =*/ IPL_SIMULATIONFLAGS_DIRECT, + /*.directFlags =*/ static_cast( + IPL_DIRECTSIMULATIONFLAGS_AIRABSORPTION | + IPL_DIRECTSIMULATIONFLAGS_DIRECTIVITY | + IPL_DIRECTSIMULATIONFLAGS_OCCLUSION | + IPL_DIRECTSIMULATIONFLAGS_TRANSMISSION), + /*.source =*/ sourceCoordinates, + /*.distanceAttenuationModel =*/ {}, + /*.airAbsorptionModel =*/ {}, + /*.directivity =*/ {}, + /*.occlusionType =*/ IPL_OCCLUSIONTYPE_VOLUMETRIC, + /*.occlusionRadius =*/ p_source.max_distance, + /*.numOcclusionSamples =*/ 16, + /*.reverbScale =*/ { 1.0f, 1.0f, 1.0f }, + /*.hybridReverbTransitionTime =*/ 1.0f, + /*.hybridReverbOverlapPercent =*/ 0.25f, + /*.baked =*/ IPL_FALSE, + /*.bakedDataIdentifier =*/ {}, + /*.pathingProbes =*/ nullptr/*scene->ipl_scene_probes*/, + /*.visRadius =*/ 1.0f, + /*.visThreshold =*/ 0.1f, + /*.visRange =*/ p_source.max_distance, + /*.pathingOrder =*/ 1, + /*.enableValidation =*/ IPL_FALSE, + /*.findAlternatePaths =*/ IPL_FALSE, + /*.numTransmissionRays =*/ 2, + }; + iplSourceSetInputs(m_ipl_source, IPL_SIMULATIONFLAGS_DIRECT, &inputs); + } +#endif } IC void volume_lerp(float& c, float t, float s, float dt) diff --git a/src/xrSound/SoundRender_Emitter_StartStop.cpp b/src/xrSound/SoundRender_Emitter_StartStop.cpp index fe1746599a2..a9001e91861 100644 --- a/src/xrSound/SoundRender_Emitter_StartStop.cpp +++ b/src/xrSound/SoundRender_Emitter_StartStop.cpp @@ -37,15 +37,52 @@ void CSoundRender_Emitter::start(const ref_sound& _owner, u32 flags, float delay bStopping = FALSE; bRewind = FALSE; + const auto data_info = source()->data_info(); + // Calc storage for (auto& buf : temp_buf) - buf.resize(source()->data_info().bytesPerBuffer); + buf.resize(data_info.bytesPerBuffer); ovf = source()->open(); + +#ifdef USE_PHONON + if (const auto simulator = scene->ipl_simulator()) + { + iplSourceAdd(m_ipl_source, simulator); + + const auto context = SoundRender->ipl_context(); + auto& settings = source()->ipl_audio_settings(); + + IPLDirectEffectSettings direct{ data_info.channels }; + iplDirectEffectCreate(context, &settings, &direct, &ipl_effects.direct); + + IPLReflectionEffectSettings refl{ IPL_REFLECTIONEFFECTTYPE_CONVOLUTION, settings.frameSize * 2, 4 }; + iplReflectionEffectCreate(context, &settings, &refl, &ipl_effects.reflection); + + IPLPathEffectSettings path{ 1, IPL_TRUE, {}, SoundRender->ipl_hrtf() }; + iplPathEffectCreate(context, &settings, &path, &ipl_effects.path); + + iplAudioBufferAllocate(context, data_info.channels, settings.frameSize, &ipl_buffers.direct_input); + iplAudioBufferAllocate(context, data_info.channels, settings.frameSize, &ipl_buffers.direct_output); + } +#endif } void CSoundRender_Emitter::i_stop() { +#ifdef USE_PHONON + if (const auto context = SoundRender->ipl_context()) + { + iplSourceRemove(m_ipl_source, scene->ipl_simulator()); + + iplDirectEffectRelease(&ipl_effects.direct); + iplReflectionEffectRelease(&ipl_effects.reflection); + iplPathEffectRelease(&ipl_effects.path); + + iplAudioBufferFree(context, &ipl_buffers.direct_output); + iplAudioBufferFree(context, &ipl_buffers.direct_input); + } +#endif bRewind = FALSE; if (target) stop_target(); diff --git a/src/xrSound/SoundRender_Scene.cpp b/src/xrSound/SoundRender_Scene.cpp index bc49844d3b8..88d09c47228 100644 --- a/src/xrSound/SoundRender_Scene.cpp +++ b/src/xrSound/SoundRender_Scene.cpp @@ -8,6 +8,47 @@ #include "SoundRender_Scene.h" #include "SoundRender_Emitter.h" +CSoundRender_Scene::CSoundRender_Scene() +{ +#ifdef USE_PHONON + if (const auto context = SoundRender->ipl_context()) + { + IPLSceneSettings sceneSettings + { + IPL_SCENETYPE_DEFAULT, + nullptr, nullptr, nullptr, nullptr, + this, + nullptr, nullptr + }; + iplSceneCreate(context, &sceneSettings, &m_ipl_scene); + + const auto [samplingRate, frameSize] = SoundRender->ipl_settings(); + + IPLSimulationSettings simulationSettings + { + IPL_SIMULATIONFLAGS_DIRECT, + IPL_SCENETYPE_DEFAULT, + IPL_REFLECTIONEFFECTTYPE_CONVOLUTION, + 128, + 4096, + 32, + 2.0f, + 1, + 8, + 2, + 5, + 32, + samplingRate, frameSize, + nullptr, nullptr, nullptr, + }; + iplSimulatorCreate(context, &simulationSettings, &m_ipl_simulator); + + iplSimulatorSetScene(m_ipl_simulator, m_ipl_scene); + iplSimulatorCommit(m_ipl_simulator); + } +#endif +} + CSoundRender_Scene::~CSoundRender_Scene() { ZoneScoped; @@ -21,6 +62,13 @@ CSoundRender_Scene::~CSoundRender_Scene() for (auto& emit : s_emitters) xr_delete(emit); s_emitters.clear(); + +#ifdef USE_PHONON + if (m_ipl_simulator) + iplSimulatorRelease(&m_ipl_simulator); + if (m_ipl_scene) + iplSceneRelease(&m_ipl_scene); +#endif } void CSoundRender_Scene::stop_emitters() const @@ -45,6 +93,55 @@ void CSoundRender_Scene::set_handler(sound_event* E) { sound_event_handler = E; void CSoundRender_Scene::set_geometry_occ(CDB::MODEL* M, const Fbox& /*aabb*/) { geom_MODEL = M; + +#ifdef USE_PHONON + if (m_ipl_scene_mesh) + { + iplStaticMeshRemove(m_ipl_scene_mesh, m_ipl_scene); + iplStaticMeshRelease(&m_ipl_scene_mesh); + } + if (M && m_ipl_scene) + { + const auto tris = M->get_tris(); + const auto tris_count = M->get_tris_count(); + + const auto verts = M->get_verts(); + const auto verts_count = M->get_verts_count(); + + auto* temp_tris = xr_alloc(tris_count); + auto* temp_mat_idx = xr_alloc(tris_count); + + // XXX: replace xr_vector with small_buffer and buffer_vector. But upgrade buffer_vector to match C++17 std::vector first. + xr_vector materials; + materials.reserve(GMLib.CountMaterial()); + + for (const SGameMtl* material : GMLib.Materials()) + { + materials.emplace_back(reinterpret_cast(material->Acoustics)); + } + + for (int i = 0; i < tris_count; ++i) + { + temp_tris[i] = reinterpret_cast(tris[i].verts); + temp_mat_idx[i] = tris[i].material; + } + + IPLStaticMeshSettings staticMeshSettings + { + verts_count, tris_count, static_cast(materials.size()), + reinterpret_cast(verts), temp_tris, + temp_mat_idx, materials.data() + }; + + iplStaticMeshCreate(m_ipl_scene, &staticMeshSettings, &m_ipl_scene_mesh); + xr_free(temp_mat_idx); + xr_free(temp_tris); + + iplStaticMeshAdd(m_ipl_scene_mesh, m_ipl_scene); + } + if (m_ipl_scene) + iplSceneCommit(m_ipl_scene); +#endif } void CSoundRender_Scene::set_geometry_som(IReader* I) @@ -170,6 +267,13 @@ CSoundRender_Emitter* CSoundRender_Scene::i_play(ref_sound& S, u32 flags, float void CSoundRender_Scene::update() { ZoneScoped; +#ifdef USE_PHONON + if (m_ipl_simulator) + { + iplSimulatorCommit(m_ipl_simulator); + iplSimulatorRunDirect(m_ipl_simulator); + } +#endif s_events_prev_count = s_events.size(); @@ -268,9 +372,16 @@ float CSoundRender_Scene::get_occlusion(const Fvector& P, float R, Fvector* occ) occ[1].set(V[T.verts[1]]); occ[2].set(V[T.verts[2]]); - const SGameMtl* mtl = GMLib.GetMaterialByIdx(T.material); - const float occlusion = fis_zero(mtl->fSndOcclusionFactor) ? 0.1f : mtl->fSndOcclusionFactor; - occ_value = psSoundOcclusionScale * occlusion; +#ifdef USE_PHONON + if (m_ipl_scene_mesh) + occ_value = psSoundOcclusionScale; + else +#endif + { + const SGameMtl* mtl = GMLib.GetMaterialByIdx(T.material); + const float occlusion = fis_zero(mtl->fSndOcclusionFactor) ? 0.1f : mtl->fSndOcclusionFactor; + occ_value = psSoundOcclusionScale * occlusion; + } } } } diff --git a/src/xrSound/SoundRender_Scene.h b/src/xrSound/SoundRender_Scene.h index dad4f0c640d..958a0feab9d 100644 --- a/src/xrSound/SoundRender_Scene.h +++ b/src/xrSound/SoundRender_Scene.h @@ -5,6 +5,7 @@ class CSoundRender_Emitter; class CSoundRender_Scene final : public ISoundScene { public: + CSoundRender_Scene(); ~CSoundRender_Scene() override; void stop_emitters() const override; @@ -35,6 +36,14 @@ class CSoundRender_Scene final : public ISoundScene auto& get_emitters() { return s_emitters; } +public: +#ifdef USE_PHONON + [[nodiscard]] + auto ipl_scene() const { return m_ipl_scene; } + auto ipl_scene_mesh() const { return m_ipl_scene_mesh; } + auto ipl_simulator() const { return m_ipl_simulator; } +#endif + private: xr_vector s_emitters; @@ -50,4 +59,10 @@ class CSoundRender_Scene final : public ISoundScene CDB::MODEL* geom_MODEL{}; int m_iPauseCounter{ 1 }; + +#ifdef USE_PHONON + IPLScene m_ipl_scene{}; + IPLStaticMesh m_ipl_scene_mesh{}; + IPLSimulator m_ipl_simulator{}; +#endif }; diff --git a/src/xrSound/SoundRender_Source.cpp b/src/xrSound/SoundRender_Source.cpp index a02ebd024fa..a569a8b346f 100644 --- a/src/xrSound/SoundRender_Source.cpp +++ b/src/xrSound/SoundRender_Source.cpp @@ -195,6 +195,10 @@ bool CSoundRender_Source::LoadWave(pcstr pName) m_data_info.avgBytesPerSec = m_data_info.samplesPerSec * m_data_info.blockAlign; m_data_info.bytesPerBuffer = sdef_target_block * m_data_info.avgBytesPerSec / 1000; +#ifdef USE_PHONON + m_ipl_audio_settings = { ovi->rate, IPLint32(m_data_info.bytesPerBuffer / m_data_info.blockAlign) }; +#endif + const s64 pcm_total = ov_pcm_total(&ovf, -1); dwBytesTotal = u32(pcm_total * m_data_info.blockAlign); fTimeTotal = dwBytesTotal / float(m_data_info.avgBytesPerSec); diff --git a/src/xrSound/SoundRender_Source.h b/src/xrSound/SoundRender_Source.h index 484c3ce014f..339da1122a1 100644 --- a/src/xrSound/SoundRender_Source.h +++ b/src/xrSound/SoundRender_Source.h @@ -40,6 +40,10 @@ class XRSOUND_API CSoundRender_Source final : public CSound_source SoundDataInfo m_data_info{}; SoundSourceInfo m_info{}; +#ifdef USE_PHONON + IPLAudioSettings m_ipl_audio_settings{}; +#endif + private: void i_decompress(OggVorbis_File* ovf, char* dest, u32 size) const; void i_decompress(OggVorbis_File* ovf, float* dest, u32 size) const; @@ -61,6 +65,8 @@ class XRSOUND_API CSoundRender_Source final : public CSound_source [[nodiscard]] const auto& data_info() const { return m_data_info; } [[nodiscard]] const auto& info() const { return m_info; } + [[nodiscard]] auto& ipl_audio_settings() { return m_ipl_audio_settings; } + [[nodiscard]] pcstr file_name() const override { return fname.c_str(); } [[nodiscard]] float length_sec() const override { return fTimeTotal; }